bindbox-game/internal/api/admin/item_cards_admin.go
邹方成 45815bfb7d chore: 清理无用文件与优化代码结构
refactor(utils): 修复密码哈希比较逻辑错误
feat(user): 新增按状态筛选优惠券接口
docs: 添加虚拟发货与任务中心相关文档
fix(wechat): 修正Code2Session上下文传递问题
test: 补充订单折扣与积分转换测试用例
build: 更新配置文件与构建脚本
style: 清理多余的空行与注释
2025-12-18 17:35:55 +08:00

418 lines
15 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 simpleMessageResponse struct {
Message string `json:"message"`
}
type createItemCardRequest struct {
Name string `json:"name" binding:"required"`
Status int32 `json:"status"`
CardType int32 `json:"card_type" binding:"required"`
ScopeType int32 `json:"scope_type" binding:"required"`
ActivityCategoryID int64 `json:"activity_category_id"`
ActivityID int64 `json:"activity_id"`
IssueID int64 `json:"issue_id"`
Price int64 `json:"price" binding:"required"`
ValidStartUnix *int64 `json:"valid_start_unix"`
ValidEndUnix *int64 `json:"valid_end_unix"`
EffectType int32 `json:"effect_type" binding:"required"`
RewardMultiplierX1000 int32 `json:"reward_multiplier_x1000"`
BoostRateX1000 int32 `json:"boost_rate_x1000"`
StackingStrategy int32 `json:"stacking_strategy"`
MaxEffectValueX1000 int32 `json:"max_effect_value_x1000"`
Remark string `json:"remark"`
}
type createItemCardResponse struct {
ID int64 `json:"id"`
Message string `json:"message"`
}
// CreateSystemItemCard 创建道具卡
// @Summary 创建道具卡
// @Description 管理员创建新的道具卡,支持设置类型、效果、有效期等属性
// @Tags 管理端.运营管理
// @Accept json
// @Produce json
// @Param RequestBody body createItemCardRequest true "创建道具卡请求参数"
// @Success 200 {object} createItemCardResponse
// @Failure 400 {object} code.Failure "参数错误"
// @Failure 401 {object} code.Failure "未授权"
// @Failure 403 {object} code.Failure "无权限,仅超管可操作"
// @Failure 500 {object} code.Failure "服务器内部错误"
// @Router /api/admin/system_item_cards [post]
func (h *handler) CreateSystemItemCard() core.HandlerFunc {
return func(ctx core.Context) {
req := new(createItemCardRequest)
res := new(createItemCardResponse)
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
}
item := &model.SystemItemCards{
Name: req.Name,
Status: func() int32 {
if req.Status == 0 {
return 1
}
return req.Status
}(),
CardType: req.CardType,
ScopeType: req.ScopeType,
ActivityCategoryID: req.ActivityCategoryID,
ActivityID: req.ActivityID,
IssueID: req.IssueID,
Price: req.Price,
EffectType: req.EffectType,
RewardMultiplierX1000: req.RewardMultiplierX1000,
BoostRateX1000: req.BoostRateX1000,
StackingStrategy: func() int32 {
if req.StackingStrategy == 0 {
return 1
}
return req.StackingStrategy
}(),
MaxEffectValueX1000: req.MaxEffectValueX1000,
Remark: req.Remark,
}
do := h.writeDB.SystemItemCards.WithContext(ctx.RequestContext())
if req.ValidStartUnix != nil {
item.ValidStart = time.Unix(*req.ValidStartUnix, 0)
}
if req.ValidEndUnix != nil {
item.ValidEnd = time.Unix(*req.ValidEndUnix, 0)
} else {
do = do.Omit(h.writeDB.SystemItemCards.ValidEnd)
}
if err := do.Create(item); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error()))
return
}
res.ID = item.ID
res.Message = "操作成功"
ctx.Payload(res)
}
}
type modifyItemCardRequest struct {
Name *string `json:"name"`
Status *int32 `json:"status"`
CardType *int32 `json:"card_type"`
ScopeType *int32 `json:"scope_type"`
ActivityCategoryID *int64 `json:"activity_category_id"`
ActivityID *int64 `json:"activity_id"`
IssueID *int64 `json:"issue_id"`
Price *int64 `json:"price"`
ValidStartUnix *int64 `json:"valid_start_unix"`
ValidEndUnix *int64 `json:"valid_end_unix"`
EffectType *int32 `json:"effect_type"`
RewardMultiplierX1000 *int32 `json:"reward_multiplier_x1000"`
BoostRateX1000 *int32 `json:"boost_rate_x1000"`
StackingStrategy *int32 `json:"stacking_strategy"`
MaxEffectValueX1000 *int32 `json:"max_effect_value_x1000"`
Remark *string `json:"remark"`
}
// ModifySystemItemCard 修改道具卡
// @Summary 修改道具卡
// @Description 管理员修改道具卡信息,支持修改名称、价格、有效期等属性
// @Tags 管理端.运营管理
// @Accept json
// @Produce json
// @Param item_card_id path integer true "道具卡ID"
// @Param RequestBody body modifyItemCardRequest true "修改道具卡请求参数"
// @Success 200 {object} simpleMessageResponse
// @Failure 400 {object} code.Failure "参数错误"
// @Failure 401 {object} code.Failure "未授权"
// @Failure 403 {object} code.Failure "无权限,仅超管可操作"
// @Failure 500 {object} code.Failure "服务器内部错误"
// @Router /api/admin/system_item_cards/{item_card_id} [put]
func (h *handler) ModifySystemItemCard() core.HandlerFunc {
return func(ctx core.Context) {
req := new(modifyItemCardRequest)
if err := ctx.ShouldBindJSON(req); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
return
}
idStr := ctx.Param("item_card_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.CardType != nil {
set["card_type"] = *req.CardType
}
if req.ScopeType != nil {
set["scope_type"] = *req.ScopeType
}
if req.ActivityCategoryID != nil {
set["activity_category_id"] = *req.ActivityCategoryID
}
if req.ActivityID != nil {
set["activity_id"] = *req.ActivityID
}
if req.IssueID != nil {
set["issue_id"] = *req.IssueID
}
if req.Price != nil {
set["price"] = *req.Price
}
if req.EffectType != nil {
set["effect_type"] = *req.EffectType
}
if req.RewardMultiplierX1000 != nil {
set["reward_multiplier_x1000"] = *req.RewardMultiplierX1000
}
if req.BoostRateX1000 != nil {
set["boost_rate_x1000"] = *req.BoostRateX1000
}
if req.StackingStrategy != nil {
set["stacking_strategy"] = *req.StackingStrategy
}
if req.MaxEffectValueX1000 != nil {
set["max_effect_value_x1000"] = *req.MaxEffectValueX1000
}
if req.Remark != nil {
set["remark"] = *req.Remark
}
if req.ValidStartUnix != nil {
set["valid_start"] = time.Unix(*req.ValidStartUnix, 0)
}
if req.ValidEndUnix != nil {
set["valid_end"] = time.Unix(*req.ValidEndUnix, 0)
}
if len(set) == 0 {
ctx.Payload(simpleMessageResponse{Message: "操作成功"})
return
}
if _, err := h.writeDB.SystemItemCards.WithContext(ctx.RequestContext()).Where(h.writeDB.SystemItemCards.ID.Eq(id)).Updates(set); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error()))
return
}
ctx.Payload(simpleMessageResponse{Message: "操作成功"})
}
}
// DeleteSystemItemCard 删除道具卡
// @Summary 删除道具卡
// @Description 管理员删除指定的道具卡
// @Tags 管理端.运营管理
// @Accept json
// @Produce json
// @Param item_card_id path integer true "道具卡ID"
// @Success 200 {object} simpleMessageResponse
// @Failure 400 {object} code.Failure "参数错误"
// @Failure 401 {object} code.Failure "未授权"
// @Failure 403 {object} code.Failure "无权限,仅超管可操作"
// @Failure 500 {object} code.Failure "服务器内部错误"
// @Router /api/admin/system_item_cards/{item_card_id} [delete]
func (h *handler) DeleteSystemItemCard() core.HandlerFunc {
return func(ctx core.Context) {
idStr := ctx.Param("item_card_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.SystemItemCards.WithContext(ctx.RequestContext()).Where(h.writeDB.SystemItemCards.ID.Eq(id)).Updates(set); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error()))
return
}
ctx.Payload(simpleMessageResponse{Message: "操作成功"})
}
}
type listItemCardsRequest struct {
Name string `form:"name"`
Status int32 `form:"status"`
CardType int32 `form:"card_type"`
ScopeType int32 `form:"scope_type"`
Page int `form:"page"`
PageSize int `form:"page_size"`
}
type itemCardListItem struct {
ID int64 `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Name string `json:"name"`
Status int32 `json:"status"`
CardType int32 `json:"card_type"`
ScopeType int32 `json:"scope_type"`
ActivityCategoryID int64 `json:"activity_category_id"`
ActivityID int64 `json:"activity_id"`
IssueID int64 `json:"issue_id"`
Price int64 `json:"price"`
ValidStart time.Time `json:"valid_start"`
ValidEnd time.Time `json:"valid_end"`
EffectType int32 `json:"effect_type"`
RewardMultiplierX1000 int32 `json:"reward_multiplier_x1000"`
BoostRateX1000 int32 `json:"boost_rate_x1000"`
StackingStrategy int32 `json:"stacking_strategy"`
MaxEffectValueX1000 int32 `json:"max_effect_value_x1000"`
Remark string `json:"remark"`
}
type listItemCardsResponse struct {
Page int `json:"page"`
PageSize int `json:"page_size"`
Total int64 `json:"total"`
List []itemCardListItem `json:"list"`
}
// ListSystemItemCards 获取道具卡列表
// @Summary 获取道具卡列表
// @Description 管理员获取道具卡列表,支持按名称、状态、类型等条件筛选
// @Tags 管理端.运营管理
// @Accept json
// @Produce json
// @Param name query string false "道具卡名称"
// @Param status query integer false "状态1启用 2禁用"
// @Param card_type query integer false "道具卡类型1抽奖卡 2加成卡 3保底卡"
// @Param scope_type query integer false "适用范围1全局 2活动分类 3活动 4期次"
// @Param page query integer false "页码默认1"
// @Param page_size query integer false "每页条数默认10"
// @Success 200 {object} listItemCardsResponse
// @Failure 400 {object} code.Failure "参数错误"
// @Failure 401 {object} code.Failure "未授权"
// @Failure 500 {object} code.Failure "服务器内部错误"
// @Router /api/admin/system_item_cards [get]
func (h *handler) ListSystemItemCards() core.HandlerFunc {
return func(ctx core.Context) {
req := new(listItemCardsRequest)
res := new(listItemCardsResponse)
if err := ctx.ShouldBindForm(req); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
return
}
q := h.readDB.SystemItemCards.WithContext(ctx.RequestContext()).ReadDB()
if req.Name != "" {
q = q.Where(h.readDB.SystemItemCards.Name.Like("%" + req.Name + "%"))
}
if req.Status != 0 {
q = q.Where(h.readDB.SystemItemCards.Status.Eq(req.Status))
}
if req.CardType != 0 {
q = q.Where(h.readDB.SystemItemCards.CardType.Eq(req.CardType))
}
if req.ScopeType != 0 {
q = q.Where(h.readDB.SystemItemCards.ScopeType.Eq(req.ScopeType))
}
total, err := q.Count()
if err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ListActivitiesError, err.Error()))
return
}
if req.Page <= 0 {
req.Page = 1
}
if req.PageSize <= 0 {
req.PageSize = 20
}
if req.PageSize > 100 {
req.PageSize = 100
}
rows, err := q.Order(h.readDB.SystemItemCards.ID.Desc()).Offset((req.Page - 1) * req.PageSize).Limit(req.PageSize).Find()
if err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ListActivitiesError, err.Error()))
return
}
res.Page = req.Page
res.PageSize = req.PageSize
res.Total = total
res.List = make([]itemCardListItem, len(rows))
for i, r := range rows {
res.List[i] = itemCardListItem{
ID: r.ID,
CreatedAt: r.CreatedAt,
UpdatedAt: r.UpdatedAt,
Name: r.Name,
Status: r.Status,
CardType: r.CardType,
ScopeType: r.ScopeType,
ActivityCategoryID: r.ActivityCategoryID,
ActivityID: r.ActivityID,
IssueID: r.IssueID,
Price: r.Price,
ValidStart: r.ValidStart,
ValidEnd: r.ValidEnd,
EffectType: r.EffectType,
RewardMultiplierX1000: r.RewardMultiplierX1000,
BoostRateX1000: r.BoostRateX1000,
StackingStrategy: r.StackingStrategy,
MaxEffectValueX1000: r.MaxEffectValueX1000,
Remark: r.Remark,
}
}
ctx.Payload(res)
}
}
type assignItemCardRequest struct {
CardID int64 `json:"card_id" binding:"required"`
Quantity int `json:"quantity"`
}
// AssignUserItemCard 给用户分配道具卡
// @Summary 给用户分配道具卡
// @Description 管理员给指定用户分配道具卡,可指定数量
// @Tags 管理端.运营管理
// @Accept json
// @Produce json
// @Param user_id path integer true "用户ID"
// @Param RequestBody body assignItemCardRequest true "分配道具卡请求参数"
// @Success 200 {object} simpleMessageResponse
// @Failure 400 {object} code.Failure "参数错误"
// @Failure 401 {object} code.Failure "未授权"
// @Failure 403 {object} code.Failure "无权限,仅超管可操作"
// @Failure 500 {object} code.Failure "服务器内部错误"
// @Router /api/admin/users/{user_id}/item_cards [post]
func (h *handler) AssignUserItemCard() core.HandlerFunc {
return func(ctx core.Context) {
req := new(assignItemCardRequest)
if err := ctx.ShouldBindJSON(req); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
return
}
userID, err := strconv.ParseInt(ctx.Param("user_id"), 10, 64)
if err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递用户ID"))
return
}
if ctx.SessionUserInfo().IsSuper != 1 {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作"))
return
}
if err := h.user.AddItemCard(ctx.RequestContext(), userID, req.CardID, req.Quantity); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error()))
return
}
ctx.Payload(simpleMessageResponse{Message: "操作成功"})
}
}