Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 39s
feat(抽奖动态): 修复抽奖动态未渲染问题并优化文案展示 fix(用户概览): 修复用户概览无数据显示问题 feat(新用户列表): 在新用户列表显示称号明细 refactor(待办事项): 移除代办模块并全宽展示实时动态 feat(批量操作): 限制为单用户操作并在批量时提醒 fix(称号分配): 防重复分配称号的改造计划 perf(接口性能): 优化新用户和抽奖动态接口性能 feat(订单漏斗): 优化订单转化漏斗指标计算 docs(测试计划): 完善盲盒运营API核查与闭环测试计划
491 lines
22 KiB
Go
491 lines
22 KiB
Go
package admin
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"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 listSystemTitlesRequest struct {
|
|
Page int `form:"page"`
|
|
PageSize int `form:"page_size"`
|
|
Name string `form:"name"`
|
|
Status *int32 `form:"status"`
|
|
}
|
|
type listSystemTitlesResponse struct {
|
|
Page int `json:"page"`
|
|
PageSize int `json:"page_size"`
|
|
Total int64 `json:"total"`
|
|
List []*model.SystemTitles `json:"list"`
|
|
}
|
|
|
|
// ListSystemTitles 系统称号列表
|
|
func (h *handler) ListSystemTitles() core.HandlerFunc {
|
|
return func(ctx core.Context) {
|
|
req := new(listSystemTitlesRequest)
|
|
rsp := new(listSystemTitlesResponse)
|
|
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 }
|
|
if req.PageSize > 100 { req.PageSize = 100 }
|
|
q := h.readDB.SystemTitles.WithContext(ctx.RequestContext()).ReadDB()
|
|
if req.Name != "" { q = q.Where(h.readDB.SystemTitles.Name.Like("%" + req.Name + "%")) }
|
|
if req.Status != nil { q = q.Where(h.readDB.SystemTitles.Status.Eq(*req.Status)) }
|
|
total, err := q.Count()
|
|
if err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 30101, err.Error()))
|
|
return
|
|
}
|
|
rows, err := q.Order(h.readDB.SystemTitles.ID.Desc()).
|
|
Offset((req.Page-1)*req.PageSize).Limit(req.PageSize).Find()
|
|
if err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 30102, err.Error()))
|
|
return
|
|
}
|
|
rsp.Page = req.Page
|
|
rsp.PageSize = req.PageSize
|
|
rsp.Total = total
|
|
rsp.List = rows
|
|
ctx.Payload(rsp)
|
|
}
|
|
}
|
|
|
|
type createSystemTitleRequest struct {
|
|
Name string `json:"name" binding:"required,min=1"`
|
|
Description string `json:"description"`
|
|
Status int32 `json:"status"`
|
|
ObtainRulesJSON string `json:"obtain_rules_json"`
|
|
ScopesJSON string `json:"scopes_json"`
|
|
}
|
|
|
|
type modifySystemTitleRequest struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Status *int32 `json:"status"`
|
|
ObtainRulesJSON string `json:"obtain_rules_json"`
|
|
ScopesJSON string `json:"scopes_json"`
|
|
}
|
|
|
|
type simpleMessageResponseTitle struct {
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
func (h *handler) CreateSystemTitle() core.HandlerFunc {
|
|
return func(ctx core.Context) {
|
|
var req createSystemTitleRequest
|
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
|
|
return
|
|
}
|
|
if req.ObtainRulesJSON == "" { req.ObtainRulesJSON = "{}" }
|
|
if req.ScopesJSON == "" { req.ScopesJSON = "{}" }
|
|
it := &model.SystemTitles{
|
|
Name: req.Name,
|
|
Description: req.Description,
|
|
Status: req.Status,
|
|
ObtainRulesJSON: req.ObtainRulesJSON,
|
|
ScopesJSON: req.ScopesJSON,
|
|
}
|
|
if err := h.writeDB.SystemTitles.WithContext(ctx.RequestContext()).Create(it); err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusInternalServerError, 30106, "创建称号失败"))
|
|
return
|
|
}
|
|
ctx.Payload(&simpleMessageResponseTitle{Message: "创建成功"})
|
|
}
|
|
}
|
|
|
|
func (h *handler) ModifySystemTitle() core.HandlerFunc {
|
|
return func(ctx core.Context) {
|
|
titleID, err := strconv.ParseInt(ctx.Param("title_id"), 10, 64)
|
|
if err != nil || titleID <= 0 {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递称号ID"))
|
|
return
|
|
}
|
|
var req modifySystemTitleRequest
|
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
|
|
return
|
|
}
|
|
row, err := h.readDB.SystemTitles.WithContext(ctx.RequestContext()).Where(h.readDB.SystemTitles.ID.Eq(titleID)).First()
|
|
if err != nil || row == nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 30107, "称号不存在"))
|
|
return
|
|
}
|
|
if req.Name != "" { row.Name = req.Name }
|
|
row.Description = req.Description
|
|
if req.Status != nil { row.Status = *req.Status }
|
|
if req.ObtainRulesJSON != "" { row.ObtainRulesJSON = req.ObtainRulesJSON }
|
|
if req.ScopesJSON != "" { row.ScopesJSON = req.ScopesJSON }
|
|
if err := h.writeDB.SystemTitles.Save(row); err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusInternalServerError, 30108, "修改称号失败"))
|
|
return
|
|
}
|
|
ctx.Payload(&simpleMessageResponseTitle{Message: "修改成功"})
|
|
}
|
|
}
|
|
|
|
func (h *handler) DeleteSystemTitle() core.HandlerFunc {
|
|
return func(ctx core.Context) {
|
|
titleID, err := strconv.ParseInt(ctx.Param("title_id"), 10, 64)
|
|
if err != nil || titleID <= 0 {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递称号ID"))
|
|
return
|
|
}
|
|
del := &model.SystemTitles{ID: titleID}
|
|
if _, err := h.writeDB.SystemTitles.Delete(del); err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusInternalServerError, 30109, "删除称号失败"))
|
|
return
|
|
}
|
|
ctx.Payload(&simpleMessageResponseTitle{Message: "删除成功"})
|
|
}
|
|
}
|
|
|
|
type listEffectsResponse struct {
|
|
List []*model.SystemTitleEffects `json:"list"`
|
|
Total int64 `json:"total"`
|
|
}
|
|
|
|
func (h *handler) ListSystemTitleEffects() core.HandlerFunc {
|
|
return func(ctx core.Context) {
|
|
titleID, err := strconv.ParseInt(ctx.Param("title_id"), 10, 64)
|
|
if err != nil || titleID <= 0 {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递称号ID"))
|
|
return
|
|
}
|
|
rows, err := h.readDB.SystemTitleEffects.WithContext(ctx.RequestContext()).Where(h.readDB.SystemTitleEffects.TitleID.Eq(titleID)).Order(h.readDB.SystemTitleEffects.Sort).Find()
|
|
if err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 30110, err.Error()))
|
|
return
|
|
}
|
|
total := int64(len(rows))
|
|
ctx.Payload(&listEffectsResponse{List: rows, Total: total})
|
|
}
|
|
}
|
|
|
|
type createEffectRequest struct {
|
|
EffectType int32 `json:"effect_type" binding:"required"`
|
|
ParamsJSON string `json:"params_json" binding:"required"`
|
|
StackingStrategy int32 `json:"stacking_strategy"`
|
|
CapValueX1000 int32 `json:"cap_value_x1000"`
|
|
ScopesJSON string `json:"scopes_json"`
|
|
Sort int32 `json:"sort"`
|
|
Status int32 `json:"status"`
|
|
}
|
|
|
|
func (h *handler) CreateSystemTitleEffect() core.HandlerFunc {
|
|
return func(ctx core.Context) {
|
|
titleID, err := strconv.ParseInt(ctx.Param("title_id"), 10, 64)
|
|
if err != nil || titleID <= 0 {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递称号ID"))
|
|
return
|
|
}
|
|
var req createEffectRequest
|
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
|
|
return
|
|
}
|
|
if req.ParamsJSON == "" { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "params_json不能为空")); return }
|
|
if req.ScopesJSON == "" { req.ScopesJSON = "{}" }
|
|
existed, _ := h.readDB.SystemTitleEffects.WithContext(ctx.RequestContext()).
|
|
Where(h.readDB.SystemTitleEffects.TitleID.Eq(titleID)).
|
|
Where(h.readDB.SystemTitleEffects.EffectType.Eq(req.EffectType)).First()
|
|
if existed != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 30115, "同类型效果已存在"))
|
|
return
|
|
}
|
|
sanitized, verr := validateEffectParams(req.EffectType, req.ParamsJSON)
|
|
if verr != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, verr.Error()))
|
|
return
|
|
}
|
|
ef := &model.SystemTitleEffects{
|
|
TitleID: titleID,
|
|
EffectType: req.EffectType,
|
|
ParamsJSON: sanitized,
|
|
StackingStrategy: req.StackingStrategy,
|
|
CapValueX1000: req.CapValueX1000,
|
|
ScopesJSON: req.ScopesJSON,
|
|
Sort: req.Sort,
|
|
Status: req.Status,
|
|
}
|
|
if err := h.writeDB.SystemTitleEffects.WithContext(ctx.RequestContext()).Create(ef); err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusInternalServerError, 30111, "创建效果失败"))
|
|
return
|
|
}
|
|
ctx.Payload(&simpleMessageResponseTitle{Message: "创建成功"})
|
|
}
|
|
}
|
|
|
|
type modifyEffectRequest struct {
|
|
EffectType *int32 `json:"effect_type"`
|
|
ParamsJSON string `json:"params_json"`
|
|
StackingStrategy *int32 `json:"stacking_strategy"`
|
|
CapValueX1000 *int32 `json:"cap_value_x1000"`
|
|
ScopesJSON string `json:"scopes_json"`
|
|
Sort *int32 `json:"sort"`
|
|
Status *int32 `json:"status"`
|
|
}
|
|
|
|
func (h *handler) ModifySystemTitleEffect() core.HandlerFunc {
|
|
return func(ctx core.Context) {
|
|
titleID, err := strconv.ParseInt(ctx.Param("title_id"), 10, 64)
|
|
if err != nil || titleID <= 0 {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递称号ID"))
|
|
return
|
|
}
|
|
effectID, err := strconv.ParseInt(ctx.Param("effect_id"), 10, 64)
|
|
if err != nil || effectID <= 0 {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递效果ID"))
|
|
return
|
|
}
|
|
var req modifyEffectRequest
|
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
|
|
return
|
|
}
|
|
row, err := h.readDB.SystemTitleEffects.WithContext(ctx.RequestContext()).Where(h.readDB.SystemTitleEffects.ID.Eq(effectID)).Where(h.readDB.SystemTitleEffects.TitleID.Eq(titleID)).First()
|
|
if err != nil || row == nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 30112, "效果不存在"))
|
|
return
|
|
}
|
|
if req.EffectType != nil {
|
|
existed, _ := h.readDB.SystemTitleEffects.WithContext(ctx.RequestContext()).
|
|
Where(h.readDB.SystemTitleEffects.TitleID.Eq(titleID)).
|
|
Where(h.readDB.SystemTitleEffects.EffectType.Eq(*req.EffectType)).
|
|
Where(h.readDB.SystemTitleEffects.ID.Neq(effectID)).First()
|
|
if existed != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 30116, "同类型效果已存在"))
|
|
return
|
|
}
|
|
row.EffectType = *req.EffectType
|
|
}
|
|
if req.ParamsJSON != "" {
|
|
et := row.EffectType
|
|
if req.EffectType != nil { et = *req.EffectType }
|
|
sanitized, verr := validateEffectParams(et, req.ParamsJSON)
|
|
if verr != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, verr.Error()))
|
|
return
|
|
}
|
|
row.ParamsJSON = sanitized
|
|
}
|
|
if req.StackingStrategy != nil { row.StackingStrategy = *req.StackingStrategy }
|
|
if req.CapValueX1000 != nil { row.CapValueX1000 = *req.CapValueX1000 }
|
|
if req.ScopesJSON != "" { row.ScopesJSON = req.ScopesJSON }
|
|
if req.Sort != nil { row.Sort = *req.Sort }
|
|
if req.Status != nil { row.Status = *req.Status }
|
|
if err := h.writeDB.SystemTitleEffects.Save(row); err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusInternalServerError, 30113, "修改效果失败"))
|
|
return
|
|
}
|
|
ctx.Payload(&simpleMessageResponseTitle{Message: "修改成功"})
|
|
}
|
|
}
|
|
|
|
func validateEffectParams(effectType int32, raw string) (string, error) {
|
|
dec := json.NewDecoder(bytes.NewBufferString(raw))
|
|
dec.DisallowUnknownFields()
|
|
switch effectType {
|
|
case 1:
|
|
var p struct {
|
|
TemplateID int64 `json:"template_id"`
|
|
Frequency struct {
|
|
Period string `json:"period"`
|
|
Times int `json:"times"`
|
|
} `json:"frequency"`
|
|
}
|
|
if err := dec.Decode(&p); err != nil { return "", err }
|
|
if p.TemplateID < 0 { return "", fmt.Errorf("template_id无效") }
|
|
if p.Frequency.Times < 1 || p.Frequency.Times > 100 { return "", fmt.Errorf("times范围错误") }
|
|
if p.Frequency.Period != "day" && p.Frequency.Period != "week" && p.Frequency.Period != "month" { return "", fmt.Errorf("period无效") }
|
|
b, _ := json.Marshal(p); return string(b), nil
|
|
case 2:
|
|
var p struct {
|
|
DiscountType string `json:"discount_type"`
|
|
ValueX1000 int32 `json:"value_x1000"`
|
|
MaxDiscountX1000 int32 `json:"max_discount_x1000"`
|
|
}
|
|
if err := dec.Decode(&p); err != nil { return "", err }
|
|
if p.DiscountType != "percentage" && p.DiscountType != "fixed" { return "", fmt.Errorf("discount_type无效") }
|
|
if p.ValueX1000 < 0 || p.MaxDiscountX1000 < 0 { return "", fmt.Errorf("数值必须>=0") }
|
|
b, _ := json.Marshal(p); return string(b), nil
|
|
case 3:
|
|
var p struct {
|
|
MultiplierX1000 int32 `json:"multiplier_x1000"`
|
|
DailyCapPoints int32 `json:"daily_cap_points"`
|
|
}
|
|
if err := dec.Decode(&p); err != nil { return "", err }
|
|
if p.MultiplierX1000 < 0 || p.DailyCapPoints < 0 { return "", fmt.Errorf("数值必须>=0") }
|
|
b, _ := json.Marshal(p); return string(b), nil
|
|
case 4:
|
|
var p struct {
|
|
TemplateID int64 `json:"template_id"`
|
|
Frequency struct {
|
|
Period string `json:"period"`
|
|
Times int `json:"times"`
|
|
} `json:"frequency"`
|
|
}
|
|
if err := dec.Decode(&p); err != nil { return "", err }
|
|
if p.TemplateID < 0 { return "", fmt.Errorf("template_id无效") }
|
|
if p.Frequency.Times < 1 || p.Frequency.Times > 100 { return "", fmt.Errorf("times范围错误") }
|
|
if p.Frequency.Period != "week" && p.Frequency.Period != "month" { return "", fmt.Errorf("period无效") }
|
|
b, _ := json.Marshal(p); return string(b), nil
|
|
case 5:
|
|
var p struct {
|
|
TargetPrizeIDs []int64 `json:"target_prize_ids"`
|
|
BoostX1000 int32 `json:"boost_x1000"`
|
|
CapX1000 *int32 `json:"cap_x1000"`
|
|
}
|
|
if err := dec.Decode(&p); err != nil { return "", err }
|
|
if p.BoostX1000 < 0 || p.BoostX1000 > 100000 { return "", fmt.Errorf("boost_x1000范围错误") }
|
|
if p.CapX1000 != nil && *p.CapX1000 < 0 { return "", fmt.Errorf("cap_x1000必须>=0") }
|
|
if len(p.TargetPrizeIDs) > 200 { return "", fmt.Errorf("target_prize_ids数量过多") }
|
|
m := make(map[int64]struct{})
|
|
out := make([]int64, 0, len(p.TargetPrizeIDs))
|
|
for _, id := range p.TargetPrizeIDs { if _, ok := m[id]; !ok { m[id] = struct{}{}; out = append(out, id) } }
|
|
p.TargetPrizeIDs = out
|
|
b, _ := json.Marshal(p); return string(b), nil
|
|
case 6:
|
|
var p struct {
|
|
TargetPrizeIDs []int64 `json:"target_prize_ids"`
|
|
ChanceX1000 int32 `json:"chance_x1000"`
|
|
PeriodCapTimes *int32 `json:"period_cap_times"`
|
|
}
|
|
if err := dec.Decode(&p); err != nil { return "", err }
|
|
if p.ChanceX1000 < 0 || p.ChanceX1000 > 100000 { return "", fmt.Errorf("chance_x1000范围错误") }
|
|
if p.PeriodCapTimes != nil && *p.PeriodCapTimes < 0 { return "", fmt.Errorf("period_cap_times必须>=0") }
|
|
if len(p.TargetPrizeIDs) > 200 { return "", fmt.Errorf("target_prize_ids数量过多") }
|
|
m := make(map[int64]struct{})
|
|
out := make([]int64, 0, len(p.TargetPrizeIDs))
|
|
for _, id := range p.TargetPrizeIDs { if _, ok := m[id]; !ok { m[id] = struct{}{}; out = append(out, id) } }
|
|
p.TargetPrizeIDs = out
|
|
b, _ := json.Marshal(p); return string(b), nil
|
|
default:
|
|
return raw, nil
|
|
}
|
|
}
|
|
|
|
func (h *handler) DeleteSystemTitleEffect() core.HandlerFunc {
|
|
return func(ctx core.Context) {
|
|
titleID, err := strconv.ParseInt(ctx.Param("title_id"), 10, 64)
|
|
if err != nil || titleID <= 0 {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递称号ID"))
|
|
return
|
|
}
|
|
effectID, err := strconv.ParseInt(ctx.Param("effect_id"), 10, 64)
|
|
if err != nil || effectID <= 0 {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递效果ID"))
|
|
return
|
|
}
|
|
del := &model.SystemTitleEffects{ID: effectID, TitleID: titleID}
|
|
if _, err := h.writeDB.SystemTitleEffects.Delete(del); err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusInternalServerError, 30114, "删除效果失败"))
|
|
return
|
|
}
|
|
ctx.Payload(&simpleMessageResponseTitle{Message: "删除成功"})
|
|
}
|
|
}
|
|
type assignUserTitleRequest struct {
|
|
TitleID int64 `json:"title_id" binding:"required,min=1"`
|
|
ExpiresAt *string `json:"expires_at"` // RFC3339 字符串,可空
|
|
Remark string `json:"remark"`
|
|
}
|
|
type assignUserTitleResponse struct {
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
// AssignUserTitle 给用户分配称号(存在则更新有效期与备注,并激活)
|
|
func (h *handler) AssignUserTitle() core.HandlerFunc {
|
|
return func(ctx core.Context) {
|
|
userID, err := strconv.ParseInt(ctx.Param("user_id"), 10, 64)
|
|
if err != nil || userID <= 0 {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递用户ID"))
|
|
return
|
|
}
|
|
var req assignUserTitleRequest
|
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
|
|
return
|
|
}
|
|
// 校验称号存在且启用
|
|
title, err := h.readDB.SystemTitles.WithContext(ctx.RequestContext()).
|
|
Where(h.readDB.SystemTitles.ID.Eq(req.TitleID)).First()
|
|
if err != nil || title == nil || title.Status != 1 {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 30103, "称号不存在或未启用"))
|
|
return
|
|
}
|
|
// 解析过期时间
|
|
var exp time.Time
|
|
if req.ExpiresAt != nil && *req.ExpiresAt != "" {
|
|
t, perr := time.Parse(time.RFC3339, *req.ExpiresAt)
|
|
if perr != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "expires_at格式错误"))
|
|
return
|
|
}
|
|
exp = t
|
|
}
|
|
// upsert 用户称号
|
|
// 先查是否存在
|
|
ut, err := h.readDB.UserTitles.WithContext(ctx.RequestContext()).
|
|
Where(h.readDB.UserTitles.UserID.Eq(userID)).
|
|
Where(h.readDB.UserTitles.TitleID.Eq(req.TitleID)).First()
|
|
if err == nil && ut != nil {
|
|
now := time.Now()
|
|
if ut.Active == 1 && (ut.ExpiresAt.IsZero() || ut.ExpiresAt.After(now)) {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 30117, "该用户已拥有该称号"))
|
|
return
|
|
}
|
|
// 更新
|
|
ut.Active = 1
|
|
ut.Remark = req.Remark
|
|
if !exp.IsZero() { ut.ExpiresAt = exp }
|
|
if err := h.writeDB.UserTitles.Save(ut); err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusInternalServerError, 30104, "更新称号失败"))
|
|
return
|
|
}
|
|
others, _ := h.readDB.UserTitles.WithContext(ctx.RequestContext()).
|
|
Where(h.readDB.UserTitles.UserID.Eq(userID)).
|
|
Where(h.readDB.UserTitles.TitleID.Neq(req.TitleID)).
|
|
Where(h.readDB.UserTitles.Active.Eq(1)).Find()
|
|
for _, o := range others {
|
|
o.Active = 0
|
|
_ = h.writeDB.UserTitles.Save(o)
|
|
}
|
|
} else {
|
|
now := time.Now()
|
|
newUT := &model.UserTitles{
|
|
UserID: userID,
|
|
TitleID: req.TitleID,
|
|
Active: 1,
|
|
ObtainedAt: now,
|
|
Source: "admin_assign",
|
|
Remark: req.Remark,
|
|
}
|
|
if !exp.IsZero() { newUT.ExpiresAt = exp }
|
|
if err := h.writeDB.UserTitles.Create(newUT); err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusInternalServerError, 30105, "分配称号失败"))
|
|
return
|
|
}
|
|
others, _ := h.readDB.UserTitles.WithContext(ctx.RequestContext()).
|
|
Where(h.readDB.UserTitles.UserID.Eq(userID)).
|
|
Where(h.readDB.UserTitles.TitleID.Neq(req.TitleID)).
|
|
Where(h.readDB.UserTitles.Active.Eq(1)).Find()
|
|
for _, o := range others {
|
|
o.Active = 0
|
|
_ = h.writeDB.UserTitles.Save(o)
|
|
}
|
|
}
|
|
ctx.Payload(&assignUserTitleResponse{Message: "分配称号成功"})
|
|
}
|
|
} |