bindbox-game/internal/api/admin/system_coupons.go
邹方成 2a89a1ab9d
Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 39s
feat(admin): 更新前端资源文件及修复相关功能
refactor(service): 修改banner和guild删除逻辑为软删除
fix(service): 修复删除操作使用软删除而非物理删除

build: 添加SQLite测试仓库实现
docs: 新增奖励管理字段拆分和批量抽奖UI改造文档

ci: 更新CI忽略文件
style: 清理无用资源文件
2025-11-19 01:35:55 +08:00

268 lines
8.4 KiB
Go

package admin
import (
"net/http"
"strconv"
"time"
"bindbox-game/internal/code"
"bindbox-game/internal/pkg/core"
"bindbox-game/internal/pkg/validation"
"bindbox-game/internal/repository/mysql/model"
)
type createSystemCouponRequest struct {
Name string `json:"name" binding:"required"`
Status *int32 `json:"status"`
CouponType *int32 `json:"coupon_type"`
DiscountType *int32 `json:"discount_type"`
DiscountValue *int64 `json:"discount_value"`
MinAmount *int64 `json:"min_amount"`
ValidDays *int `json:"valid_days"`
TotalQuantity *int64 `json:"total_quantity"`
}
type createSystemCouponResponse struct {
ID int64 `json:"id"`
Message string `json:"message"`
}
func (h *handler) CreateSystemCoupon() core.HandlerFunc {
return func(ctx core.Context) {
req := new(createSystemCouponRequest)
res := new(createSystemCouponResponse)
if err := ctx.ShouldBindJSON(req); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
return
}
if ctx.SessionUserInfo().IsSuper != 1 {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作"))
return
}
now := time.Now()
m := &model.SystemCoupons{
Name: req.Name,
ScopeType: getInt32OrDefault(req.CouponType, 1),
DiscountType: getInt32OrDefault(req.DiscountType, 1),
DiscountValue: getInt64OrDefault(req.DiscountValue, 0),
MinSpend: getInt64OrDefault(req.MinAmount, 0),
Status: getInt32OrDefault(req.Status, 1),
}
if req.ValidDays != nil && *req.ValidDays > 0 {
m.ValidStart = now
m.ValidEnd = now.Add(time.Duration(*req.ValidDays) * 24 * time.Hour)
}
if err := h.writeDB.SystemCoupons.WithContext(ctx.RequestContext()).Create(m); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error()))
return
}
if req.TotalQuantity != nil {
if _, err := h.writeDB.SystemCoupons.WithContext(ctx.RequestContext()).Where(h.writeDB.SystemCoupons.ID.Eq(m.ID)).Updates(map[string]any{"total_quantity": *req.TotalQuantity}); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error()))
return
}
}
res.ID = m.ID
res.Message = "操作成功"
ctx.Payload(res)
}
}
type modifySystemCouponRequest struct {
Name *string `json:"name"`
Status *int32 `json:"status"`
CouponType *int32 `json:"coupon_type"`
DiscountType *int32 `json:"discount_type"`
DiscountValue *int64 `json:"discount_value"`
MinAmount *int64 `json:"min_amount"`
ValidDays *int `json:"valid_days"`
TotalQuantity *int64 `json:"total_quantity"`
}
func (h *handler) ModifySystemCoupon() core.HandlerFunc {
return func(ctx core.Context) {
req := new(modifySystemCouponRequest)
if err := ctx.ShouldBindJSON(req); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
return
}
idStr := ctx.Param("coupon_id")
id, _ := strconv.ParseInt(idStr, 10, 64)
if ctx.SessionUserInfo().IsSuper != 1 {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作"))
return
}
set := map[string]any{}
if req.Name != nil {
set["name"] = *req.Name
}
if req.Status != nil {
set["status"] = *req.Status
}
if req.CouponType != nil {
set["scope_type"] = *req.CouponType
}
if req.DiscountType != nil {
set["discount_type"] = *req.DiscountType
}
if req.DiscountValue != nil {
set["discount_value"] = *req.DiscountValue
}
if req.MinAmount != nil {
set["min_spend"] = *req.MinAmount
}
if req.ValidDays != nil && *req.ValidDays > 0 {
now := time.Now()
set["valid_start"] = now
set["valid_end"] = now.Add(time.Duration(*req.ValidDays) * 24 * time.Hour)
}
if req.TotalQuantity != nil {
set["total_quantity"] = *req.TotalQuantity
}
if len(set) == 0 {
ctx.Payload(pcSimpleMessage{Message: "操作成功"})
return
}
if _, err := h.writeDB.SystemCoupons.WithContext(ctx.RequestContext()).Where(h.writeDB.SystemCoupons.ID.Eq(id)).Updates(set); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error()))
return
}
ctx.Payload(pcSimpleMessage{Message: "操作成功"})
}
}
func (h *handler) DeleteSystemCoupon() core.HandlerFunc {
return func(ctx core.Context) {
idStr := ctx.Param("coupon_id")
id, _ := strconv.ParseInt(idStr, 10, 64)
if ctx.SessionUserInfo().IsSuper != 1 {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作"))
return
}
uid := int64(ctx.SessionUserInfo().Id)
set := map[string]any{"deleted_at": time.Now(), "deleted_by": uid}
if _, err := h.writeDB.SystemCoupons.WithContext(ctx.RequestContext()).Where(h.writeDB.SystemCoupons.ID.Eq(id)).Updates(set); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error()))
return
}
ctx.Payload(pcSimpleMessage{Message: "操作成功"})
}
}
type listSystemCouponsRequest struct {
Name string `form:"name"`
Status *int32 `form:"status"`
Page int `form:"page"`
PageSize int `form:"page_size"`
}
type systemCouponItem struct {
ID int64 `json:"id"`
Name string `json:"name"`
Status int32 `json:"status"`
CouponType int32 `json:"coupon_type"`
DiscountType int32 `json:"discount_type"`
DiscountValue int64 `json:"discount_value"`
MinAmount int64 `json:"min_amount"`
MaxDiscount int64 `json:"max_discount"`
ValidDays int `json:"valid_days"`
TotalQuantity int64 `json:"total_quantity"`
UsedQuantity int64 `json:"used_quantity"`
CreatedAt string `json:"created_at"`
}
type listSystemCouponsResponse struct {
Page int `json:"page"`
PageSize int `json:"page_size"`
Total int64 `json:"total"`
List []systemCouponItem `json:"list"`
}
func (h *handler) ListSystemCoupons() core.HandlerFunc {
return func(ctx core.Context) {
req := new(listSystemCouponsRequest)
res := new(listSystemCouponsResponse)
if err := ctx.ShouldBindForm(req); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
return
}
if req.Page <= 0 {
req.Page = 1
}
if req.PageSize <= 0 {
req.PageSize = 20
}
q := h.readDB.SystemCoupons.WithContext(ctx.RequestContext()).ReadDB()
if req.Name != "" {
q = q.Where(h.readDB.SystemCoupons.Name.Like("%" + req.Name + "%"))
}
if req.Status != nil {
q = q.Where(h.readDB.SystemCoupons.Status.Eq(*req.Status))
}
total, err := q.Count()
if err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, err.Error()))
return
}
type dbRow struct {
ID int64
Name string
Status int32
ScopeType int32
DiscountType int32
DiscountValue int64
MinSpend int64
ValidStart time.Time
ValidEnd time.Time
CreatedAt time.Time
TotalQuantity int64
}
var rows []dbRow
if err := q.Order(h.readDB.SystemCoupons.ID.Desc()).Limit(req.PageSize).Offset((req.Page - 1) * req.PageSize).Scan(&rows); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, err.Error()))
return
}
res.Page = req.Page
res.PageSize = req.PageSize
res.Total = total
res.List = make([]systemCouponItem, len(rows))
for i, v := range rows {
validDays := 0
if !v.ValidStart.IsZero() && !v.ValidEnd.IsZero() {
diff := v.ValidEnd.Sub(v.ValidStart)
validDays = int(diff.Hours() / 24)
}
// 统计已发放数量(用户持券数)
usedCnt, _ := h.readDB.UserCoupons.WithContext(ctx.RequestContext()).Where(h.readDB.UserCoupons.CouponID.Eq(v.ID)).Count()
res.List[i] = systemCouponItem{
ID: v.ID,
Name: v.Name,
Status: v.Status,
CouponType: v.ScopeType,
DiscountType: v.DiscountType,
DiscountValue: v.DiscountValue,
MinAmount: v.MinSpend,
MaxDiscount: 0,
ValidDays: validDays,
TotalQuantity: v.TotalQuantity,
UsedQuantity: usedCnt,
CreatedAt: v.CreatedAt.Format("2006-01-02 15:04:05"),
}
}
ctx.Payload(res)
}
}
func getInt32OrDefault(v *int32, d int32) int32 {
if v != nil {
return *v
}
return d
}
func getInt64OrDefault(v *int64, d int64) int64 {
if v != nil {
return *v
}
return d
}