package logic import ( "context" "strconv" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" v1 "tool-api/api/admin/v1" "tool-api/internal/consts" ) // ============================================================================ // 管理端:额度配置 / 次数包 / 会员套餐 / 额度设置 // ============================================================================ // ===== 次数包档位 ===== func AdminQuotaPackList(ctx context.Context) (*v1.QuotaPackListRes, error) { records, err := g.Model(consts.TableQuotaPacks).OrderAsc("sort").OrderAsc("times").All() if err != nil { return nil, err } toolNames, err := toolNameMap(ctx) if err != nil { return nil, err } list := make([]v1.QuotaPackItem, 0, len(records)) for _, r := range records { toolKey := r["tool_key"].String() toolName := "" if toolKey != "" { toolName = toolNames[toolKey] if toolName == "" { toolName = toolKey + "(工具不存在)" } } list = append(list, v1.QuotaPackItem{ Id: r["id"].Int64(), PackKey: r["pack_key"].String(), Name: r["name"].String(), Times: r["times"].Int(), PriceCents: r["price_cents"].Int64(), ProductId: r["product_id"].String(), ValidDays: r["valid_days"].Int(), ToolKey: toolKey, ToolName: toolName, Badge: r["badge"].String(), Sort: r["sort"].Int(), IsEnabled: r["is_enabled"].Int(), Remark: r["remark"].String(), }) } return &v1.QuotaPackListRes{List: list}, nil } func AdminQuotaPackSave(ctx context.Context, req *v1.QuotaPackSaveReq) (*v1.QuotaPackSaveRes, error) { if req.ToolKey != "" { count, err := g.Model(consts.TableTools).Where("tool_key", req.ToolKey).WhereNull("deleted_at").Count() if err != nil { return nil, err } if count == 0 { return nil, gerror.New("绑定的工具不存在") } } validDays := req.ValidDays if validDays <= 0 { validDays = 365 } data := g.Map{ "name": req.Name, "times": req.Times, "price_cents": req.PriceCents, "product_id": req.ProductId, "valid_days": validDays, "tool_key": req.ToolKey, "badge": req.Badge, "sort": req.Sort, "is_enabled": req.IsEnabled, "remark": req.Remark, "updated_at": gtime.Now(), } if req.Id > 0 { // 编辑:pack_key 是跨端契约(决定道具),不允许改 if _, err := g.Model(consts.TableQuotaPacks).Where("id", req.Id).Data(data).Update(); err != nil { return nil, err } return &v1.QuotaPackSaveRes{}, nil } count, err := g.Model(consts.TableQuotaPacks).Where("pack_key", req.PackKey).Count() if err != nil { return nil, err } if count > 0 { return nil, gerror.New("档位标识已存在") } data["pack_key"] = req.PackKey data["created_at"] = gtime.Now() if _, err = g.Model(consts.TableQuotaPacks).Data(data).Insert(); err != nil { return nil, err } return &v1.QuotaPackSaveRes{}, nil } func AdminQuotaPackToggle(ctx context.Context, id int64, value int) (*v1.QuotaPackToggleRes, error) { if _, err := g.Model(consts.TableQuotaPacks).Where("id", id). Data(g.Map{"is_enabled": value, "updated_at": gtime.Now()}).Update(); err != nil { return nil, err } return &v1.QuotaPackToggleRes{}, nil } // ===== 额度设置 ===== func AdminQuotaSettingGet(ctx context.Context) (*v1.QuotaSettingGetRes, error) { return &v1.QuotaSettingGetRes{ PeriodDays: GlobalQuotaPeriodDays(ctx), DefaultPeriodDays: consts.DefaultQuotaPeriodDays, }, nil } func AdminQuotaSettingSave(ctx context.Context, periodDays int) (*v1.QuotaSettingSaveRes, error) { if err := settingSave(ctx, consts.SettingQuotaPeriodDays, strconv.Itoa(periodDays), "全局默认额度重置周期(天)"); err != nil { return nil, err } return &v1.QuotaSettingSaveRes{}, nil } // ===== 会员套餐 ===== func AdminPlanList(ctx context.Context) (*v1.PlanListRes, error) { records, err := g.Model(consts.TableMemberPlans).OrderAsc("sort").All() if err != nil { return nil, err } levelNames, err := levelNameMap(ctx) if err != nil { return nil, err } list := make([]v1.PlanItem, 0, len(records)) for _, r := range records { levelKey := r["level_key"].String() list = append(list, v1.PlanItem{ Id: r["id"].Int64(), PlanKey: r["plan_key"].String(), Name: r["name"].String(), Subtitle: r["subtitle"].String(), LevelKey: levelKey, LevelName: levelNames[levelKey], DurationDays: r["duration_days"].Int(), PriceCents: r["price_cents"].Int64(), ProductId: r["product_id"].String(), Badge: r["badge"].String(), QuotaPeriodDays: r["quota_period_days"].Int(), Sort: r["sort"].Int(), IsEnabled: r["is_enabled"].Int(), Remark: r["remark"].String(), }) } return &v1.PlanListRes{List: list}, nil } // AdminPlanSave 只覆盖运营需要维护的列。 // // 刻意不放开 plan_key / level_key / duration_days: // - plan_key 是订单与前端展示的关联键 // - level_key 必须是 levels 表里真实存在的等级 // - duration_days 涉及续费顺延语义 // // 这三项仍以 seedMemberPlans 为准,避免后台改出前端不认识的组合。 func AdminPlanSave(ctx context.Context, req *v1.PlanSaveReq) (*v1.PlanSaveRes, error) { record, err := g.Model(consts.TableMemberPlans).Where("id", req.Id).One() if err != nil { return nil, err } if record.IsEmpty() { return nil, gerror.New("套餐不存在") } if _, err = g.Model(consts.TableMemberPlans).Where("id", req.Id).Data(g.Map{ "name": req.Name, "subtitle": req.Subtitle, "price_cents": req.PriceCents, "product_id": req.ProductId, "badge": req.Badge, "quota_period_days": req.QuotaPeriodDays, "sort": req.Sort, "is_enabled": req.IsEnabled, "remark": req.Remark, "updated_at": gtime.Now(), }).Update(); err != nil { return nil, err } return &v1.PlanSaveRes{}, nil } // ===== 用户额度周期 ===== func AdminUserQuotaPeriod(ctx context.Context, userId int64, periodDays int) (*v1.UserQuotaPeriodRes, error) { before, err := g.Model(consts.TableUsers).Where("id", userId).WhereNull("deleted_at").One() if err != nil { return nil, err } if before.IsEmpty() { return nil, gerror.New("用户不存在") } if _, err = g.Model(consts.TableUsers).Where("id", userId).Data(g.Map{ "quota_period_days": periodDays, "updated_at": gtime.Now(), }).Update(); err != nil { return nil, err } WriteAudit(ctx, AuditEntry{ Action: "user.set_quota_period", TargetType: "user", TargetId: auditId(userId), Before: g.Map{"quota_period_days": before["quota_period_days"].Int()}, After: g.Map{"quota_period_days": periodDays}, Result: consts.AuditResultSuccess, }) return &v1.UserQuotaPeriodRes{}, nil }