bindbox-game/internal/api/admin/titles_seed.go
邹方成 8141a47690
Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 39s
feat(称号系统): 新增称号管理功能与抽奖效果集成
- 新增系统称号模板与效果配置表及相关CRUD接口
- 实现用户称号分配与抽奖效果应用逻辑
- 优化抽奖接口支持用户ID参数以应用称号效果
- 新增称号管理前端页面与分配功能
- 修复Windows时区错误与JSON字段初始化问题
- 移除无用管理接口代码并更新文档说明
2025-11-16 11:37:40 +08:00

178 lines
7.9 KiB
Go

package admin
import (
"encoding/json"
"net/http"
"bindbox-game/internal/code"
"bindbox-game/internal/pkg/core"
"bindbox-game/internal/repository/mysql/model"
)
type seedDefaultTitlesResponse struct {
Created int `json:"created"`
Exists int `json:"exists"`
IDs []int64 `json:"ids"`
}
// SeedDefaultTitles 内置6个称号与基础效果
func (h *handler) SeedDefaultTitles() core.HandlerFunc {
return func(ctx core.Context) {
// 定义默认称号与效果
type def struct {
Name string
Description string
EffectType int32
Params map[string]interface{}
Stack int32
CapX1000 int32
}
defs := []def{
{Name: "优惠券使者", Description: "可领取优惠券", EffectType: 1, Params: map[string]interface{}{ "template_id": 0, "frequency": map[string]interface{}{"period": "day", "times": 1}}, Stack: 1, CapX1000: 0},
{Name: "折扣官", Description: "抽奖购票折扣", EffectType: 2, Params: map[string]interface{}{ "discount_type": "percentage", "value_x1000": 100, "max_discount_x1000": 300}, Stack: 0, CapX1000: 300},
{Name: "签到达人", Description: "签到双倍积分", EffectType: 3, Params: map[string]interface{}{ "multiplier_x1000": 2000, "daily_cap_points": 0}, Stack: 1, CapX1000: 3000},
{Name: "卡牌使者", Description: "可领取道具卡", EffectType: 4, Params: map[string]interface{}{ "template_id": 0, "frequency": map[string]interface{}{"period": "week", "times": 2}}, Stack: 1, CapX1000: 0},
{Name: "幸运加成者", Description: "抽奖概率加成", EffectType: 5, Params: map[string]interface{}{ "target_prize_ids": []int64{}, "boost_x1000": 100, "cap_x1000": 300}, Stack: 1, CapX1000: 300},
{Name: "双倍之王", Description: "奖品双倍概率", EffectType: 6, Params: map[string]interface{}{ "target_prize_ids": []int64{}, "chance_x1000": 200, "period_cap_times": 1}, Stack: 1, CapX1000: 500},
}
created := 0
exists := 0
var ids []int64
for _, d := range defs {
// 是否存在同名称号
row, _ := h.readDB.SystemTitles.WithContext(ctx.RequestContext()).Where(h.readDB.SystemTitles.Name.Eq(d.Name)).First()
if row != nil {
exists++
ids = append(ids, row.ID)
continue
}
// 创建称号(使用模型类型)
newRow := &model.SystemTitles{
Name: d.Name,
Description: d.Description,
Status: 1,
ObtainRulesJSON: "{}",
ScopesJSON: "{}",
}
if err := h.writeDB.SystemTitles.WithContext(ctx.RequestContext()).Create(newRow); err != nil {
ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, "创建称号失败"))
return
}
// 重新读取ID
trow, _ := h.readDB.SystemTitles.WithContext(ctx.RequestContext()).Where(h.readDB.SystemTitles.Name.Eq(d.Name)).First()
if trow == nil {
ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, "创建称号失败(读取)"))
return
}
ids = append(ids, trow.ID)
created++
// 创建效果
paramsBytes, _ := json.Marshal(d.Params)
eff := &model.SystemTitleEffects{
TitleID: trow.ID,
EffectType: d.EffectType,
ParamsJSON: string(paramsBytes),
StackingStrategy: d.Stack,
CapValueX1000: d.CapX1000,
ScopesJSON: "{}",
Sort: 1,
Status: 1,
}
if err := h.writeDB.SystemTitleEffects.WithContext(ctx.RequestContext()).Create(eff); err != nil {
ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, "创建称号效果失败"))
return
}
}
ctx.Payload(&seedDefaultTitlesResponse{Created: created, Exists: exists, IDs: ids})
}
}
type ensureTitlesMenuResponse struct {
Ensured bool `json:"ensured"`
Parent int64 `json:"parent_id"`
MenuID int64 `json:"menu_id"`
}
// EnsureTitlesMenu 确保运营菜单下存在“称号管理”子菜单
func (h *handler) EnsureTitlesMenu() core.HandlerFunc {
return func(ctx core.Context) {
// 查找运营菜单父节点
parent, _ := h.readDB.Menus.WithContext(ctx.RequestContext()).Where(h.readDB.Menus.Name.Eq("Operations")).First()
var parentID int64
if parent == nil {
// 创建运营父菜单
pm := &model.Menus{
ParentID: 0,
Path: "/operations",
Name: "Operations",
Component: "/index/index",
Icon: "ri:tools-line",
Sort: 10,
Status: true,
KeepAlive: true,
IsHide: false,
IsHideTab: false,
CreatedUser: "system",
UpdatedUser: "system",
}
if err := h.writeDB.Menus.WithContext(ctx.RequestContext()).Create(pm); err != nil {
ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, "创建运营菜单失败"))
return
}
// 读取
parent, _ = h.readDB.Menus.WithContext(ctx.RequestContext()).Where(h.readDB.Menus.Name.Eq("Operations")).First()
if parent == nil {
ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, "创建运营菜单失败(读取)"))
return
}
}
parentID = parent.ID
// 查找称号菜单
titlesMenu, _ := h.readDB.Menus.WithContext(ctx.RequestContext()).Where(h.readDB.Menus.Path.Eq("titles")).Where(h.readDB.Menus.ParentID.Eq(parentID)).First()
if titlesMenu == nil {
tm := &struct {
ParentID int64
Path string
Name string
Component string
Icon string
Sort int32
Status bool
KeepAlive bool
IsHide bool
IsHideTab bool
}{
ParentID: parentID,
Path: "titles",
Name: "Titles",
Component: "/operations/titles",
Icon: "ri:medal-line",
Sort: 50,
Status: true,
KeepAlive: true,
IsHide: false,
IsHideTab: false,
}
mm := &model.Menus{
ParentID: tm.ParentID,
Path: tm.Path,
Name: tm.Name,
Component: tm.Component,
Icon: tm.Icon,
Sort: tm.Sort,
Status: tm.Status,
KeepAlive: tm.KeepAlive,
IsHide: tm.IsHide,
IsHideTab: tm.IsHideTab,
CreatedUser: "system",
UpdatedUser: "system",
}
if err := h.writeDB.Menus.WithContext(ctx.RequestContext()).Create(mm); err != nil {
ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, "创建称号菜单失败"))
return
}
titlesMenu, _ = h.readDB.Menus.WithContext(ctx.RequestContext()).Where(h.readDB.Menus.Path.Eq("titles")).Where(h.readDB.Menus.ParentID.Eq(parentID)).First()
}
ctx.Payload(&ensureTitlesMenuResponse{Ensured: true, Parent: parentID, MenuID: titlesMenu.ID})
}
}