Skip to content

Commit 6f5dae5

Browse files
committed
feat: support redeem code operation (coaidev#90)
1 parent 296c1f8 commit 6f5dae5

File tree

18 files changed

+192
-98
lines changed

18 files changed

+192
-98
lines changed

admin/controller.go

+10-10
Original file line numberDiff line numberDiff line change
@@ -133,27 +133,27 @@ func UserTypeAnalysisAPI(c *gin.Context) {
133133

134134
func RedeemListAPI(c *gin.Context) {
135135
db := utils.GetDBFromContext(c)
136-
c.JSON(http.StatusOK, GetRedeemData(db))
137-
}
138136

139-
func RedeemSegmentAPI(c *gin.Context) {
140-
quota := utils.ParseFloat32(c.Query("quota"))
141-
onlyUnused := utils.ParseBool(c.Query("unused"))
137+
page, _ := strconv.Atoi(c.Query("page"))
138+
c.JSON(http.StatusOK, GetRedeemData(db, int64(page)))
139+
}
142140

141+
func DeleteRedeemAPI(c *gin.Context) {
143142
db := utils.GetDBFromContext(c)
144143

145-
data, err := GetRedeemSegment(db, quota, onlyUnused)
146-
if err != nil {
144+
var form DeleteInvitationForm
145+
if err := c.ShouldBindJSON(&form); err != nil {
147146
c.JSON(http.StatusOK, gin.H{
148147
"status": false,
149148
"error": err.Error(),
150149
})
151150
return
152-
153151
}
152+
153+
err := DeleteRedeemCode(db, form.Code)
154154
c.JSON(http.StatusOK, gin.H{
155-
"status": true,
156-
"data": data,
155+
"status": err == nil,
156+
"error": err,
157157
})
158158
}
159159

admin/redeem.go

+41-37
Original file line numberDiff line numberDiff line change
@@ -5,60 +5,64 @@ import (
55
"chat/utils"
66
"database/sql"
77
"fmt"
8+
"math"
89
"strings"
910
)
1011

11-
func GetRedeemData(db *sql.DB) []RedeemData {
12-
var data []RedeemData
12+
func GetRedeemData(db *sql.DB, page int64) PaginationForm {
13+
var data []interface{}
14+
var total int64
15+
if err := globals.QueryRowDb(db, `
16+
SELECT COUNT(*) FROM redeem
17+
`).Scan(&total); err != nil {
18+
return PaginationForm{
19+
Status: false,
20+
Message: err.Error(),
21+
}
22+
}
1323

1424
rows, err := globals.QueryDb(db, `
15-
SELECT quota, COUNT(*) AS total, SUM(IF(used = 0, 0, 1)) AS used
25+
SELECT code, quota, used, created_at, updated_at
1626
FROM redeem
17-
GROUP BY quota
18-
`)
27+
ORDER BY id DESC LIMIT ? OFFSET ?
28+
`, pagination, page*pagination)
29+
1930
if err != nil {
20-
return data
31+
return PaginationForm{
32+
Status: false,
33+
Message: err.Error(),
34+
}
2135
}
2236

2337
for rows.Next() {
24-
var d RedeemData
25-
if err := rows.Scan(&d.Quota, &d.Total, &d.Used); err != nil {
26-
return data
38+
var redeem RedeemData
39+
var createdAt []uint8
40+
var updatedAt []uint8
41+
if err := rows.Scan(&redeem.Code, &redeem.Quota, &redeem.Used, &createdAt, &updatedAt); err != nil {
42+
return PaginationForm{
43+
Status: false,
44+
Message: err.Error(),
45+
}
2746
}
28-
data = append(data, d)
29-
}
30-
31-
return data
32-
}
33-
34-
func GetRedeemSegment(db *sql.DB, quota float32, onlyUnused bool) ([]string, error) {
35-
var codes []string
36-
var rows *sql.Rows
37-
var err error
3847

39-
if onlyUnused {
40-
rows, err = globals.QueryDb(db, `
41-
SELECT code FROM redeem WHERE quota = ? AND used = 0
42-
`, quota)
43-
} else {
44-
rows, err = globals.QueryDb(db, `
45-
SELECT code FROM redeem WHERE quota = ?
46-
`, quota)
48+
redeem.CreatedAt = utils.ConvertTime(createdAt).Format("2006-01-02 15:04:05")
49+
redeem.UpdatedAt = utils.ConvertTime(updatedAt).Format("2006-01-02 15:04:05")
50+
data = append(data, redeem)
4751
}
4852

49-
if err != nil {
50-
return codes, err
53+
return PaginationForm{
54+
Status: true,
55+
Total: int(math.Ceil(float64(total) / float64(pagination))),
56+
Data: data,
5157
}
58+
}
5259

53-
for rows.Next() {
54-
var code string
55-
if err := rows.Scan(&code); err != nil {
56-
return codes, err
57-
}
58-
codes = append(codes, code)
59-
}
60+
func DeleteRedeemCode(db *sql.DB, code string) error {
61+
_, err := globals.ExecDb(db, `
62+
DELETE FROM redeem WHERE code = ?
63+
`, code)
6064

61-
return codes, nil
65+
return err
6266
}
6367

6468
func GenerateRedeemCodes(db *sql.DB, num int, quota float32) RedeemGenerateResponse {

admin/router.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ func Register(app *gin.RouterGroup) {
2020
app.POST("/admin/invitation/delete", DeleteInvitationAPI)
2121

2222
app.GET("/admin/redeem/list", RedeemListAPI)
23-
app.GET("/admin/redeem/segment", RedeemSegmentAPI)
2423
app.POST("/admin/redeem/generate", GenerateRedeemAPI)
24+
app.POST("/admin/redeem/delete", DeleteRedeemAPI)
2525

2626
app.GET("/admin/user/list", UserPaginationAPI)
2727
app.POST("/admin/user/quota", UserQuotaAPI)

admin/types.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,11 @@ type InvitationData struct {
5151
}
5252

5353
type RedeemData struct {
54-
Quota float32 `json:"quota"`
55-
Used float32 `json:"used"`
56-
Total float32 `json:"total"`
54+
Code string `json:"code"`
55+
Quota float32 `json:"quota"`
56+
Used bool `json:"used"`
57+
CreatedAt string `json:"created_at"`
58+
UpdatedAt string `json:"updated_at"`
5759
}
5860

5961
type InvitationGenerateResponse struct {

app/src-tauri/tauri.conf.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
},
99
"package": {
1010
"productName": "chatnio",
11-
"version": "3.10.5"
11+
"version": "3.10.6"
1212
},
1313
"tauri": {
1414
"allowlist": {

app/src/admin/api/chart.ts

+7-10
Original file line numberDiff line numberDiff line change
@@ -128,25 +128,22 @@ export async function generateInvitation(
128128
}
129129
}
130130

131-
export async function getRedeemList(): Promise<RedeemResponse> {
131+
export async function getRedeemList(page: number): Promise<RedeemResponse> {
132132
try {
133-
const response = await axios.get("/admin/redeem/list");
133+
const response = await axios.get(`/admin/redeem/list?page=${page}`);
134134
return response.data as RedeemResponse;
135135
} catch (e) {
136136
console.warn(e);
137-
return [];
137+
return { status: false, message: getErrorMessage(e), data: [], total: 0 };
138138
}
139139
}
140140

141-
export async function getRedeemSegment(quota: number, only_unused: boolean) {
141+
export async function deleteRedeem(code: string): Promise<CommonResponse> {
142142
try {
143-
const response = await axios.get(
144-
`/admin/redeem/segment?quota=${quota}&unused=${only_unused}`,
145-
);
146-
return response.data as RedeemResponse;
143+
const response = await axios.post("/admin/redeem/delete", { code });
144+
return response.data as CommonResponse;
147145
} catch (e) {
148-
console.warn(e);
149-
return [];
146+
return { status: false, message: getErrorMessage(e) };
150147
}
151148
}
152149

app/src/admin/types.ts

+10-3
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,21 @@ export type InvitationResponse = {
6565
};
6666

6767
export type Redeem = {
68+
code: string;
6869
quota: number;
6970
used: boolean;
71+
created_at: string;
72+
updated_at: string;
73+
};
74+
75+
export type RedeemForm = {
76+
data: Redeem[];
7077
total: number;
7178
};
7279

73-
export type RedeemResponse = Redeem[];
74-
export type RedeemSegmentResponse = CommonResponse & {
75-
data: string[];
80+
export type RedeemResponse = CommonResponse & {
81+
data: Redeem[];
82+
total: number;
7683
};
7784

7885
export type InvitationGenerateResponse = {

app/src/assets/globals.less

+6
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@
3535
--destructive: 0 67.22% 50.59%;
3636
--destructive-foreground: 0 0% 98%;
3737

38+
--success: 120 100% 50%;
39+
--success-foreground: 0 0% 98%;
40+
41+
--failure: 0 67.22% 50.59%;
42+
--failure-foreground: 0 0% 98%;
43+
3844
--border: 240 5.9% 90%;
3945
--border-hover: 240 5.9% 85%;
4046
--border-active: 240 5.9% 80%;

app/src/components/admin/InvitationTable.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { PaginationAction } from "@/components/ui/pagination.tsx";
3434
import { Badge } from "@/components/ui/badge.tsx";
3535
import OperationAction from "@/components/OperationAction.tsx";
3636
import { toastState } from "@/api/common.ts";
37+
import StateBadge from "@/components/admin/common/StateBadge.tsx";
3738

3839
function GenerateDialog({ update }: { update: () => void }) {
3940
const { t } = useTranslation();
@@ -191,7 +192,9 @@ function InvitationTable() {
191192
<TableCell>
192193
<Badge>{invitation.type}</Badge>
193194
</TableCell>
194-
<TableCell>{t(`admin.used-${invitation.used}`)}</TableCell>
195+
<TableCell>
196+
<StateBadge state={invitation.used} />
197+
</TableCell>
195198
<TableCell>{invitation.username || "-"}</TableCell>
196199
<TableCell>{invitation.created_at}</TableCell>
197200
<TableCell>{invitation.updated_at}</TableCell>

0 commit comments

Comments
 (0)