Files
qitongxue-api/internal/logic/admin_quota.go
2026-09-17 17:38:45 +08:00

218 lines
6.5 KiB
Go
Raw Permalink 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 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).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) {
count, err := g.Model(consts.TableUsers).Where("id", userId).Count()
if err != nil {
return nil, err
}
if count == 0 {
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
}
return &v1.UserQuotaPeriodRes{}, nil
}