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" ) // ==================== 套餐管理 API ==================== type createGamePassPackageRequest struct { Name string `json:"name" binding:"required"` PassCount int32 `json:"pass_count" binding:"required,min=1"` Price int64 `json:"price" binding:"required,min=0"` OriginalPrice *int64 `json:"original_price"` ActivityID *int64 `json:"activity_id"` ValidDays *int32 `json:"valid_days"` SortOrder *int32 `json:"sort_order"` Status *int32 `json:"status"` // 1=上架 2=下架 } type createGamePassPackageResponse struct { ID int64 `json:"id"` Message string `json:"message"` } // CreateGamePassPackage 创建次数卡套餐 // @Summary 创建次数卡套餐 // @Description 创建可购买的次数卡套餐 // @Tags 管理端.次数卡套餐 // @Accept json // @Produce json // @Param RequestBody body createGamePassPackageRequest true "请求参数" // @Success 200 {object} createGamePassPackageResponse // @Failure 400 {object} code.Failure // @Router /api/admin/game-pass-packages [post] // @Security LoginVerifyToken func (h *handler) CreateGamePassPackage() core.HandlerFunc { return func(ctx core.Context) { req := new(createGamePassPackageRequest) res := new(createGamePassPackageResponse) if err := ctx.ShouldBindJSON(req); err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) return } now := time.Now() m := &model.GamePassPackages{ Name: req.Name, PassCount: req.PassCount, Price: req.Price, Status: 1, // 默认上架 CreatedAt: now, UpdatedAt: now, } if req.OriginalPrice != nil { m.OriginalPrice = *req.OriginalPrice } if req.ActivityID != nil && *req.ActivityID > 0 { m.ActivityID = *req.ActivityID } if req.ValidDays != nil { m.ValidDays = *req.ValidDays } if req.SortOrder != nil { m.SortOrder = *req.SortOrder } if req.Status != nil { m.Status = *req.Status } if err := h.writeDB.GamePassPackages.WithContext(ctx.RequestContext()).Create(m); err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, err.Error())) return } res.ID = m.ID res.Message = "创建成功" ctx.Payload(res) } } type modifyGamePassPackageRequest struct { Name *string `json:"name"` PassCount *int32 `json:"pass_count"` Price *int64 `json:"price"` OriginalPrice *int64 `json:"original_price"` ActivityID *int64 `json:"activity_id"` ValidDays *int32 `json:"valid_days"` SortOrder *int32 `json:"sort_order"` Status *int32 `json:"status"` } // ModifyGamePassPackage 修改次数卡套餐 // @Summary 修改次数卡套餐 // @Description 修改次数卡套餐配置 // @Tags 管理端.次数卡套餐 // @Accept json // @Produce json // @Param package_id path integer true "套餐ID" // @Param RequestBody body modifyGamePassPackageRequest true "请求参数" // @Success 200 {object} simpleMessageResponse // @Failure 400 {object} code.Failure // @Router /api/admin/game-pass-packages/{package_id} [put] // @Security LoginVerifyToken func (h *handler) ModifyGamePassPackage() core.HandlerFunc { return func(ctx core.Context) { req := new(modifyGamePassPackageRequest) if err := ctx.ShouldBindJSON(req); err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) return } idStr := ctx.Param("package_id") id, _ := strconv.ParseInt(idStr, 10, 64) if id <= 0 { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "套餐ID无效")) return } set := map[string]any{} if req.Name != nil { set["name"] = *req.Name } if req.PassCount != nil { set["pass_count"] = *req.PassCount } if req.Price != nil { set["price"] = *req.Price } if req.OriginalPrice != nil { set["original_price"] = *req.OriginalPrice } if req.ActivityID != nil { set["activity_id"] = *req.ActivityID } if req.ValidDays != nil { set["valid_days"] = *req.ValidDays } if req.SortOrder != nil { set["sort_order"] = *req.SortOrder } if req.Status != nil { set["status"] = *req.Status } if len(set) == 0 { ctx.Payload(simpleMessageResponse{Message: "无修改"}) return } if _, err := h.writeDB.GamePassPackages.WithContext(ctx.RequestContext()). Where(h.writeDB.GamePassPackages.ID.Eq(id)).Updates(set); err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, err.Error())) return } ctx.Payload(simpleMessageResponse{Message: "修改成功"}) } } // DeleteGamePassPackage 删除次数卡套餐 // @Summary 删除次数卡套餐 // @Description 软删除次数卡套餐 // @Tags 管理端.次数卡套餐 // @Accept json // @Produce json // @Param package_id path integer true "套餐ID" // @Success 200 {object} simpleMessageResponse // @Failure 400 {object} code.Failure // @Router /api/admin/game-pass-packages/{package_id} [delete] // @Security LoginVerifyToken func (h *handler) DeleteGamePassPackage() core.HandlerFunc { return func(ctx core.Context) { idStr := ctx.Param("package_id") id, _ := strconv.ParseInt(idStr, 10, 64) if id <= 0 { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "套餐ID无效")) return } if _, err := h.writeDB.GamePassPackages.WithContext(ctx.RequestContext()). Where(h.writeDB.GamePassPackages.ID.Eq(id)).Delete(); err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, err.Error())) return } ctx.Payload(simpleMessageResponse{Message: "删除成功"}) } } // ==================== 套餐列表 ==================== type listGamePassPackagesRequest struct { ActivityID *int64 `form:"activity_id"` Status *int32 `form:"status"` Page int `form:"page"` PageSize int `form:"page_size"` } type gamePassPackageItem struct { ID int64 `json:"id"` Name string `json:"name"` PassCount int32 `json:"pass_count"` Price int64 `json:"price"` OriginalPrice int64 `json:"original_price"` ActivityID int64 `json:"activity_id"` ActivityName string `json:"activity_name"` ValidDays int32 `json:"valid_days"` SortOrder int32 `json:"sort_order"` Status int32 `json:"status"` CreatedAt string `json:"created_at"` } type listGamePassPackagesResponse struct { Page int `json:"page"` PageSize int `json:"page_size"` Total int64 `json:"total"` List []gamePassPackageItem `json:"list"` } // ListGamePassPackages 次数卡套餐列表 // @Summary 次数卡套餐列表 // @Description 查询次数卡套餐列表 // @Tags 管理端.次数卡套餐 // @Accept json // @Produce json // @Param activity_id query integer false "活动ID" // @Param status query integer false "状态: 1=上架 2=下架" // @Param page query integer false "页码" // @Param page_size query integer false "每页条数" // @Success 200 {object} listGamePassPackagesResponse // @Failure 400 {object} code.Failure // @Router /api/admin/game-pass-packages [get] // @Security LoginVerifyToken func (h *handler) ListGamePassPackages() core.HandlerFunc { return func(ctx core.Context) { req := new(listGamePassPackagesRequest) res := new(listGamePassPackagesResponse) 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.GamePassPackages.WithContext(ctx.RequestContext()).ReadDB() if req.ActivityID != nil && *req.ActivityID > 0 { q = q.Where(h.readDB.GamePassPackages.ActivityID.Eq(*req.ActivityID)) } if req.Status != nil { q = q.Where(h.readDB.GamePassPackages.Status.Eq(*req.Status)) } total, err := q.Count() if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, err.Error())) return } packages, err := q.Order(h.readDB.GamePassPackages.SortOrder.Desc(), h.readDB.GamePassPackages.ID.Desc()). Limit(req.PageSize). Offset((req.Page - 1) * req.PageSize). Find() if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, err.Error())) return } // 获取活动名称 activityIDs := make([]int64, 0) for _, p := range packages { if p.ActivityID > 0 { activityIDs = append(activityIDs, p.ActivityID) } } activityMap := make(map[int64]string) if len(activityIDs) > 0 { activities, _ := h.readDB.Activities.WithContext(ctx.RequestContext()).Where(h.readDB.Activities.ID.In(activityIDs...)).Find() for _, a := range activities { activityMap[a.ID] = a.Name } } res.Page = req.Page res.PageSize = req.PageSize res.Total = total res.List = make([]gamePassPackageItem, len(packages)) for i, p := range packages { res.List[i] = gamePassPackageItem{ ID: p.ID, Name: p.Name, PassCount: p.PassCount, Price: p.Price, OriginalPrice: p.OriginalPrice, ActivityID: p.ActivityID, ActivityName: activityMap[p.ActivityID], ValidDays: p.ValidDays, SortOrder: p.SortOrder, Status: p.Status, CreatedAt: p.CreatedAt.Format("2006-01-02 15:04:05"), } } ctx.Payload(res) } }