Backend • Introduced `validateExpiredTime` helper in `controller/redemption.go`; reused in both Add & Update endpoints to enforce “expiry time must not be earlier than now”, eliminating duplicated checks • Removed `RedemptionCodeStatusExpired` constant and all related references – expiry is now determined exclusively by the `expired_time` field for simpler, safer state management • Simplified `DeleteInvalidRedemptions`: deletes codes that are `used` / `disabled` or `enabled` but already expired, without relying on extra status codes • Controller no longer mutates `status` when listing or fetching redemption codes; clients derive expiry status from timestamp Frontend • Added reusable `isExpired` helper in `RedemptionsTable.js`; leveraged for: – status rendering (orange “Expired” tag) – action-menu enable/disable logic – row styling • Removed duplicated inline expiry logic, improving readability and performance • Adjusted toolbar layout: on small screens the “Clear invalid codes” button now wraps onto its own line, while “Add” & “Copy” remain grouped Result The codebase is now more maintainable, secure, and performant with no redundant constants, centralized validation, and cleaner UI behaviour across devices.
252 lines
5.2 KiB
Go
252 lines
5.2 KiB
Go
package controller
|
|
|
|
import (
|
|
"net/http"
|
|
"one-api/common"
|
|
"one-api/model"
|
|
"strconv"
|
|
"errors"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func GetAllRedemptions(c *gin.Context) {
|
|
p, _ := strconv.Atoi(c.Query("p"))
|
|
pageSize, _ := strconv.Atoi(c.Query("page_size"))
|
|
if p < 0 {
|
|
p = 0
|
|
}
|
|
if pageSize < 1 {
|
|
pageSize = common.ItemsPerPage
|
|
}
|
|
redemptions, total, err := model.GetAllRedemptions((p-1)*pageSize, pageSize)
|
|
if err != nil {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": false,
|
|
"message": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"message": "",
|
|
"data": gin.H{
|
|
"items": redemptions,
|
|
"total": total,
|
|
"page": p,
|
|
"page_size": pageSize,
|
|
},
|
|
})
|
|
return
|
|
}
|
|
|
|
func SearchRedemptions(c *gin.Context) {
|
|
keyword := c.Query("keyword")
|
|
p, _ := strconv.Atoi(c.Query("p"))
|
|
pageSize, _ := strconv.Atoi(c.Query("page_size"))
|
|
if p < 0 {
|
|
p = 0
|
|
}
|
|
if pageSize < 1 {
|
|
pageSize = common.ItemsPerPage
|
|
}
|
|
redemptions, total, err := model.SearchRedemptions(keyword, (p-1)*pageSize, pageSize)
|
|
if err != nil {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": false,
|
|
"message": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"message": "",
|
|
"data": gin.H{
|
|
"items": redemptions,
|
|
"total": total,
|
|
"page": p,
|
|
"page_size": pageSize,
|
|
},
|
|
})
|
|
return
|
|
}
|
|
|
|
func GetRedemption(c *gin.Context) {
|
|
id, err := strconv.Atoi(c.Param("id"))
|
|
if err != nil {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": false,
|
|
"message": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
redemption, err := model.GetRedemptionById(id)
|
|
if err != nil {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": false,
|
|
"message": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"message": "",
|
|
"data": redemption,
|
|
})
|
|
return
|
|
}
|
|
|
|
func AddRedemption(c *gin.Context) {
|
|
redemption := model.Redemption{}
|
|
err := c.ShouldBindJSON(&redemption)
|
|
if err != nil {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": false,
|
|
"message": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
if len(redemption.Name) == 0 || len(redemption.Name) > 20 {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": false,
|
|
"message": "兑换码名称长度必须在1-20之间",
|
|
})
|
|
return
|
|
}
|
|
if redemption.Count <= 0 {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": false,
|
|
"message": "兑换码个数必须大于0",
|
|
})
|
|
return
|
|
}
|
|
if redemption.Count > 100 {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": false,
|
|
"message": "一次兑换码批量生成的个数不能大于 100",
|
|
})
|
|
return
|
|
}
|
|
if err := validateExpiredTime(redemption.ExpiredTime); err != nil {
|
|
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
|
return
|
|
}
|
|
var keys []string
|
|
for i := 0; i < redemption.Count; i++ {
|
|
key := common.GetUUID()
|
|
cleanRedemption := model.Redemption{
|
|
UserId: c.GetInt("id"),
|
|
Name: redemption.Name,
|
|
Key: key,
|
|
CreatedTime: common.GetTimestamp(),
|
|
Quota: redemption.Quota,
|
|
ExpiredTime: redemption.ExpiredTime,
|
|
}
|
|
err = cleanRedemption.Insert()
|
|
if err != nil {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": false,
|
|
"message": err.Error(),
|
|
"data": keys,
|
|
})
|
|
return
|
|
}
|
|
keys = append(keys, key)
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"message": "",
|
|
"data": keys,
|
|
})
|
|
return
|
|
}
|
|
|
|
func DeleteRedemption(c *gin.Context) {
|
|
id, _ := strconv.Atoi(c.Param("id"))
|
|
err := model.DeleteRedemptionById(id)
|
|
if err != nil {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": false,
|
|
"message": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"message": "",
|
|
})
|
|
return
|
|
}
|
|
|
|
func UpdateRedemption(c *gin.Context) {
|
|
statusOnly := c.Query("status_only")
|
|
redemption := model.Redemption{}
|
|
err := c.ShouldBindJSON(&redemption)
|
|
if err != nil {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": false,
|
|
"message": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
cleanRedemption, err := model.GetRedemptionById(redemption.Id)
|
|
if err != nil {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": false,
|
|
"message": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
if statusOnly == "" {
|
|
if err := validateExpiredTime(redemption.ExpiredTime); err != nil {
|
|
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
|
return
|
|
}
|
|
// If you add more fields, please also update redemption.Update()
|
|
cleanRedemption.Name = redemption.Name
|
|
cleanRedemption.Quota = redemption.Quota
|
|
cleanRedemption.ExpiredTime = redemption.ExpiredTime
|
|
}
|
|
if statusOnly != "" {
|
|
cleanRedemption.Status = redemption.Status
|
|
}
|
|
err = cleanRedemption.Update()
|
|
if err != nil {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": false,
|
|
"message": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"message": "",
|
|
"data": cleanRedemption,
|
|
})
|
|
return
|
|
}
|
|
|
|
func DeleteInvalidRedemption(c *gin.Context) {
|
|
rows, err := model.DeleteInvalidRedemptions()
|
|
if err != nil {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": false,
|
|
"message": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"message": "",
|
|
"data": rows,
|
|
})
|
|
return
|
|
}
|
|
|
|
func validateExpiredTime(expired int64) error {
|
|
if expired != 0 && expired < common.GetTimestamp() {
|
|
return errors.New("过期时间不能早于当前时间")
|
|
}
|
|
return nil
|
|
}
|