bindbox-game/internal/api/admin/system_coupons.go
邹方成 1ab39d2f5a
Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 25s
refactor: 重构项目结构并重命名模块
feat(admin): 新增工会管理功能
feat(activity): 添加活动管理相关服务
feat(user): 实现用户道具卡和积分管理
feat(guild): 新增工会成员管理功能

fix: 修复数据库连接配置
fix: 修正jwtoken导入路径
fix: 解决端口冲突问题

style: 统一代码格式和注释风格
style: 更新项目常量命名

docs: 添加项目框架和开发规范文档
docs: 更新接口文档注释

chore: 移除无用代码和文件
chore: 更新Makefile和配置文件
chore: 清理日志文件

test: 添加道具卡测试脚本
2025-11-14 21:10:00 +08:00

266 lines
8.2 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
}
if _, err := h.writeDB.SystemCoupons.WithContext(ctx.RequestContext()).Where(h.writeDB.SystemCoupons.ID.Eq(id)).Delete(); 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
}