diff --git a/.DS_Store b/.DS_Store index b3c81ee..c93aac5 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/README.md b/README.md index bf7f7df..66fc8e2 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -tags timetzda export DOCKER_DEFAULT_PLATFORM=linux/amd64 -docker build -t zfc931912343/bindbox-game:v1.8 . -docker push zfc931912343/bindbox-game:v1.8 +docker build -t zfc931912343/bindbox-game:v1.9 . +docker push zfc931912343/bindbox-game:v1.9 -docker pull zfc931912343/bindbox-game:v1.8 &&docker rm -f bindbox-game && docker run -d --name bindbox-game -p 9991:9991 zfc931912343/bindbox-game:v1.8 \ No newline at end of file +docker pull zfc931912343/bindbox-game:v1.9 &&docker rm -f bindbox-game && docker run -d --name bindbox-game -p 9991:9991 zfc931912343/bindbox-game:v1.9 diff --git a/build.zip b/build.zip new file mode 100644 index 0000000..9a8b4f4 Binary files /dev/null and b/build.zip differ diff --git a/build/resources/admin/assets/index-BmVnH7U1.css b/build/resources/admin/assets/index-BmVnH7U1.css deleted file mode 100644 index 7cafbc0..0000000 --- a/build/resources/admin/assets/index-BmVnH7U1.css +++ /dev/null @@ -1 +0,0 @@ -.page[data-v-5a52cd3e]{padding:12px}.mb-3[data-v-5a52cd3e]{margin-bottom:12px} diff --git a/build/resources/admin/assets/index-DZmctYbl.css b/build/resources/admin/assets/index-DZmctYbl.css deleted file mode 100644 index edf5223..0000000 --- a/build/resources/admin/assets/index-DZmctYbl.css +++ /dev/null @@ -1 +0,0 @@ -.page-container[data-v-c90a46c6]{padding:16px}.quick-actions[data-v-c90a46c6]{margin-bottom:16px}.ellipsis[data-v-c90a46c6]{display:inline-block;max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.compact-actions[data-v-c90a46c6]{display:flex;align-items:center;gap:6px;white-space:nowrap} diff --git a/build/resources/admin/assets/index-Dr092ZhG.css b/build/resources/admin/assets/index-Dr092ZhG.css deleted file mode 100644 index faf84e6..0000000 --- a/build/resources/admin/assets/index-Dr092ZhG.css +++ /dev/null @@ -1 +0,0 @@ -.page[data-v-15ff8e59]{padding:12px}.mb-3[data-v-15ff8e59]{margin-bottom:12px} diff --git a/build/resources/admin/index.html b/build/resources/admin/index.html index aca2404..6cc47bf 100644 --- a/build/resources/admin/index.html +++ b/build/resources/admin/index.html @@ -38,8 +38,8 @@ } })() - - + + diff --git a/docs/docs.go b/docs/docs.go index 14f39fb..05427e6 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -5057,6 +5057,58 @@ const docTemplate = `{ } } }, + "/api/app/users/{user_id}/inventory/cancel-shipping": { + "post": { + "security": [ + { + "LoginVerifyToken": [] + } + ], + "description": "取消已提交但未发货的申请;恢复库存状态", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "APP端.用户" + ], + "summary": "取消发货申请", + "parameters": [ + { + "type": "integer", + "description": "用户ID", + "name": "user_id", + "in": "path", + "required": true + }, + { + "description": "请求参数:资产ID", + "name": "RequestBody", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/app.cancelShippingRequest" + } + } + ], + "responses": { + "200": { + "description": "成功", + "schema": { + "$ref": "#/definitions/app.cancelShippingResponse" + } + }, + "400": { + "description": "参数错误/记录不存在/已处理", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, "/api/app/users/{user_id}/inventory/redeem": { "post": { "security": [ @@ -8286,6 +8338,17 @@ const docTemplate = `{ } } }, + "app.cancelShippingRequest": { + "type": "object", + "properties": { + "inventory_id": { + "type": "integer" + } + } + }, + "app.cancelShippingResponse": { + "type": "object" + }, "app.couponDetail": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 0d52fd7..897fe26 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -5049,6 +5049,58 @@ } } }, + "/api/app/users/{user_id}/inventory/cancel-shipping": { + "post": { + "security": [ + { + "LoginVerifyToken": [] + } + ], + "description": "取消已提交但未发货的申请;恢复库存状态", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "APP端.用户" + ], + "summary": "取消发货申请", + "parameters": [ + { + "type": "integer", + "description": "用户ID", + "name": "user_id", + "in": "path", + "required": true + }, + { + "description": "请求参数:资产ID", + "name": "RequestBody", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/app.cancelShippingRequest" + } + } + ], + "responses": { + "200": { + "description": "成功", + "schema": { + "$ref": "#/definitions/app.cancelShippingResponse" + } + }, + "400": { + "description": "参数错误/记录不存在/已处理", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, "/api/app/users/{user_id}/inventory/redeem": { "post": { "security": [ @@ -8278,6 +8330,17 @@ } } }, + "app.cancelShippingRequest": { + "type": "object", + "properties": { + "inventory_id": { + "type": "integer" + } + } + }, + "app.cancelShippingResponse": { + "type": "object" + }, "app.couponDetail": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index d2eede2..e190294 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1393,6 +1393,13 @@ definitions: status: type: integer type: object + app.cancelShippingRequest: + properties: + inventory_id: + type: integer + type: object + app.cancelShippingResponse: + type: object app.couponDetail: properties: amount: @@ -6113,6 +6120,39 @@ paths: summary: 撤销共享地址链接 tags: - APP端.用户 + /api/app/users/{user_id}/inventory/cancel-shipping: + post: + consumes: + - application/json + description: 取消已提交但未发货的申请;恢复库存状态 + parameters: + - description: 用户ID + in: path + name: user_id + required: true + type: integer + - description: 请求参数:资产ID + in: body + name: RequestBody + required: true + schema: + $ref: '#/definitions/app.cancelShippingRequest' + produces: + - application/json + responses: + "200": + description: 成功 + schema: + $ref: '#/definitions/app.cancelShippingResponse' + "400": + description: 参数错误/记录不存在/已处理 + schema: + $ref: '#/definitions/code.Failure' + security: + - LoginVerifyToken: [] + summary: 取消发货申请 + tags: + - APP端.用户 /api/app/users/{user_id}/inventory/redeem: post: consumes: diff --git a/internal/api/activity/app.go b/internal/api/activity/app.go index 5a6a563..f5d4fa0 100644 --- a/internal/api/activity/app.go +++ b/internal/api/activity/app.go @@ -5,7 +5,6 @@ import ( "bindbox-game/internal/repository/mysql" "bindbox-game/internal/repository/mysql/dao" activitysvc "bindbox-game/internal/service/activity" - syscfgsvc "bindbox-game/internal/service/sysconfig" titlesvc "bindbox-game/internal/service/title" usersvc "bindbox-game/internal/service/user" @@ -17,13 +16,11 @@ type handler struct { writeDB *dao.Query readDB *dao.Query activity activitysvc.Service - syscfg syscfgsvc.Service title titlesvc.Service repo mysql.Repo user usersvc.Service redis *redis.Client activityOrder activitysvc.ActivityOrderService // 活动订单服务 - rewardEffects activitysvc.RewardEffectsService // 奖励效果服务 } func New(logger logger.CustomLogger, db mysql.Repo, rdb *redis.Client) *handler { @@ -32,12 +29,10 @@ func New(logger logger.CustomLogger, db mysql.Repo, rdb *redis.Client) *handler writeDB: dao.Use(db.GetDbW()), readDB: dao.Use(db.GetDbR()), activity: activitysvc.New(logger, db), - syscfg: syscfgsvc.New(logger, db), title: titlesvc.New(logger, db), repo: db, user: usersvc.New(logger, db), redis: rdb, activityOrder: activitysvc.NewActivityOrderService(logger, db), - rewardEffects: activitysvc.NewRewardEffectsService(logger, db), } } diff --git a/internal/api/activity/issue_choices_app.go b/internal/api/activity/issue_choices_app.go index 7e2b2d3..23c689f 100644 --- a/internal/api/activity/issue_choices_app.go +++ b/internal/api/activity/issue_choices_app.go @@ -50,12 +50,8 @@ func (h *handler) ListIssueChoices() core.HandlerFunc { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ListIssueRewardsError, err.Error())) return } - var total int64 - for _, r := range rewards { - if r.OriginalQty > 0 { - total += r.OriginalQty - } - } + // 一番赏:每种奖品 = 1个格位 + total := int64(len(rewards)) var claimed0 []int64 if err := h.repo.GetDbR().Raw("SELECT slot_index FROM issue_position_claims WHERE issue_id = ?", issueID).Scan(&claimed0).Error; err != nil { claimed0 = []int64{} diff --git a/internal/api/activity/issues_app.go b/internal/api/activity/issues_app.go index 0d24dea..38f6556 100644 --- a/internal/api/activity/issues_app.go +++ b/internal/api/activity/issues_app.go @@ -73,15 +73,14 @@ func (h *handler) ListActivityIssues() core.HandlerFunc { it := issueItem{ID: v.ID, IssueNumber: v.IssueNumber, Status: v.Status, Sort: v.Sort} if activityItem.ActivityCategoryID == 1 { rewards, _ := h.readDB.ActivityRewardSettings.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ActivityRewardSettings.IssueID.Eq(v.ID)).Find() - var totalQty int64 - var remainQty int64 - for _, r := range rewards { - if r.OriginalQty >= 0 { - totalQty += r.OriginalQty - } - if r.Quantity >= 0 { - remainQty += r.Quantity - } + // 一番赏:每种奖品 = 1个格位 + totalQty := int64(len(rewards)) + // 查询已占用格位数 + var claimedCnt int64 + _ = h.repo.GetDbR().Raw("SELECT COUNT(*) FROM issue_position_claims WHERE issue_id = ?", v.ID).Scan(&claimedCnt).Error + remainQty := totalQty - claimedCnt + if remainQty < 0 { + remainQty = 0 } it.TotalPrizeQuantity = totalQty it.RemainingPrizeQuantity = remainQty diff --git a/internal/api/activity/lottery_app.go b/internal/api/activity/lottery_app.go index 80cdda5..4f55750 100644 --- a/internal/api/activity/lottery_app.go +++ b/internal/api/activity/lottery_app.go @@ -81,8 +81,6 @@ func (h *handler) JoinLottery() core.HandlerFunc { if activity.DrawMode != "" { cfgMode = activity.DrawMode } - fmt.Printf("[抽奖下单] 用户=%d 活动ID=%d 期ID=%d 次数=%d 渠道=%s 优惠券ID=%v 道具卡ID=%v\n", userID, req.ActivityID, req.IssueID, req.Count, req.Channel, req.CouponID, req.ItemCardID) - fmt.Printf("[抽奖下单] 活动票价(分)=%d 允许优惠券=%t 允许道具卡=%t 抽奖模式=%s 玩法=%s\n", activity.PriceDraw, activity.AllowCoupons, activity.AllowItemCards, cfgMode, activity.PlayType) // 定时一番赏:开奖前20秒禁止下单,防止订单抖动 if activity.PlayType == "ichiban" && cfgMode == "scheduled" && !activity.ScheduledTime.IsZero() { now := time.Now() @@ -118,12 +116,9 @@ func (h *handler) JoinLottery() core.HandlerFunc { order.PointsAmount = 0 order.PointsLedgerID = 0 order.ActualAmount = order.TotalAmount - fmt.Printf("[抽奖下单] 订单总额(分)=%d 初始实付(分)=%d 备注=%s\n", order.TotalAmount, order.ActualAmount, order.Remark) applied := int64(0) if activity.AllowCoupons && req.CouponID != nil && *req.CouponID > 0 { - fmt.Printf("[抽奖下单] 尝试优惠券 用户券ID=%d 应用前实付(分)=%d 累计优惠(分)=%d\n", *req.CouponID, order.ActualAmount, order.DiscountAmount) applied = h.applyCouponWithCap(ctx, userID, order, req.ActivityID, *req.CouponID) - fmt.Printf("[抽奖下单] 优惠后 实付(分)=%d 累计优惠(分)=%d 备注=%s\n", order.ActualAmount, order.DiscountAmount, order.Remark) } // Title Discount Logic // 1. Fetch active effects for this user, scoped to this activity/issue/category @@ -168,7 +163,6 @@ func (h *handler) JoinLottery() core.HandlerFunc { if discount > 0 { order.ActualAmount -= discount - fmt.Printf("[抽奖下单] Title Discount Applied: -%d (EffectID: %d)\n", discount, ef.ID) // Append to remark or separate logging? if order.Remark == "" { order.Remark = fmt.Sprintf("title_discount:%d:%d", ef.ID, discount) @@ -220,7 +214,6 @@ func (h *handler) JoinLottery() core.HandlerFunc { rsp.JoinID = joinID rsp.OrderNo = orderNo rsp.DrawMode = cfgMode - fmt.Printf("[抽奖下单] 汇总 订单号=%s 总额(分)=%d 优惠(分)=%d 实付(分)=%d 队列=true 模式=%s\n", orderNo, order.TotalAmount, order.DiscountAmount, order.ActualAmount, cfgMode) if order.ActualAmount == 0 { now := time.Now() _, _ = h.writeDB.Orders.WithContext(ctx.RequestContext()).Where(h.writeDB.Orders.OrderNo.Eq(orderNo)).Updates(map[string]any{h.writeDB.Orders.Status.ColumnName().String(): 2, h.writeDB.Orders.PaidAt.ColumnName().String(): now}) @@ -289,210 +282,6 @@ func (h *handler) JoinLottery() core.HandlerFunc { } } } - if cfgMode == "instant" { - ord, _ := h.readDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.OrderNo.Eq(orderNo)).First() - if ord != nil { - // 解析次数 - slotsIdx, slotsCnt := parseSlotsCountsFromRemark(ord.Remark) - dc := func() int64 { - if len(slotsIdx) > 0 && len(slotsIdx) == len(slotsCnt) { - var s int64 - for i := range slotsCnt { - if slotsCnt[i] > 0 { - s += slotsCnt[i] - } - } - if s > 0 { - return s - } - } - remark := ord.Remark - p := 0 - for i := 0; i < len(remark); i++ { - if remark[i] == '|' { - seg := remark[p:i] - if len(seg) > 6 && seg[:6] == "count:" { - var n int64 - for j := 6; j < len(seg); j++ { - c := seg[j] - if c < '0' || c > '9' { - break - } - n = n*10 + int64(c-'0') - } - if n <= 0 { - return 1 - } - return n - } - p = i + 1 - } - } - return 1 - }() - sel := strat.NewDefault(h.readDB, h.writeDB) - logs, _ := h.readDB.ActivityDrawLogs.WithContext(ctx.RequestContext()).Where(h.readDB.ActivityDrawLogs.OrderID.Eq(ord.ID)).Find() - done := int64(len(logs)) - rem := make([]int64, len(slotsCnt)) - copy(rem, slotsCnt) - cur := 0 - for i := done; i < dc; i++ { - rid := int64(0) - var e2 error - if activity.PlayType == "ichiban" { - slot := func() int64 { - if len(slotsIdx) > 0 && len(slotsIdx) == len(rem) { - for cur < len(rem) && rem[cur] == 0 { - cur++ - } - if cur >= len(rem) { - return -1 - } - rem[cur]-- - return slotsIdx[cur] - 1 - } - return parseSlotFromRemark(ord.Remark) - }() - if slot >= 0 { - var cnt int64 - _ = h.repo.GetDbR().Raw("SELECT COUNT(*) FROM issue_position_claims WHERE issue_id=? AND slot_index=?", req.IssueID, slot).Scan(&cnt).Error - if cnt > 0 { - break - } - e := h.repo.GetDbW().Exec("INSERT INTO issue_position_claims (issue_id, slot_index, user_id, order_id, created_at) VALUES (?,?,?,?,NOW(3))", req.IssueID, slot, userID, ord.ID).Error - if e != nil { - break - } - } - } - var proof map[string]any - var rw *model.ActivityRewardSettings - var log *model.ActivityDrawLogs - if activity.PlayType == "ichiban" { - slot := parseSlotFromRemark(ord.Remark) - rid, proof, e2 = strat.NewIchiban(h.readDB, h.writeDB).SelectItemBySlot(ctx.RequestContext(), req.ActivityID, req.IssueID, slot) - if e2 == nil && rid > 0 { - rw, _ = h.readDB.ActivityRewardSettings.WithContext(ctx.RequestContext()).Where(h.readDB.ActivityRewardSettings.ID.Eq(rid)).First() - if rw != nil { - _ = strat.NewIchiban(h.readDB, h.writeDB).GrantReward(ctx.RequestContext(), userID, rid) - // 创建抽奖日志 - log = &model.ActivityDrawLogs{UserID: userID, IssueID: req.IssueID, OrderID: ord.ID, RewardID: rid, IsWinner: 1, Level: rw.Level, CurrentLevel: 1} - _ = h.writeDB.ActivityDrawLogs.WithContext(ctx.RequestContext()).Create(log) - // 保存凭证 - _ = strat.SaveDrawReceipt(ctx.RequestContext(), h.writeDB, log.ID, req.IssueID, userID, proof) - // ... 道具卡逻辑 - } - } - } else { - rid, proof, e2 = sel.SelectItem(ctx.RequestContext(), req.ActivityID, req.IssueID, userID) - if e2 == nil && rid > 0 { - rw, _ = h.readDB.ActivityRewardSettings.WithContext(ctx.RequestContext()).Where(h.readDB.ActivityRewardSettings.ID.Eq(rid)).First() - if rw != nil { - _, _ = h.user.GrantRewardToOrder(ctx.RequestContext(), userID, usersvc.GrantRewardToOrderRequest{OrderID: ord.ID, ProductID: rw.ProductID, Quantity: 1, ActivityID: &req.ActivityID, RewardID: &rid, Remark: rw.Name}) - // 创建抽奖日志 - log = &model.ActivityDrawLogs{UserID: userID, IssueID: req.IssueID, OrderID: ord.ID, RewardID: rid, IsWinner: 1, Level: rw.Level, CurrentLevel: 1} - _ = h.writeDB.ActivityDrawLogs.WithContext(ctx.RequestContext()).Create(log) - // 保存凭证 - _ = strat.SaveDrawReceipt(ctx.RequestContext(), h.writeDB, log.ID, req.IssueID, userID, proof) - // ... 道具卡逻辑 - } - } - } - if log == nil { - break - } - // 道具卡效果(奖励倍数/概率提升的简单实现:奖励倍数=额外发同奖品;概率提升=尝试升级到更高等级) - fmt.Printf("[道具卡-JoinLottery] 开始检查 活动允许道具卡=%t 请求道具卡ID=%v\n", activity.AllowItemCards, req.ItemCardID) - if activity.AllowItemCards && req.ItemCardID != nil && *req.ItemCardID > 0 { - fmt.Printf("[道具卡-JoinLottery] 查询用户道具卡 用户ID=%d 道具卡ID=%d\n", userID, *req.ItemCardID) - uic, _ := h.readDB.UserItemCards.WithContext(ctx.RequestContext()).Where(h.readDB.UserItemCards.ID.Eq(*req.ItemCardID), h.readDB.UserItemCards.UserID.Eq(userID), h.readDB.UserItemCards.Status.Eq(1)).First() - if uic != nil { - fmt.Printf("[道具卡-JoinLottery] 找到用户道具卡 ID=%d CardID=%d Status=%d ValidStart=%s ValidEnd=%s\n", uic.ID, uic.CardID, uic.Status, uic.ValidStart.Format(time.RFC3339), uic.ValidEnd.Format(time.RFC3339)) - ic, _ := h.readDB.SystemItemCards.WithContext(ctx.RequestContext()).Where(h.readDB.SystemItemCards.ID.Eq(uic.CardID), h.readDB.SystemItemCards.Status.Eq(1)).First() - now := time.Now() - if ic != nil { - fmt.Printf("[道具卡-JoinLottery] 找到系统道具卡 ID=%d Name=%s EffectType=%d RewardMultiplierX1000=%d ScopeType=%d ActivityID=%d IssueID=%d Status=%d\n", ic.ID, ic.Name, ic.EffectType, ic.RewardMultiplierX1000, ic.ScopeType, ic.ActivityID, ic.IssueID, ic.Status) - fmt.Printf("[道具卡-JoinLottery] 时间检查 当前时间=%s ValidStart.After(now)=%t ValidEnd.Before(now)=%t\n", now.Format(time.RFC3339), uic.ValidStart.After(now), uic.ValidEnd.Before(now)) - if !uic.ValidStart.After(now) && !uic.ValidEnd.Before(now) { - scopeOK := (ic.ScopeType == 1) || (ic.ScopeType == 3 && ic.ActivityID == req.ActivityID) || (ic.ScopeType == 4 && ic.IssueID == req.IssueID) - fmt.Printf("[道具卡-JoinLottery] 范围检查 ScopeType=%d 请求ActivityID=%d 请求IssueID=%d scopeOK=%t\n", ic.ScopeType, req.ActivityID, req.IssueID, scopeOK) - if scopeOK { - if ic.EffectType == 1 && ic.RewardMultiplierX1000 >= 2000 { // ×2及以上:额外发一次相同奖品 - fmt.Printf("[道具卡-JoinLottery] ✅ 应用双倍奖励 倍数=%d 奖品ID=%d 奖品名=%s PlayType=%s\n", ic.RewardMultiplierX1000, rid, rw.Name, activity.PlayType) - if activity.PlayType == "ichiban" { - _ = strat.NewIchiban(h.readDB, h.writeDB).GrantReward(ctx.RequestContext(), userID, rid) - } else { - _, _ = h.user.GrantRewardToOrder(ctx.RequestContext(), userID, usersvc.GrantRewardToOrderRequest{OrderID: ord.ID, ProductID: rw.ProductID, Quantity: 1, ActivityID: &req.ActivityID, RewardID: &rid, Remark: rw.Name + "(倍数)"}) - } - fmt.Printf("[道具卡-JoinLottery] ✅ 双倍奖励发放完成\n") - } else if ic.EffectType == 2 && ic.BoostRateX1000 > 0 { // 概率提升:尝试升级到更高等级的可用奖品 - fmt.Printf("[道具卡-JoinLottery] 应用概率提升 BoostRateX1000=%d\n", ic.BoostRateX1000) - uprw, _ := h.readDB.ActivityRewardSettings.WithContext(ctx.RequestContext()).Where(h.readDB.ActivityRewardSettings.IssueID.Eq(req.IssueID)).Find() - var better *model.ActivityRewardSettings - for _, r := range uprw { - if r.Level < rw.Level && r.Quantity != 0 { - if better == nil || r.Level < better.Level { - better = r - } - } - } - if better != nil { - // 以boost率决定升级 - if rand.Int31n(1000) < ic.BoostRateX1000 { - rid2 := better.ID - fmt.Printf("[道具卡-JoinLottery] ✅ 概率提升成功 升级到奖品ID=%d 奖品名=%s\n", rid2, better.Name) - if activity.PlayType == "ichiban" { - _ = strat.NewIchiban(h.readDB, h.writeDB).GrantReward(ctx.RequestContext(), userID, rid2) - } else { - _, _ = h.user.GrantRewardToOrder(ctx.RequestContext(), userID, usersvc.GrantRewardToOrderRequest{OrderID: ord.ID, ProductID: better.ProductID, Quantity: 1, ActivityID: &req.ActivityID, RewardID: &rid2, Remark: better.Name + "(升级)"}) - } - // 创建抽奖日志并保存凭据 - drawLog := &model.ActivityDrawLogs{UserID: userID, IssueID: req.IssueID, OrderID: ord.ID, RewardID: rid2, IsWinner: 1, Level: better.Level, CurrentLevel: 1} - if err := h.writeDB.ActivityDrawLogs.WithContext(ctx.RequestContext()).Create(drawLog); err != nil { - fmt.Printf("[道具卡-JoinLottery] ❌ 创建抽奖日志失败 err=%v\n", err) - } else { - if err := strat.SaveDrawReceipt(ctx.RequestContext(), h.writeDB, drawLog.ID, req.IssueID, userID, proof); err != nil { - fmt.Printf("[道具卡-JoinLottery] ⚠️ 保存凭据失败 DrawLogID=%d IssueID=%d UserID=%d err=%v\n", drawLog.ID, req.IssueID, userID, err) - } else { - fmt.Printf("[道具卡-JoinLottery] ✅ 保存凭据成功 DrawLogID=%d IssueID=%d\n", drawLog.ID, req.IssueID) - } - } - } else { - fmt.Printf("[道具卡-JoinLottery] 概率提升未触发\n") - } - } else { - fmt.Printf("[道具卡-JoinLottery] 没有找到更好的奖品可升级\n") - } - } else { - fmt.Printf("[道具卡-JoinLottery] ⚠️ 道具卡效果类型不匹配或倍数不足 EffectType=%d RewardMultiplierX1000=%d\n", ic.EffectType, ic.RewardMultiplierX1000) - } - // 核销道具卡 - fmt.Printf("[道具卡-JoinLottery] 核销道具卡 用户道具卡ID=%d\n", *req.ItemCardID) - _, _ = h.writeDB.UserItemCards.WithContext(ctx.RequestContext()).Where(h.readDB.UserItemCards.ID.Eq(*req.ItemCardID), h.readDB.UserItemCards.UserID.Eq(userID), h.readDB.UserItemCards.Status.Eq(1)).Updates(map[string]any{ - h.readDB.UserItemCards.Status.ColumnName().String(): 2, - h.readDB.UserItemCards.UsedDrawLogID.ColumnName().String(): log.ID, - h.readDB.UserItemCards.UsedActivityID.ColumnName().String(): req.ActivityID, - h.readDB.UserItemCards.UsedIssueID.ColumnName().String(): req.IssueID, - h.readDB.UserItemCards.UsedAt.ColumnName().String(): time.Now(), - }) - } else { - fmt.Printf("[道具卡-JoinLottery] ❌ 范围检查失败\n") - } - } else { - fmt.Printf("[道具卡-JoinLottery] ❌ 时间检查失败\n") - } - } else { - fmt.Printf("[道具卡-JoinLottery] ❌ 未找到系统道具卡 CardID=%d\n", uic.CardID) - } - } else { - fmt.Printf("[道具卡-JoinLottery] ❌ 未找到用户道具卡 用户ID=%d 道具卡ID=%d\n", userID, *req.ItemCardID) - } - } else { - fmt.Printf("[道具卡-JoinLottery] 跳过道具卡检查\n") - } - } - } - } rsp.Queued = true } else { rsp.Queued = true @@ -600,6 +389,8 @@ func (h *handler) GetLotteryResult() core.HandlerFunc { ctx.AbortWithError(core.Error(http.StatusBadRequest, 170006, "order not paid")) return } + // Daily Seed logic removed to ensure strict adherence to CommitmentSeedMaster + if actCommit.PlayType == "ichiban" { slot := parseSlotFromRemark(ord.Remark) if slot >= 0 { @@ -634,20 +425,14 @@ func (h *handler) GetLotteryResult() core.HandlerFunc { _ = h.writeDB.ActivityDrawLogs.WithContext(ctx.RequestContext()).Create(log) _ = strat.SaveDrawReceipt(ctx.RequestContext(), h.writeDB, log.ID, issueID, userID, proof) icID := parseItemCardIDFromRemark(ord.Remark) - fmt.Printf("[道具卡-GetLotteryResult] 从订单备注解析道具卡ID icID=%d 订单备注=%s\n", icID, ord.Remark) if icID > 0 { - fmt.Printf("[道具卡-GetLotteryResult] 查询用户道具卡 用户ID=%d 道具卡ID=%d\n", userID, icID) uic, _ := h.readDB.UserItemCards.WithContext(ctx.RequestContext()).Where(h.readDB.UserItemCards.ID.Eq(icID), h.readDB.UserItemCards.UserID.Eq(userID), h.readDB.UserItemCards.Status.Eq(1)).First() if uic != nil { - fmt.Printf("[道具卡-GetLotteryResult] 找到用户道具卡 ID=%d CardID=%d Status=%d ValidStart=%s ValidEnd=%s\n", uic.ID, uic.CardID, uic.Status, uic.ValidStart.Format(time.RFC3339), uic.ValidEnd.Format(time.RFC3339)) ic, _ := h.readDB.SystemItemCards.WithContext(ctx.RequestContext()).Where(h.readDB.SystemItemCards.ID.Eq(uic.CardID), h.readDB.SystemItemCards.Status.Eq(1)).First() now := time.Now() if ic != nil { - fmt.Printf("[道具卡-GetLotteryResult] 找到系统道具卡 ID=%d Name=%s EffectType=%d RewardMultiplierX1000=%d ScopeType=%d ActivityID=%d IssueID=%d Status=%d\n", ic.ID, ic.Name, ic.EffectType, ic.RewardMultiplierX1000, ic.ScopeType, ic.ActivityID, ic.IssueID, ic.Status) - fmt.Printf("[道具卡-GetLotteryResult] 时间检查 当前时间=%s ValidStart.After(now)=%t ValidEnd.Before(now)=%t\n", now.Format(time.RFC3339), uic.ValidStart.After(now), uic.ValidEnd.Before(now)) if !uic.ValidStart.After(now) && !uic.ValidEnd.Before(now) { scopeOK := (ic.ScopeType == 1) || (ic.ScopeType == 3 && ic.ActivityID == activityID) || (ic.ScopeType == 4 && ic.IssueID == issueID) - fmt.Printf("[道具卡-GetLotteryResult] 范围检查 ScopeType=%d ActivityID=%d IssueID=%d scopeOK=%t\n", ic.ScopeType, activityID, issueID, scopeOK) if scopeOK { eff := &model.ActivityDrawEffects{ DrawLogID: log.ID, @@ -665,13 +450,9 @@ func (h *handler) GetLotteryResult() core.HandlerFunc { IssueID: issueID, } _ = h.writeDB.ActivityDrawEffects.WithContext(ctx.RequestContext()).Create(eff) - fmt.Printf("[道具卡-GetLotteryResult] 创建道具卡效果记录 EffectID=%d\n", eff.ID) if ic.EffectType == 1 && ic.RewardMultiplierX1000 >= 2000 { - fmt.Printf("[道具卡-GetLotteryResult] ✅ 应用双倍奖励 倍数=%d 奖品ID=%d 奖品名=%s\n", ic.RewardMultiplierX1000, rid, rw.Name) _, _ = h.user.GrantRewardToOrder(ctx.RequestContext(), userID, usersvc.GrantRewardToOrderRequest{OrderID: orderID, ProductID: rw.ProductID, Quantity: 1, ActivityID: &activityID, RewardID: &rid, Remark: rw.Name + "(倍数)"}) - fmt.Printf("[道具卡-GetLotteryResult] ✅ 双倍奖励发放完成\n") } else if ic.EffectType == 2 && ic.BoostRateX1000 > 0 { - fmt.Printf("[道具卡-GetLotteryResult] 应用概率提升 BoostRateX1000=%d\n", ic.BoostRateX1000) uprw, _ := h.readDB.ActivityRewardSettings.WithContext(ctx.RequestContext()).Where(h.readDB.ActivityRewardSettings.IssueID.Eq(issueID)).Find() var better *model.ActivityRewardSettings for _, r := range uprw { @@ -683,41 +464,29 @@ func (h *handler) GetLotteryResult() core.HandlerFunc { } if better != nil && rand.Int31n(1000) < ic.BoostRateX1000 { rid2 := better.ID - fmt.Printf("[道具卡-GetLotteryResult] ✅ 概率提升成功 升级到奖品ID=%d 奖品名=%s\n", rid2, better.Name) _, _ = h.user.GrantRewardToOrder(ctx.RequestContext(), userID, usersvc.GrantRewardToOrderRequest{OrderID: orderID, ProductID: better.ProductID, Quantity: 1, ActivityID: &activityID, RewardID: &rid2, Remark: better.Name + "(升级)"}) // 创建升级后的抽奖日志并保存凭据 drawLog2 := &model.ActivityDrawLogs{UserID: userID, IssueID: issueID, OrderID: orderID, RewardID: rid2, IsWinner: 1, Level: better.Level, CurrentLevel: 1} if err := h.writeDB.ActivityDrawLogs.WithContext(ctx.RequestContext()).Create(drawLog2); err != nil { - fmt.Printf("[道具卡-GetLotteryResult] ❌ 创建升级抽奖日志失败 err=%v\n", err) } else { if err := strat.SaveDrawReceipt(ctx.RequestContext(), h.writeDB, drawLog2.ID, issueID, userID, proof); err != nil { - fmt.Printf("[道具卡-GetLotteryResult] ⚠️ 保存升级凭据失败 DrawLogID=%d IssueID=%d UserID=%d err=%v\n", drawLog2.ID, issueID, userID, err) } else { - fmt.Printf("[道具卡-GetLotteryResult] ✅ 保存升级凭据成功 DrawLogID=%d IssueID=%d\n", drawLog2.ID, issueID) } } } else { - fmt.Printf("[道具卡-GetLotteryResult] 概率提升未触发\n") } } else { - fmt.Printf("[道具卡-GetLotteryResult] ⚠️ 道具卡效果类型不匹配或倍数不足 EffectType=%d RewardMultiplierX1000=%d\n", ic.EffectType, ic.RewardMultiplierX1000) } - fmt.Printf("[道具卡-GetLotteryResult] 核销道具卡 用户道具卡ID=%d\n", icID) _, _ = h.writeDB.UserItemCards.WithContext(ctx.RequestContext()).Where(h.readDB.UserItemCards.ID.Eq(icID), h.readDB.UserItemCards.UserID.Eq(userID), h.readDB.UserItemCards.Status.Eq(1)).Updates(map[string]any{h.readDB.UserItemCards.Status.ColumnName().String(): 2, h.readDB.UserItemCards.UsedDrawLogID.ColumnName().String(): log.ID, h.readDB.UserItemCards.UsedActivityID.ColumnName().String(): activityID, h.readDB.UserItemCards.UsedIssueID.ColumnName().String(): issueID, h.readDB.UserItemCards.UsedAt.ColumnName().String(): time.Now()}) } else { - fmt.Printf("[道具卡-GetLotteryResult] ❌ 范围检查失败\n") } } else { - fmt.Printf("[道具卡-GetLotteryResult] ❌ 时间检查失败\n") } } else { - fmt.Printf("[道具卡-GetLotteryResult] ❌ 未找到系统道具卡 CardID=%d\n", uic.CardID) } } else { - fmt.Printf("[道具卡-GetLotteryResult] ❌ 未找到用户道具卡 用户ID=%d 道具卡ID=%d\n", userID, icID) } } else { - fmt.Printf("[道具卡-GetLotteryResult] 订单备注中没有道具卡ID\n") } rsp.Result = map[string]any{"reward_id": rid, "reward_name": func() string { if rw != nil { @@ -749,143 +518,6 @@ func (h *handler) GetLotteryResult() core.HandlerFunc { } } -func (h *handler) randomID(prefix string) string { - now := time.Now() - return prefix + now.Format("20060102150405") -} - -func (h *handler) orderModel(userID int64, orderNo string, amount int64, activityID int64, issueID int64, count int64) *model.Orders { - return &model.Orders{UserID: userID, OrderNo: orderNo, SourceType: 2, TotalAmount: amount, DiscountAmount: 0, PointsAmount: 0, ActualAmount: amount, Status: 1, IsConsumed: 0, Remark: fmt.Sprintf("lottery:activity:%d|issue:%d|count:%d", activityID, issueID, count)} -} - -func parseSlotFromRemark(remark string) int64 { - if remark == "" { - return -1 - } - p := 0 - for i := 0; i < len(remark); i++ { - if remark[i] == '|' { - seg := remark[p:i] - if len(seg) > 5 && seg[:5] == "slot:" { - var n int64 - for j := 5; j < len(seg); j++ { - c := seg[j] - if c < '0' || c > '9' { - break - } - n = n*10 + int64(c-'0') - } - return n - } - p = i + 1 - } - } - if p < len(remark) { - seg := remark[p:] - if len(seg) > 5 && seg[:5] == "slot:" { - var n int64 - for j := 5; j < len(seg); j++ { - c := seg[j] - if c < '0' || c > '9' { - break - } - n = n*10 + int64(c-'0') - } - return n - } - } - return -1 -} - -func parseItemCardIDFromRemark(remark string) int64 { - // remark segments separated by '|', find segment starting with "itemcard:" - p := 0 - for i := 0; i < len(remark); i++ { - if remark[i] == '|' { - seg := remark[p:i] - if len(seg) > 9 && seg[:9] == "itemcard:" { - var n int64 - for j := 9; j < len(seg); j++ { - c := seg[j] - if c < '0' || c > '9' { - break - } - n = n*10 + int64(c-'0') - } - if n > 0 { - return n - } - } - p = i + 1 - } - } - return 0 -} - -func (h *handler) joinSigPayload(userID int64, issueID int64, ts int64, nonce int64) string { - return fmt.Sprintf("%d|%d|%d|%d", userID, issueID, ts, nonce) -} - -func buildSlotsRemarkWithScalarCount(slots []int64) string { - s := "" - for i := range slots { - if i > 0 { - s += "," - } - s += fmt.Sprintf("%d:%d", slots[i]-1, 1) - } - return s -} - -func parseSlotsCountsFromRemark(remark string) ([]int64, []int64) { - if remark == "" { - return nil, nil - } - p := 0 - for i := 0; i < len(remark); i++ { - if remark[i] == '|' { - seg := remark[p:i] - if len(seg) > 6 && seg[:6] == "slots:" { - pairs := seg[6:] - idxs := make([]int64, 0) - cnts := make([]int64, 0) - start := 0 - for start <= len(pairs) { - end := start - for end < len(pairs) && pairs[end] != ',' { - end++ - } - if end > start { - a := pairs[start:end] - // a format: num:num - x, y := int64(0), int64(0) - j := 0 - for j < len(a) && a[j] >= '0' && a[j] <= '9' { - x = x*10 + int64(a[j]-'0') - j++ - } - if j < len(a) && a[j] == ':' { - j++ - for j < len(a) && a[j] >= '0' && a[j] <= '9' { - y = y*10 + int64(a[j]-'0') - j++ - } - } - if y > 0 { - idxs = append(idxs, x+1) - cnts = append(cnts, y) - } - } - start = end + 1 - } - return idxs, cnts - } - p = i + 1 - } - } - return nil, nil -} - // validateIchibanSlots 一番赏格位校验 // 功能:校验请求中的格位选择是否有效(数量匹配、范围合法、未被占用) // 参数: @@ -922,379 +554,3 @@ func (h *handler) validateIchibanSlots(ctx core.Context, req *joinLotteryRequest } return nil } - -// applyCouponWithCap 优惠券抵扣(含50%封顶与金额券部分使用) -// 功能:在订单上应用一张用户券,实施总价50%封顶;金额券支持“部分使用”,在 remark 记录明细 -// 参数: -// - ctx:请求上下文 -// - userID:用户ID -// - order:待更新的订单对象(入参引用,被本函数更新 discount_amount/actual_amount/remark) -// - activityID:活动ID用于范围校验 -// - userCouponID:用户持券ID -// -// 返回:本次实际应用的抵扣金额(分);若不适用或受封顶为0则返回0 -func (h *handler) applyCouponWithCap(ctx core.Context, userID int64, order *model.Orders, activityID int64, userCouponID int64) int64 { - uc, _ := h.readDB.UserCoupons.WithContext(ctx.RequestContext()).Where(h.readDB.UserCoupons.ID.Eq(userCouponID), h.readDB.UserCoupons.UserID.Eq(userID), h.readDB.UserCoupons.Status.Eq(1)).First() - if uc == nil { - return 0 - } - fmt.Printf("[优惠券] 用户券ID=%d 开始=%s 结束=%s\n", userCouponID, uc.ValidStart.Format(time.RFC3339), func() string { - if uc.ValidEnd.IsZero() { - return "无截止" - } - return uc.ValidEnd.Format(time.RFC3339) - }()) - sc, _ := h.readDB.SystemCoupons.WithContext(ctx.RequestContext()).Where(h.readDB.SystemCoupons.ID.Eq(uc.CouponID), h.readDB.SystemCoupons.Status.Eq(1)).First() - now := time.Now() - if sc == nil { - fmt.Printf("[优惠券] 模板不存在 用户券ID=%d\n", userCouponID) - return 0 - } - if uc.ValidStart.After(now) { - fmt.Printf("[优惠券] 未到开始时间 用户券ID=%d 当前=%s\n", userCouponID, now.Format(time.RFC3339)) - return 0 - } - if !uc.ValidEnd.IsZero() && uc.ValidEnd.Before(now) { - fmt.Printf("[优惠券] 已过期 用户券ID=%d 当前=%s\n", userCouponID, now.Format(time.RFC3339)) - return 0 - } - scopeOK := (sc.ScopeType == 1) || (sc.ScopeType == 2 && sc.ActivityID == activityID) - if !scopeOK { - fmt.Printf("[优惠券] 范围不匹配 用户券ID=%d scope_type=%d 模板活动ID=%d 当前活动ID=%d\n", userCouponID, sc.ScopeType, sc.ActivityID, activityID) - return 0 - } - if order.TotalAmount < sc.MinSpend { - fmt.Printf("[优惠券] 未达使用门槛 用户券ID=%d min_spend(分)=%d 订单总额(分)=%d\n", userCouponID, sc.MinSpend, order.TotalAmount) - return 0 - } - cap := order.TotalAmount / 2 - remainingCap := cap - order.DiscountAmount - if remainingCap <= 0 { - fmt.Printf("[优惠券] 已达封顶 封顶(分)=%d 剩余封顶(分)=0\n", cap) - return 0 - } - fmt.Printf("[优惠券] 计算前 类型=%d 面值/折扣=%d 封顶(分)=%d 剩余封顶(分)=%d 当前实付(分)=%d 累计优惠(分)=%d\n", sc.DiscountType, sc.DiscountValue, cap, remainingCap, order.ActualAmount, order.DiscountAmount) - applied := int64(0) - switch sc.DiscountType { - case 1: - var bal int64 - _ = h.repo.GetDbR().Raw("SELECT COALESCE(balance_amount,0) FROM user_coupons WHERE id=?", userCouponID).Scan(&bal).Error - if bal <= 0 { - bal = sc.DiscountValue - } - fmt.Printf("[优惠券] 金额券余额(分)=%d 模板面值(分)=%d\n", bal, sc.DiscountValue) - if bal > 0 { - if bal > remainingCap { - applied = remainingCap - } else { - applied = bal - } - } - case 2: - applied = sc.DiscountValue - if applied > remainingCap { - applied = remainingCap - } - fmt.Printf("[优惠券] 满减券 应用金额(分)=%d\n", applied) - case 3: - rate := sc.DiscountValue - if rate < 0 { - rate = 0 - } - if rate > 1000 { - rate = 1000 - } - newAmt := order.ActualAmount * rate / 1000 - d := order.ActualAmount - newAmt - if d > remainingCap { - applied = remainingCap - } else { - applied = d - } - fmt.Printf("[优惠券] 折扣券 折扣千分比=%d 抵扣(分)=%d\n", rate, applied) - } - if applied > order.ActualAmount { - applied = order.ActualAmount - } - fmt.Printf("[优惠券] 本次抵扣(分)=%d\n", applied) - if applied <= 0 { - return 0 - } - order.DiscountAmount += applied - order.ActualAmount -= applied - order.Remark = order.Remark + fmt.Sprintf("|c:%d:%d", userCouponID, applied) - fmt.Printf("[优惠券] 应用后 累计优惠(分)=%d 订单实付(分)=%d\n", order.DiscountAmount, order.ActualAmount) - return applied -} - -// updateUserCouponAfterApply 应用后更新用户券(扣减余额或核销) -// 功能:根据订单 remark 中记录的 applied_amount, -// -// 对直金额券扣减余额并在余额为0时核销;满减/折扣券一次性核销 -// -// 参数: -// - ctx:请求上下文 -// - userID:用户ID -// - order:订单(用于读取 remark 和写入 used_order_id) -// - userCouponID:用户持券ID -// -// 返回:无 -func (h *handler) updateUserCouponAfterApply(ctx core.Context, userID int64, order *model.Orders, userCouponID int64) { - uc, _ := h.readDB.UserCoupons.WithContext(ctx.RequestContext()).Where(h.readDB.UserCoupons.ID.Eq(userCouponID), h.readDB.UserCoupons.UserID.Eq(userID), h.readDB.UserCoupons.Status.Eq(1)).First() - if uc == nil { - return - } - sc, _ := h.readDB.SystemCoupons.WithContext(ctx.RequestContext()).Where(h.readDB.SystemCoupons.ID.Eq(uc.CouponID), h.readDB.SystemCoupons.Status.Eq(1)).First() - if sc == nil { - return - } - applied := int64(0) - remark := order.Remark - p := 0 - for i := 0; i < len(remark); i++ { - if remark[i] == '|' { - seg := remark[p:i] - if len(seg) > 2 && seg[:2] == "c:" { - j := 2 - var id int64 - for j < len(seg) && seg[j] >= '0' && seg[j] <= '9' { - id = id*10 + int64(seg[j]-'0') - j++ - } - if j < len(seg) && seg[j] == ':' { - j++ - var amt int64 - for j < len(seg) && seg[j] >= '0' && seg[j] <= '9' { - amt = amt*10 + int64(seg[j]-'0') - j++ - } - if id == userCouponID { - applied = amt - } - } - } - p = i + 1 - } - } - if sc.DiscountType == 1 { - var bal int64 - _ = h.repo.GetDbR().Raw("SELECT COALESCE(balance_amount,0) FROM user_coupons WHERE id=?", userCouponID).Scan(&bal).Error - newBal := bal - applied - if newBal < 0 { - newBal = 0 - } - if newBal == 0 { - _, _ = h.writeDB.UserCoupons.WithContext(ctx.RequestContext()).Where(h.writeDB.UserCoupons.ID.Eq(userCouponID), h.writeDB.UserCoupons.UserID.Eq(userID)).Updates(map[string]any{"balance_amount": newBal, h.writeDB.UserCoupons.Status.ColumnName().String(): 2, h.writeDB.UserCoupons.UsedOrderID.ColumnName().String(): order.ID, h.writeDB.UserCoupons.UsedAt.ColumnName().String(): time.Now()}) - } else { - _, _ = h.writeDB.UserCoupons.WithContext(ctx.RequestContext()).Where(h.writeDB.UserCoupons.ID.Eq(userCouponID), h.writeDB.UserCoupons.UserID.Eq(userID)).Updates(map[string]any{"balance_amount": newBal, h.writeDB.UserCoupons.UsedOrderID.ColumnName().String(): order.ID, h.writeDB.UserCoupons.UsedAt.ColumnName().String(): time.Now()}) - } - } else { - _, _ = h.writeDB.UserCoupons.WithContext(ctx.RequestContext()).Where(h.writeDB.UserCoupons.ID.Eq(userCouponID), h.writeDB.UserCoupons.UserID.Eq(userID)).Updates(map[string]any{h.writeDB.UserCoupons.Status.ColumnName().String(): 2, h.writeDB.UserCoupons.UsedOrderID.ColumnName().String(): order.ID, h.writeDB.UserCoupons.UsedAt.ColumnName().String(): time.Now()}) - } -} - -// markOrderPaid 将订单标记为已支付 -// 功能:用于0元订单直接置为已支付并写入支付时间 -// 参数: -// - ctx:请求上下文 -// - orderNo:订单号 -// -// 返回:无 -func (h *handler) markOrderPaid(ctx core.Context, orderNo string) { - now := time.Now() - _, _ = h.writeDB.Orders.WithContext(ctx.RequestContext()).Where(h.writeDB.Orders.OrderNo.Eq(orderNo)).Updates(map[string]any{h.writeDB.Orders.Status.ColumnName().String(): 2, h.writeDB.Orders.PaidAt.ColumnName().String(): now}) -} - -// processInstantDraw 即时抽奖流程 -// 功能:在订单已支付情况下,执行抽奖与发奖;支持一番赏固定格位与普通模式,处理道具卡效果 -// 参数: -// - ctx:请求上下文 -// - userID:用户ID -// - activity:活动实体(用于判断玩法) -// - activityID:活动ID -// - issueID:期ID -// - orderNo:订单号 -// - itemCardID:道具卡ID(可选) -// -// 返回:无 -func (h *handler) processInstantDraw(ctx core.Context, userID int64, activity *model.Activities, activityID int64, issueID int64, orderNo string, itemCardID *int64) { - ord, _ := h.readDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.OrderNo.Eq(orderNo)).First() - if ord == nil { - return - } - slotsIdx, slotsCnt := parseSlotsCountsFromRemark(ord.Remark) - dc := func() int64 { - if len(slotsIdx) > 0 && len(slotsIdx) == len(slotsCnt) { - var s int64 - for i := range slotsCnt { - if slotsCnt[i] > 0 { - s += slotsCnt[i] - } - } - if s > 0 { - return s - } - } - remark := ord.Remark - p := 0 - for i := 0; i < len(remark); i++ { - if remark[i] == '|' { - seg := remark[p:i] - if len(seg) > 6 && seg[:6] == "count:" { - var n int64 - for j := 6; j < len(seg); j++ { - c := seg[j] - if c < '0' || c > '9' { - break - } - n = n*10 + int64(c-'0') - } - if n <= 0 { - return 1 - } - return n - } - p = i + 1 - } - } - return 1 - }() - sel := strat.NewDefault(h.readDB, h.writeDB) - logs, _ := h.readDB.ActivityDrawLogs.WithContext(ctx.RequestContext()).Where(h.readDB.ActivityDrawLogs.OrderID.Eq(ord.ID)).Find() - done := int64(len(logs)) - rem := make([]int64, len(slotsCnt)) - copy(rem, slotsCnt) - cur := 0 - for i := done; i < dc; i++ { - rid := int64(0) - var e2 error - if activity.PlayType == "ichiban" { - // ... (inside loop) - var proof map[string]any - if activity.PlayType == "ichiban" { - slot := func() int64 { - // ... (existing slot parsing logic) - if len(slotsIdx) > 0 && len(slotsIdx) == len(rem) { - for cur < len(rem) && rem[cur] == 0 { - cur++ - } - if cur >= len(rem) { - return -1 - } - rem[cur]-- - return slotsIdx[cur] - 1 - } - return parseSlotFromRemark(ord.Remark) - }() - if slot >= 0 { - var cnt int64 - _ = h.repo.GetDbR().Raw("SELECT COUNT(*) FROM issue_position_claims WHERE issue_id=? AND slot_index=?", issueID, slot).Scan(&cnt).Error - if cnt > 0 { - break - } - e := h.repo.GetDbW().Exec("INSERT INTO issue_position_claims (issue_id, slot_index, user_id, order_id, created_at) VALUES (?,?,?,?,NOW(3))", issueID, slot, userID, ord.ID).Error - if e != nil { - break - } - rid, proof, e2 = strat.NewIchiban(h.readDB, h.writeDB).SelectItemBySlot(ctx.RequestContext(), activityID, issueID, slot) - } else { - rid, proof, e2 = sel.SelectItem(ctx.RequestContext(), activityID, issueID, userID) - } - } else { - rid, proof, e2 = sel.SelectItem(ctx.RequestContext(), activityID, issueID, userID) - } - if e2 != nil || rid <= 0 { - break - } - rw, _ := h.readDB.ActivityRewardSettings.WithContext(ctx.RequestContext()).Where(h.readDB.ActivityRewardSettings.ID.Eq(rid)).First() - if rw == nil { - break - } - if activity.PlayType == "ichiban" { - _ = strat.NewIchiban(h.readDB, h.writeDB).GrantReward(ctx.RequestContext(), userID, rid) - } else { - _, _ = h.user.GrantRewardToOrder(ctx.RequestContext(), userID, usersvc.GrantRewardToOrderRequest{OrderID: ord.ID, ProductID: rw.ProductID, Quantity: 1, ActivityID: &activityID, RewardID: &rid, Remark: rw.Name}) - } - log := &model.ActivityDrawLogs{UserID: userID, IssueID: issueID, OrderID: ord.ID, RewardID: rid, IsWinner: 1, Level: rw.Level, CurrentLevel: 1} - _ = h.writeDB.ActivityDrawLogs.WithContext(ctx.RequestContext()).Create(log) - _ = strat.SaveDrawReceipt(ctx.RequestContext(), h.writeDB, log.ID, issueID, userID, proof) - fmt.Printf("[道具卡-processInstantDraw] 开始检查 活动允许道具卡=%t itemCardID=%v\n", activity.AllowItemCards, itemCardID) - if activity.AllowItemCards && itemCardID != nil && *itemCardID > 0 { - fmt.Printf("[道具卡-processInstantDraw] 查询用户道具卡 用户ID=%d 道具卡ID=%d\n", userID, *itemCardID) - uic, _ := h.readDB.UserItemCards.WithContext(ctx.RequestContext()).Where(h.readDB.UserItemCards.ID.Eq(*itemCardID), h.readDB.UserItemCards.UserID.Eq(userID), h.readDB.UserItemCards.Status.Eq(1)).First() - if uic != nil { - fmt.Printf("[道具卡-processInstantDraw] 找到用户道具卡 ID=%d CardID=%d Status=%d ValidStart=%s ValidEnd=%s\n", uic.ID, uic.CardID, uic.Status, uic.ValidStart.Format(time.RFC3339), uic.ValidEnd.Format(time.RFC3339)) - ic, _ := h.readDB.SystemItemCards.WithContext(ctx.RequestContext()).Where(h.readDB.SystemItemCards.ID.Eq(uic.CardID), h.readDB.SystemItemCards.Status.Eq(1)).First() - now := time.Now() - if ic != nil { - fmt.Printf("[道具卡-processInstantDraw] 找到系统道具卡 ID=%d Name=%s EffectType=%d RewardMultiplierX1000=%d ScopeType=%d ActivityID=%d IssueID=%d Status=%d\n", ic.ID, ic.Name, ic.EffectType, ic.RewardMultiplierX1000, ic.ScopeType, ic.ActivityID, ic.IssueID, ic.Status) - fmt.Printf("[道具卡-processInstantDraw] 时间检查 当前时间=%s ValidStart.After(now)=%t ValidEnd.Before(now)=%t\n", now.Format(time.RFC3339), uic.ValidStart.After(now), uic.ValidEnd.Before(now)) - if !uic.ValidStart.After(now) && !uic.ValidEnd.Before(now) { - scopeOK := (ic.ScopeType == 1) || (ic.ScopeType == 3 && ic.ActivityID == activityID) || (ic.ScopeType == 4 && ic.IssueID == issueID) - fmt.Printf("[道具卡-processInstantDraw] 范围检查 ScopeType=%d ActivityID=%d IssueID=%d scopeOK=%t\n", ic.ScopeType, activityID, issueID, scopeOK) - if scopeOK { - if ic.EffectType == 1 && ic.RewardMultiplierX1000 >= 2000 { - fmt.Printf("[道具卡-processInstantDraw] ✅ 应用双倍奖励 倍数=%d 奖品ID=%d 奖品名=%s PlayType=%s\n", ic.RewardMultiplierX1000, rid, rw.Name, activity.PlayType) - if activity.PlayType == "ichiban" { - _ = strat.NewIchiban(h.readDB, h.writeDB).GrantReward(ctx.RequestContext(), userID, rid) - } else { - _, _ = h.user.GrantRewardToOrder(ctx.RequestContext(), userID, usersvc.GrantRewardToOrderRequest{OrderID: ord.ID, ProductID: rw.ProductID, Quantity: 1, ActivityID: &activityID, RewardID: &rid, Remark: rw.Name + "(倍数)"}) - } - fmt.Printf("[道具卡-processInstantDraw] ✅ 双倍奖励发放完成\n") - } else if ic.EffectType == 2 && ic.BoostRateX1000 > 0 { - fmt.Printf("[道具卡-processInstantDraw] 应用概率提升 BoostRateX1000=%d\n", ic.BoostRateX1000) - uprw, _ := h.readDB.ActivityRewardSettings.WithContext(ctx.RequestContext()).Where(h.readDB.ActivityRewardSettings.IssueID.Eq(issueID)).Find() - var better *model.ActivityRewardSettings - for _, r := range uprw { - if r.Level < rw.Level && r.Quantity != 0 { - if better == nil || r.Level < better.Level { - better = r - } - } - } - if better != nil && rand.Int31n(1000) < ic.BoostRateX1000 { - rid2 := better.ID - fmt.Printf("[道具卡-processInstantDraw] ✅ 概率提升成功 升级到奖品ID=%d 奖品名=%s\n", rid2, better.Name) - if activity.PlayType == "ichiban" { - _ = strat.NewIchiban(h.readDB, h.writeDB).GrantReward(ctx.RequestContext(), userID, rid2) - } else { - _, _ = h.user.GrantRewardToOrder(ctx.RequestContext(), userID, usersvc.GrantRewardToOrderRequest{OrderID: ord.ID, ProductID: better.ProductID, Quantity: 1, ActivityID: &activityID, RewardID: &rid2, Remark: better.Name + "(升级)"}) - } - // 创建升级后的抽奖日志并保存凭据 - drawLog2 := &model.ActivityDrawLogs{UserID: userID, IssueID: issueID, OrderID: ord.ID, RewardID: rid2, IsWinner: 1, Level: better.Level, CurrentLevel: 1} - if err := h.writeDB.ActivityDrawLogs.WithContext(ctx.RequestContext()).Create(drawLog2); err != nil { - fmt.Printf("[道具卡-processInstantDraw] ❌ 创建升级抽奖日志失败 err=%v\n", err) - } else { - if err := strat.SaveDrawReceipt(ctx.RequestContext(), h.writeDB, drawLog2.ID, issueID, userID, proof); err != nil { - fmt.Printf("[道具卡-processInstantDraw] ⚠️ 保存升级凭据失败 DrawLogID=%d IssueID=%d UserID=%d err=%v\n", drawLog2.ID, issueID, userID, err) - } else { - fmt.Printf("[道具卡-processInstantDraw] ✅ 保存升级凭据成功 DrawLogID=%d IssueID=%d\n", drawLog2.ID, issueID) - } - } - } else { - fmt.Printf("[道具卡-processInstantDraw] 概率提升未触发\n") - } - } else { - fmt.Printf("[道具卡-processInstantDraw] ⚠️ 道具卡效果类型不匹配或倍数不足 EffectType=%d RewardMultiplierX1000=%d\n", ic.EffectType, ic.RewardMultiplierX1000) - } - fmt.Printf("[道具卡-processInstantDraw] 核销道具卡 用户道具卡ID=%d\n", *itemCardID) - _, _ = h.writeDB.UserItemCards.WithContext(ctx.RequestContext()).Where(h.readDB.UserItemCards.ID.Eq(*itemCardID), h.readDB.UserItemCards.UserID.Eq(userID), h.readDB.UserItemCards.Status.Eq(1)).Updates(map[string]any{h.readDB.UserItemCards.Status.ColumnName().String(): 2, h.readDB.UserItemCards.UsedDrawLogID.ColumnName().String(): log.ID, h.readDB.UserItemCards.UsedActivityID.ColumnName().String(): activityID, h.readDB.UserItemCards.UsedIssueID.ColumnName().String(): issueID, h.readDB.UserItemCards.UsedAt.ColumnName().String(): time.Now()}) - } else { - fmt.Printf("[道具卡-processInstantDraw] ❌ 范围检查失败\n") - } - } else { - fmt.Printf("[道具卡-processInstantDraw] ❌ 时间检查失败\n") - } - } else { - fmt.Printf("[道具卡-processInstantDraw] ❌ 未找到系统道具卡 CardID=%d\n", uic.CardID) - } - } else { - fmt.Printf("[道具卡-processInstantDraw] ❌ 未找到用户道具卡 用户ID=%d 道具卡ID=%d\n", userID, *itemCardID) - } - } else { - fmt.Printf("[道具卡-processInstantDraw] 跳过道具卡检查\n") - } - } - } - -} diff --git a/internal/api/activity/lottery_helper.go b/internal/api/activity/lottery_helper.go new file mode 100644 index 0000000..5be6eac --- /dev/null +++ b/internal/api/activity/lottery_helper.go @@ -0,0 +1,387 @@ +package app + +import ( + "bindbox-game/internal/pkg/core" + "bindbox-game/internal/repository/mysql/model" + "fmt" + "time" +) + +// applyCouponWithCap 优惠券抵扣(含50%封顶与金额券部分使用) +// 功能:在订单上应用一张用户券,实施总价50%封顶;金额券支持“部分使用”,在 remark 记录明细 +// 参数: +// - ctx:请求上下文 +// - userID:用户ID +// - order:待更新的订单对象(入参引用,被本函数更新 discount_amount/actual_amount/remark) +// - activityID:活动ID用于范围校验 +// - userCouponID:用户持券ID +// +// 返回:本次实际应用的抵扣金额(分);若不适用或受封顶为0则返回0 +func (h *handler) applyCouponWithCap(ctx core.Context, userID int64, order *model.Orders, activityID int64, userCouponID int64) int64 { + uc, _ := h.readDB.UserCoupons.WithContext(ctx.RequestContext()).Where(h.readDB.UserCoupons.ID.Eq(userCouponID), h.readDB.UserCoupons.UserID.Eq(userID), h.readDB.UserCoupons.Status.Eq(1)).First() + if uc == nil { + return 0 + } + sc, _ := h.readDB.SystemCoupons.WithContext(ctx.RequestContext()).Where(h.readDB.SystemCoupons.ID.Eq(uc.CouponID), h.readDB.SystemCoupons.Status.Eq(1)).First() + now := time.Now() + if sc == nil { + return 0 + } + if uc.ValidStart.After(now) { + return 0 + } + if !uc.ValidEnd.IsZero() && uc.ValidEnd.Before(now) { + return 0 + } + scopeOK := (sc.ScopeType == 1) || (sc.ScopeType == 2 && sc.ActivityID == activityID) + if !scopeOK { + return 0 + } + if order.TotalAmount < sc.MinSpend { + return 0 + } + cap := order.TotalAmount / 2 + remainingCap := cap - order.DiscountAmount + if remainingCap <= 0 { + return 0 + } + applied := int64(0) + switch sc.DiscountType { + case 1: + var bal int64 + _ = h.repo.GetDbR().Raw("SELECT COALESCE(balance_amount,0) FROM user_coupons WHERE id=?", userCouponID).Scan(&bal).Error + if bal <= 0 { + bal = sc.DiscountValue + } + if bal > 0 { + if bal > remainingCap { + applied = remainingCap + } else { + applied = bal + } + } + case 2: + applied = sc.DiscountValue + if applied > remainingCap { + applied = remainingCap + } + case 3: + rate := sc.DiscountValue + if rate < 0 { + rate = 0 + } + if rate > 1000 { + rate = 1000 + } + newAmt := order.ActualAmount * rate / 1000 + d := order.ActualAmount - newAmt + if d > remainingCap { + applied = remainingCap + } else { + applied = d + } + } + if applied > order.ActualAmount { + applied = order.ActualAmount + } + if applied <= 0 { + return 0 + } + order.DiscountAmount += applied + order.ActualAmount -= applied + order.Remark = order.Remark + fmt.Sprintf("|c:%d:%d", userCouponID, applied) + return applied +} + +// updateUserCouponAfterApply 应用后更新用户券(扣减余额或核销) +// 功能:根据订单 remark 中记录的 applied_amount, +// +// 对直金额券扣减余额并在余额为0时核销;满减/折扣券一次性核销 +// +// 参数: +// - ctx:请求上下文 +// - userID:用户ID +// - order:订单(用于读取 remark 和写入 used_order_id) +// - userCouponID:用户持券ID +// +// 返回:无 +func (h *handler) updateUserCouponAfterApply(ctx core.Context, userID int64, order *model.Orders, userCouponID int64) { + uc, _ := h.readDB.UserCoupons.WithContext(ctx.RequestContext()).Where(h.readDB.UserCoupons.ID.Eq(userCouponID), h.readDB.UserCoupons.UserID.Eq(userID), h.readDB.UserCoupons.Status.Eq(1)).First() + if uc == nil { + return + } + sc, _ := h.readDB.SystemCoupons.WithContext(ctx.RequestContext()).Where(h.readDB.SystemCoupons.ID.Eq(uc.CouponID), h.readDB.SystemCoupons.Status.Eq(1)).First() + if sc == nil { + return + } + applied := int64(0) + remark := order.Remark + p := 0 + for i := 0; i < len(remark); i++ { + if remark[i] == '|' { + seg := remark[p:i] + if len(seg) > 2 && seg[:2] == "c:" { + j := 2 + var id int64 + for j < len(seg) && seg[j] >= '0' && seg[j] <= '9' { + id = id*10 + int64(seg[j]-'0') + j++ + } + if j < len(seg) && seg[j] == ':' { + j++ + var amt int64 + for j < len(seg) && seg[j] >= '0' && seg[j] <= '9' { + amt = amt*10 + int64(seg[j]-'0') + j++ + } + if id == userCouponID { + applied = amt + } + } + } + p = i + 1 + } + } + if sc.DiscountType == 1 { + var bal int64 + _ = h.repo.GetDbR().Raw("SELECT COALESCE(balance_amount,0) FROM user_coupons WHERE id=?", userCouponID).Scan(&bal).Error + newBal := bal - applied + if newBal < 0 { + newBal = 0 + } + if newBal == 0 { + _, _ = h.writeDB.UserCoupons.WithContext(ctx.RequestContext()).Where(h.writeDB.UserCoupons.ID.Eq(userCouponID), h.writeDB.UserCoupons.UserID.Eq(userID)).Updates(map[string]any{"balance_amount": newBal, h.writeDB.UserCoupons.Status.ColumnName().String(): 2, h.writeDB.UserCoupons.UsedOrderID.ColumnName().String(): order.ID, h.writeDB.UserCoupons.UsedAt.ColumnName().String(): time.Now()}) + } else { + _, _ = h.writeDB.UserCoupons.WithContext(ctx.RequestContext()).Where(h.writeDB.UserCoupons.ID.Eq(userCouponID), h.writeDB.UserCoupons.UserID.Eq(userID)).Updates(map[string]any{"balance_amount": newBal, h.writeDB.UserCoupons.UsedOrderID.ColumnName().String(): order.ID, h.writeDB.UserCoupons.UsedAt.ColumnName().String(): time.Now()}) + } + } else { + _, _ = h.writeDB.UserCoupons.WithContext(ctx.RequestContext()).Where(h.writeDB.UserCoupons.ID.Eq(userCouponID), h.writeDB.UserCoupons.UserID.Eq(userID)).Updates(map[string]any{h.writeDB.UserCoupons.Status.ColumnName().String(): 2, h.writeDB.UserCoupons.UsedOrderID.ColumnName().String(): order.ID, h.writeDB.UserCoupons.UsedAt.ColumnName().String(): time.Now()}) + } +} + +// markOrderPaid 将订单标记为已支付 +// 功能:用于0元订单直接置为已支付并写入支付时间 +// 参数: +// - ctx:请求上下文 +// - orderNo:订单号 +// +// 返回:无 +func (h *handler) markOrderPaid(ctx core.Context, orderNo string) { + now := time.Now() + _, _ = h.writeDB.Orders.WithContext(ctx.RequestContext()).Where(h.writeDB.Orders.OrderNo.Eq(orderNo)).Updates(map[string]any{h.writeDB.Orders.Status.ColumnName().String(): 2, h.writeDB.Orders.PaidAt.ColumnName().String(): now}) +} + +func (h *handler) randomID(prefix string) string { + now := time.Now() + return prefix + now.Format("20060102150405") +} + +func (h *handler) orderModel(userID int64, orderNo string, amount int64, activityID int64, issueID int64, count int64) *model.Orders { + return &model.Orders{UserID: userID, OrderNo: orderNo, SourceType: 2, TotalAmount: amount, DiscountAmount: 0, PointsAmount: 0, ActualAmount: amount, Status: 1, IsConsumed: 0, Remark: fmt.Sprintf("lottery:activity:%d|issue:%d|count:%d", activityID, issueID, count)} +} + +func parseSlotFromRemark(remark string) int64 { + if remark == "" { + return -1 + } + p := 0 + for i := 0; i < len(remark); i++ { + if remark[i] == '|' { + seg := remark[p:i] + if len(seg) > 5 && seg[:5] == "slot:" { + var n int64 + for j := 5; j < len(seg); j++ { + c := seg[j] + if c < '0' || c > '9' { + break + } + n = n*10 + int64(c-'0') + } + return n + } + p = i + 1 + } + } + if p < len(remark) { + seg := remark[p:] + if len(seg) > 5 && seg[:5] == "slot:" { + var n int64 + for j := 5; j < len(seg); j++ { + c := seg[j] + if c < '0' || c > '9' { + break + } + n = n*10 + int64(c-'0') + } + return n + } + } + return -1 +} + +func parseItemCardIDFromRemark(remark string) int64 { + // remark segments separated by '|', find segment starting with "itemcard:" + p := 0 + n := len(remark) + for i := 0; i <= n; i++ { + if i == n || remark[i] == '|' { + seg := remark[p:i] + if len(seg) > 9 && seg[:9] == "itemcard:" { + var val int64 + valid := true + for j := 9; j < len(seg); j++ { + c := seg[j] + if c < '0' || c > '9' { + valid = false + break + } + val = val*10 + int64(c-'0') + } + if valid && val > 0 { + return val + } + } + p = i + 1 + } + } + return 0 +} + +func (h *handler) joinSigPayload(userID int64, issueID int64, ts int64, nonce int64) string { + return fmt.Sprintf("%d|%d|%d|%d", userID, issueID, ts, nonce) +} + +func buildSlotsRemarkWithScalarCount(slots []int64) string { + s := "" + for i := range slots { + if i > 0 { + s += "," + } + s += fmt.Sprintf("%d:%d", slots[i]-1, 1) + } + return s +} + +func parseSlotsCountsFromRemark(remark string) ([]int64, []int64) { + if remark == "" { + return nil, nil + } + p := 0 + for i := 0; i < len(remark); i++ { + if remark[i] == '|' { + seg := remark[p:i] + if len(seg) > 6 && seg[:6] == "slots:" { + pairs := seg[6:] + idxs := make([]int64, 0) + cnts := make([]int64, 0) + start := 0 + for start <= len(pairs) { + end := start + for end < len(pairs) && pairs[end] != ',' { + end++ + } + if end > start { + a := pairs[start:end] + // a format: num:num + x, y := int64(0), int64(0) + j := 0 + for j < len(a) && a[j] >= '0' && a[j] <= '9' { + x = x*10 + int64(a[j]-'0') + j++ + } + if j < len(a) && a[j] == ':' { + j++ + for j < len(a) && a[j] >= '0' && a[j] <= '9' { + y = y*10 + int64(a[j]-'0') + j++ + } + } + if y > 0 { + idxs = append(idxs, x+1) + cnts = append(cnts, y) + } + } + start = end + 1 + } + return idxs, cnts + } + p = i + 1 + } + } + return nil, nil +} + +func parseIssueIDFromRemark(remark string) int64 { + if remark == "" { + return 0 + } + // Try "issue:" or "matching_game:issue:" + // Split by | + segs := make([]string, 0) + last := 0 + for i := 0; i < len(remark); i++ { + if remark[i] == '|' { + segs = append(segs, remark[last:i]) + last = i + 1 + } + } + if last < len(remark) { + segs = append(segs, remark[last:]) + } + + for _, seg := range segs { + // handle 'issue:123' + if len(seg) > 6 && seg[:6] == "issue:" { + var n int64 + for j := 6; j < len(seg); j++ { + c := seg[j] + if c < '0' || c > '9' { + break + } + n = n*10 + int64(c-'0') + } + return n + } + // handle 'matching_game:issue:123' + if len(seg) > 20 && seg[:20] == "matching_game:issue:" { + var n int64 + for j := 20; j < len(seg); j++ { + c := seg[j] + if c < '0' || c > '9' { + break + } + n = n*10 + int64(c-'0') + } + return n + } + } + return 0 +} + +func parseCountFromRemark(remark string) int64 { + if remark == "" { + return 1 + } + p := 0 + for i := 0; i < len(remark); i++ { + if remark[i] == '|' { + seg := remark[p:i] + if len(seg) > 6 && seg[:6] == "count:" { + var n int64 + for j := 6; j < len(seg); j++ { + c := seg[j] + if c < '0' || c > '9' { + break + } + n = n*10 + int64(c-'0') + } + if n <= 0 { + return 1 + } + return n + } + p = i + 1 + } + } + return 1 +} diff --git a/internal/api/activity/lottery_result_order_app.go b/internal/api/activity/lottery_result_order_app.go index 43c17f0..c018adc 100644 --- a/internal/api/activity/lottery_result_order_app.go +++ b/internal/api/activity/lottery_result_order_app.go @@ -119,8 +119,6 @@ func (h *handler) LotteryResultByOrder() core.HandlerFunc { }())).First() if act != nil { // 一次补抽 - rid := int64(0) - var e2 error if act.PlayType == "ichiban" { slot := parseSlotFromRemark(ord.Remark) if slot >= 0 { @@ -129,8 +127,8 @@ func (h *handler) LotteryResultByOrder() core.HandlerFunc { if cnt > 0 { st = "slot_unavailable" } else if err := h.repo.GetDbW().Exec("INSERT INTO issue_position_claims (issue_id, slot_index, user_id, order_id, created_at) VALUES (?,?,?,?,NOW(3))", iss, slot, userID, ord.ID).Error; err == nil { - var proof map[string]any - rid, proof, e2 = strat.NewIchiban(h.readDB, h.writeDB).SelectItemBySlot(ctx.RequestContext(), act.ID, iss, slot) + // Daily Seed removed to enforce CommitmentSeedMaster + rid, proof, e2 := strat.NewIchiban(h.readDB, h.writeDB).SelectItemBySlot(ctx.RequestContext(), act.ID, iss, slot) if e2 == nil && rid > 0 { rw, _ := h.readDB.ActivityRewardSettings.WithContext(ctx.RequestContext()).Where(h.readDB.ActivityRewardSettings.ID.Eq(rid)).First() if rw != nil { @@ -146,8 +144,8 @@ func (h *handler) LotteryResultByOrder() core.HandlerFunc { } } else { sel := strat.NewDefault(h.readDB, h.writeDB) - var proof map[string]any - rid, proof, e2 = sel.SelectItem(ctx.RequestContext(), act.ID, iss, userID) + // Daily Seed removed + rid, proof, e2 := sel.SelectItem(ctx.RequestContext(), act.ID, iss, userID) if e2 == nil && rid > 0 { rw, _ := h.readDB.ActivityRewardSettings.WithContext(ctx.RequestContext()).Where(h.readDB.ActivityRewardSettings.ID.Eq(rid)).First() if rw != nil { @@ -257,78 +255,3 @@ func (h *handler) LotteryResultByOrder() core.HandlerFunc { ctx.Payload(rsp) } } - -func parseIssueIDFromRemark(remark string) int64 { - if remark == "" { - return 0 - } - // Try "issue:" or "matching_game:issue:" - // Split by | - segs := make([]string, 0) - last := 0 - for i := 0; i < len(remark); i++ { - if remark[i] == '|' { - segs = append(segs, remark[last:i]) - last = i + 1 - } - } - if last < len(remark) { - segs = append(segs, remark[last:]) - } - - for _, seg := range segs { - // handle 'issue:123' - if len(seg) > 6 && seg[:6] == "issue:" { - var n int64 - for j := 6; j < len(seg); j++ { - c := seg[j] - if c < '0' || c > '9' { - break - } - n = n*10 + int64(c-'0') - } - return n - } - // handle 'matching_game:issue:123' - if len(seg) > 20 && seg[:20] == "matching_game:issue:" { - var n int64 - for j := 20; j < len(seg); j++ { - c := seg[j] - if c < '0' || c > '9' { - break - } - n = n*10 + int64(c-'0') - } - return n - } - } - return 0 -} - -func parseCountFromRemark(remark string) int64 { - if remark == "" { - return 1 - } - p := 0 - for i := 0; i < len(remark); i++ { - if remark[i] == '|' { - seg := remark[p:i] - if len(seg) > 6 && seg[:6] == "count:" { - var n int64 - for j := 6; j < len(seg); j++ { - c := seg[j] - if c < '0' || c > '9' { - break - } - n = n*10 + int64(c-'0') - } - if n <= 0 { - return 1 - } - return n - } - p = i + 1 - } - } - return 1 -} diff --git a/internal/api/activity/matching_game_app.go b/internal/api/activity/matching_game_app.go index ddd6f18..9d744b3 100644 --- a/internal/api/activity/matching_game_app.go +++ b/internal/api/activity/matching_game_app.go @@ -1,291 +1,25 @@ package app import ( + "bindbox-game/configs" "bindbox-game/internal/code" "bindbox-game/internal/pkg/core" - "bindbox-game/internal/pkg/logger" "bindbox-game/internal/pkg/validation" - "bindbox-game/internal/repository/mysql/dao" + "bindbox-game/internal/pkg/wechat" "bindbox-game/internal/repository/mysql/model" + activitysvc "bindbox-game/internal/service/activity" "context" - "crypto/hmac" "crypto/rand" - "crypto/sha256" "encoding/binary" "encoding/hex" - "encoding/json" "fmt" "net/http" - "sync" "time" "github.com/redis/go-redis/v9" "go.uber.org/zap" - - activitysvc "bindbox-game/internal/service/activity" - usersvc "bindbox-game/internal/service/user" ) -// CardType 卡牌类型 -type CardType string - -// CardTypeConfig 卡牌类型配置(从数据库加载) -type CardTypeConfig struct { - Code CardType `json:"code"` - Name string `json:"name"` - ImageURL string `json:"image_url"` - Quantity int32 `json:"quantity"` -} - -// MatchingCard 游戏中的卡牌实例 -type MatchingCard struct { - ID string `json:"id"` - Type CardType `json:"type"` -} - -// MatchingGame 对对碰游戏结构 -type MatchingGame struct { - Mu sync.Mutex `json:"-"` // 互斥锁保护并发访问 - ServerSeed []byte `json:"server_seed"` - ServerSeedHash string `json:"server_seed_hash"` - Nonce int64 `json:"nonce"` - CardConfigs []CardTypeConfig `json:"card_configs"` - Deck []*MatchingCard `json:"deck"` // 牌堆 (预生成的卡牌对象) - Board [9]*MatchingCard `json:"board"` // 固定9格棋盘 - CardIDCounter int64 `json:"card_id_counter"` // 用于生成唯一ID - TotalPairs int64 `json:"total_pairs"` - MaxPossiblePairs int64 `json:"max_possible_pairs"` // 最大可能消除对数 (安全校验) - Round int64 `json:"round"` - RoundHistory []MatchingRoundResult `json:"round_history"` - LastActivity time.Time `json:"last_activity"` - - // Context info for reward granting - ActivityID int64 `json:"activity_id"` - IssueID int64 `json:"issue_id"` - OrderID int64 `json:"order_id"` - UserID int64 `json:"user_id"` -} - -type MatchingRoundResult struct { - Round int64 `json:"round"` - Board [9]*MatchingCard `json:"board"` - Pairs []MatchingPair `json:"pairs"` - PairsCount int64 `json:"pairs_count"` - DrawnCards []DrawnCardInfo `json:"drawn_cards"` // 优化:包含位置信息 - Reshuffled bool `json:"reshuffled"` - CanContinue bool `json:"can_continue"` -} - -type DrawnCardInfo struct { - SlotIndex int `json:"slot_index"` - Card MatchingCard `json:"card"` -} - -type MatchingPair struct { - CardType CardType `json:"card_type"` - Count int64 `json:"count"` - CardIDs []string `json:"card_ids"` - SlotIndices []int `json:"slot_indices"` // 新增:消除的格子索引 -} - -// loadCardTypesFromDB 从数据库加载启用的卡牌类型配置 -func loadCardTypesFromDB(ctx context.Context, readDB *dao.Query) ([]CardTypeConfig, error) { - items, err := readDB.MatchingCardTypes.WithContext(ctx).Where(readDB.MatchingCardTypes.Status.Eq(1)).Order(readDB.MatchingCardTypes.Sort.Asc()).Find() - if err != nil { - return nil, err - } - configs := make([]CardTypeConfig, len(items)) - for i, item := range items { - configs[i] = CardTypeConfig{ - Code: CardType(item.Code), - Name: item.Name, - ImageURL: item.ImageURL, - Quantity: item.Quantity, - } - } - return configs, nil -} - -// NewMatchingGameWithConfig 使用数据库配置创建游戏 -// NewMatchingGameWithConfig 使用数据库配置创建游戏 -// position: 用户选择的位置(可选),用于增加随机熵值 -func NewMatchingGameWithConfig(configs []CardTypeConfig, position string) *MatchingGame { - g := &MatchingGame{ - CardConfigs: configs, - RoundHistory: []MatchingRoundResult{}, - Board: [9]*MatchingCard{}, - LastActivity: time.Now(), - } - - // 生成服务器种子 - g.ServerSeed = make([]byte, 32) - rand.Read(g.ServerSeed) - - // 如果有 position 参数,将其混入种子逻辑 - if position != "" { - // 使用 SHA256 (seed + position + timestamp) 生成新的混合种子 - h := sha256.New() - h.Write(g.ServerSeed) - h.Write([]byte(position)) - // 还可以加个时间戳确保不仅仅依赖 position - h.Write([]byte(fmt.Sprintf("%d", time.Now().UnixNano()))) - g.ServerSeed = h.Sum(nil) - } - - hash := sha256.Sum256(g.ServerSeed) - g.ServerSeedHash = fmt.Sprintf("%x", hash) - - // 根据配置生成所有卡牌 (99张) - totalCards := 0 - for _, cfg := range configs { - totalCards += int(cfg.Quantity) - } - // 创建所有卡牌对象 - g.CardIDCounter = 0 - allCards := make([]*MatchingCard, 0, totalCards) - for _, cfg := range configs { - for i := int32(0); i < cfg.Quantity; i++ { - // 创建卡牌对象 - g.CardIDCounter++ - id := fmt.Sprintf("c%d", g.CardIDCounter) - mc := &MatchingCard{ - ID: id, - Type: cfg.Code, - } - allCards = append(allCards, mc) - } - } - g.Deck = allCards - - // 安全洗牌 - g.secureShuffle() - - // 初始填充棋盘 - for i := 0; i < 9; i++ { - if len(g.Deck) > 0 { - // 从牌堆顶取一张 - card := g.Deck[0] - g.Deck = g.Deck[1:] - g.Board[i] = card - } else { - g.Board[i] = nil - } - } - - // 计算理论最大对数 (Sanity Check) - // 遍历所有生成的卡牌配置 - var theoreticalMax int64 - for _, cfg := range configs { - theoreticalMax += int64(cfg.Quantity / 2) // 向下取整,每2张算1对 - } - g.MaxPossiblePairs = theoreticalMax - - return g -} - -// createMatchingCard (已废弃,改为预生成) - 但为了兼容 PlayRound 里可能的动态生成(如有),保留作为 helper? -// 不,PlayRound 现在应该直接从 deck 取对象。 -// 只需要保留 getCardConfig 即可。 - -// getCardConfig 获取指定卡牌类型的配置 -func (g *MatchingGame) getCardConfig(cardType CardType) *CardTypeConfig { - for i := range g.CardConfigs { - if g.CardConfigs[i].Code == cardType { - return &g.CardConfigs[i] - } - } - return nil -} - -// secureShuffle 使用 HMAC-SHA256 的 Fisher-Yates 洗牌 -func (g *MatchingGame) secureShuffle() { - n := len(g.Deck) - for i := n - 1; i > 0; i-- { - j := g.secureRandInt(i+1, fmt.Sprintf("shuffle:%d", i)) - g.Deck[i], g.Deck[j] = g.Deck[j], g.Deck[i] - } -} - -// secureRandInt 使用 HMAC-SHA256 生成安全随机数 -func (g *MatchingGame) secureRandInt(max int, context string) int { - g.Nonce++ - message := fmt.Sprintf("%s|nonce:%d", context, g.Nonce) - mac := hmac.New(sha256.New, g.ServerSeed) - mac.Write([]byte(message)) - sum := mac.Sum(nil) - val := binary.BigEndian.Uint64(sum[:8]) - return int(val % uint64(max)) -} - -// reshuffleBoard 重洗棋盘和牌堆 -func (g *MatchingGame) reshuffleBoard() { - // 1. 回收所有卡牌(板上 + 牌堆) - tempDeck := make([]*MatchingCard, 0, len(g.Deck)+9) - tempDeck = append(tempDeck, g.Deck...) - for i := 0; i < 9; i++ { - if g.Board[i] != nil { - tempDeck = append(tempDeck, g.Board[i]) - g.Board[i] = nil - } - } - - // 2. 循环尝试洗牌,直到开局有解(或者尝试一定次数) - // 尝试最多 10 次,寻找一个起手就有解的局面 - bestDeck := make([]*MatchingCard, len(tempDeck)) - copy(bestDeck, tempDeck) - - for retry := 0; retry < 10; retry++ { - // 复制一份进行尝试 - currentDeck := make([]*MatchingCard, len(tempDeck)) - copy(currentDeck, tempDeck) - g.Deck = currentDeck - g.secureShuffle() - - // 检查前9张(或更少)是否有对子 - checkCount := 9 - if len(g.Deck) < 9 { - checkCount = len(g.Deck) - } - - counts := make(map[CardType]int) - hasPair := false - for k := 0; k < checkCount; k++ { - t := g.Deck[k].Type - counts[t]++ - if counts[t] >= 2 { - hasPair = true - break - } - } - - if hasPair { - // 找到有解的洗牌结果,采用之 - // g.deck 已经是洗好的状态 - break - } - } - - // 3. 重新填满棋盘 - for i := 0; i < 9; i++ { - if len(g.Deck) > 0 { - card := g.Deck[0] - g.Deck = g.Deck[1:] - g.Board[i] = card - } - } -} - -// GetGameState 获取游戏状态 -func (g *MatchingGame) GetGameState() map[string]any { - return map[string]any{ - "board": g.Board, - "deck_count": len(g.Deck), - "total_pairs": g.TotalPairs, - "round": g.Round, - "server_seed_hash": g.ServerSeedHash, - } -} - // ========== API Handlers ========== type matchingGamePreOrderRequest struct { @@ -321,178 +55,6 @@ type matchingGameCheckResponse struct { Reward *MatchingRewardInfo `json:"reward,omitempty"` } -// Redis Key Prefix -const matchingGameKeyPrefix = "bindbox:matching_game:" - -// saveGameToRedis 保存游戏状态到 Redis -func (h *handler) saveGameToRedis(ctx context.Context, gameID string, game *MatchingGame) error { - data, err := json.Marshal(game) - if err != nil { - return err - } - // TTL: 30 minutes - return h.redis.Set(ctx, matchingGameKeyPrefix+gameID, data, 30*time.Minute).Err() -} - -// loadGameFromRedis 从 Redis 加载游戏状态 -// 如果 Redis 中没有找到,则尝试从数据库恢复 -func (h *handler) loadGameFromRedis(ctx context.Context, gameID string) (*MatchingGame, error) { - data, err := h.redis.Get(ctx, matchingGameKeyPrefix+gameID).Bytes() - if err == nil { - var game MatchingGame - if err := json.Unmarshal(data, &game); err != nil { - return nil, err - } - return &game, nil - } - // Redis miss - try to recover from DB - if err == redis.Nil { - game, recoverErr := h.recoverGameFromDB(ctx, gameID) - if recoverErr != nil { - return nil, redis.Nil // Return original error to indicate session not found - } - // Cache the recovered game back to Redis - _ = h.saveGameToRedis(ctx, gameID, game) - return game, nil - } - return nil, err -} - -// recoverGameFromDB 从数据库恢复游戏状态 -// 通过 game_id 解析 user_id,然后查找对应的 activity_draw_receipts 记录 -// 使用 ServerSubSeed 重建游戏状态 -func (h *handler) recoverGameFromDB(ctx context.Context, gameID string) (*MatchingGame, error) { - // Parse user_id from game_id (format: MG{userID}{timestamp}) - // Example: MG121766299471192637903 - if len(gameID) < 3 || gameID[:2] != "MG" { - return nil, fmt.Errorf("invalid game_id format") - } - - // Extract user_id: find the first digit sequence after "MG" - // The user_id is typically short (1-5 digits), timestamp is long (19 digits) - numPart := gameID[2:] - var userID int64 - if len(numPart) > 19 { - // User ID is everything before the last 19 chars (nanosecond timestamp) - userIDStr := numPart[:len(numPart)-19] - userID = parseInt64(userIDStr) - } else { - return nil, fmt.Errorf("cannot parse user_id from game_id") - } - - if userID <= 0 { - return nil, fmt.Errorf("invalid user_id in game_id") - } - - // Find the most recent matching game receipt for this user - receipt, err := h.readDB.ActivityDrawReceipts.WithContext(ctx). - Where(h.readDB.ActivityDrawReceipts.ClientID.Eq(userID)). - Where(h.readDB.ActivityDrawReceipts.AlgoVersion.Eq("HMAC-SHA256-v1")). - Order(h.readDB.ActivityDrawReceipts.ID.Desc()). - First() - if err != nil || receipt == nil { - return nil, fmt.Errorf("no matching game receipt found for user %d", userID) - } - - // Decode ServerSubSeed (hex -> bytes) - serverSeed, err := hex.DecodeString(receipt.ServerSubSeed) - if err != nil || len(serverSeed) == 0 { - return nil, fmt.Errorf("invalid server seed in receipt") - } - - // Get DrawLog to find IssueID and OrderID - drawLog, err := h.readDB.ActivityDrawLogs.WithContext(ctx). - Where(h.readDB.ActivityDrawLogs.ID.Eq(receipt.DrawLogID)). - First() - if err != nil || drawLog == nil { - return nil, fmt.Errorf("draw log not found") - } - - // Load card configs - configs, err := loadCardTypesFromDB(ctx, h.readDB) - if err != nil || len(configs) == 0 { - // Fallback to default configs - configs = []CardTypeConfig{ - {Code: "A", Name: "类型A", Quantity: 9}, - {Code: "B", Name: "类型B", Quantity: 9}, - {Code: "C", Name: "类型C", Quantity: 9}, - {Code: "D", Name: "类型D", Quantity: 9}, - {Code: "E", Name: "类型E", Quantity: 9}, - {Code: "F", Name: "类型F", Quantity: 9}, - {Code: "G", Name: "类型G", Quantity: 9}, - {Code: "H", Name: "类型H", Quantity: 9}, - {Code: "I", Name: "类型I", Quantity: 9}, - {Code: "J", Name: "类型J", Quantity: 9}, - {Code: "K", Name: "类型K", Quantity: 9}, - } - } - - // Reconstruct game with the same seed - game := &MatchingGame{ - CardConfigs: configs, - RoundHistory: []MatchingRoundResult{}, - Board: [9]*MatchingCard{}, - LastActivity: time.Now(), - ServerSeed: serverSeed, - ServerSeedHash: receipt.ServerSeedHash, - Nonce: 0, // Reset nonce for reconstruction - ActivityID: drawLog.IssueID, // Note: IssueID is stored in DrawLog - IssueID: drawLog.IssueID, - OrderID: drawLog.OrderID, - UserID: userID, - } - - // Get ActivityID from Issue - if issue, _ := h.readDB.ActivityIssues.WithContext(ctx).Where(h.readDB.ActivityIssues.ID.Eq(drawLog.IssueID)).First(); issue != nil { - game.ActivityID = issue.ActivityID - } - - // Generate all cards - totalCards := 0 - for _, cfg := range configs { - totalCards += int(cfg.Quantity) - } - game.CardIDCounter = 0 - allCards := make([]*MatchingCard, 0, totalCards) - for _, cfg := range configs { - for i := int32(0); i < cfg.Quantity; i++ { - game.CardIDCounter++ - id := fmt.Sprintf("c%d", game.CardIDCounter) - mc := &MatchingCard{ - ID: id, - Type: cfg.Code, - } - allCards = append(allCards, mc) - } - } - game.Deck = allCards - - // Shuffle with the same seed (deterministic) - game.secureShuffle() - - // Fill board - for i := 0; i < 9; i++ { - if len(game.Deck) > 0 { - card := game.Deck[0] - game.Deck = game.Deck[1:] - game.Board[i] = card - } else { - game.Board[i] = nil - } - } - - // Calculate max possible pairs - var theoreticalMax int64 - for _, cfg := range configs { - theoreticalMax += int64(cfg.Quantity / 2) - } - game.MaxPossiblePairs = theoreticalMax - - fmt.Printf("[会话恢复] 成功从数据库恢复游戏 game_id=%s user_id=%d issue_id=%d\n", gameID, userID, drawLog.IssueID) - - return game, nil -} - // PreOrderMatchingGame 下单并预生成对对碰游戏数据 // @Summary 下单并获取对对碰全量数据 // @Description 用户下单,服务器扣费并返回全量99张乱序卡牌,前端自行负责游戏流程 @@ -588,7 +150,13 @@ func (h *handler) PreOrderMatchingGame() core.HandlerFunc { } // 3. 创建游戏并洗牌 - game := NewMatchingGameWithConfig(configs, req.Position) + // 使用 Activity Commitment 作为随机源 + var serverSeedSrc []byte + if len(activity.CommitmentSeedMaster) > 0 { + serverSeedSrc = activity.CommitmentSeedMaster + } + + game := NewMatchingGameWithConfig(configs, req.Position, serverSeedSrc) game.ActivityID = issue.ActivityID game.IssueID = req.IssueID game.OrderID = order.ID @@ -717,10 +285,13 @@ func (h *handler) CheckMatchingGame() core.HandlerFunc { } // 校验:不能超过理论最大对数 + fmt.Printf("[对对碰Check] 校验对子数量: 客户端提交=%d 服务端计算最大值=%d GameID=%s\n", req.TotalPairs, game.MaxPossiblePairs, req.GameID) if req.TotalPairs > game.MaxPossiblePairs { + fmt.Printf("[对对碰Check] ❌ 校验失败: 提交数量超过理论最大值\n") ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, fmt.Sprintf("invalid total pairs: %d > %d", req.TotalPairs, game.MaxPossiblePairs))) return } + fmt.Printf("[对对碰Check] ✅ 校验通过\n") game.TotalPairs = req.TotalPairs // 记录一下 var rewardInfo *MatchingRewardInfo @@ -742,112 +313,111 @@ func (h *handler) CheckMatchingGame() core.HandlerFunc { } } - // 3. Grant Reward if found if candidate != nil { - if err := h.grantRewardHelper(ctx.RequestContext(), game.UserID, game.OrderID, candidate); err != nil { - h.logger.Error("Failed to grant matching reward", zap.Int64("order_id", game.OrderID), zap.Error(err)) - } else { - rewardInfo = &MatchingRewardInfo{ - RewardID: candidate.ID, - Name: candidate.Name, - Level: candidate.Level, - } + // 3. Prepare Grant Params + // Fetch real product name for remark + productName := candidate.Name + if p, _ := h.readDB.Products.WithContext(ctx.RequestContext()).Where(h.readDB.Products.ID.Eq(candidate.ProductID)).First(); p != nil { + productName = p.Name + } - // 4. Apply Item Card Effects (if any) - ord, _ := h.readDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.ID.Eq(game.OrderID)).First() - if ord != nil { - icID := parseItemCardIDFromRemark(ord.Remark) - fmt.Printf("[道具卡-CheckMatchingGame] 从订单备注解析道具卡ID icID=%d 订单备注=%s\n", icID, ord.Remark) - if icID > 0 { - uic, _ := h.readDB.UserItemCards.WithContext(ctx.RequestContext()).Where( - h.readDB.UserItemCards.ID.Eq(icID), - h.readDB.UserItemCards.UserID.Eq(game.UserID), - h.readDB.UserItemCards.Status.Eq(1), + finalReward := candidate + finalQuantity := 1 + finalRemark := fmt.Sprintf("%s %s", order.OrderNo, productName) + var cardToVoid int64 = 0 + + // 4. Apply Item Card Effects (Determine final reward and quantity) + if order != nil { + icID := parseItemCardIDFromRemark(order.Remark) + if icID > 0 { + uic, _ := h.readDB.UserItemCards.WithContext(ctx.RequestContext()).Where( + h.readDB.UserItemCards.ID.Eq(icID), + h.readDB.UserItemCards.UserID.Eq(game.UserID), + h.readDB.UserItemCards.Status.Eq(1), + ).First() + if uic != nil { + ic, _ := h.readDB.SystemItemCards.WithContext(ctx.RequestContext()).Where( + h.readDB.SystemItemCards.ID.Eq(uic.CardID), + h.readDB.SystemItemCards.Status.Eq(1), ).First() - if uic != nil { - ic, _ := h.readDB.SystemItemCards.WithContext(ctx.RequestContext()).Where( - h.readDB.SystemItemCards.ID.Eq(uic.CardID), - h.readDB.SystemItemCards.Status.Eq(1), - ).First() - now := time.Now() - if ic != nil && !uic.ValidStart.After(now) && !uic.ValidEnd.Before(now) { - scopeOK := (ic.ScopeType == 1) || (ic.ScopeType == 3 && ic.ActivityID == game.ActivityID) || (ic.ScopeType == 4 && ic.IssueID == game.IssueID) - fmt.Printf("[道具卡-CheckMatchingGame] 范围检查 ScopeType=%d ActivityID=%d IssueID=%d scopeOK=%t\n", ic.ScopeType, game.ActivityID, game.IssueID, scopeOK) - if scopeOK { - // Apply effect based on type - if ic.EffectType == 1 && ic.RewardMultiplierX1000 >= 2000 { - // Double reward - fmt.Printf("[道具卡-CheckMatchingGame] ✅ 应用双倍奖励 倍数=%d 奖品ID=%d 奖品名=%s\n", ic.RewardMultiplierX1000, candidate.ID, candidate.Name) - rid := candidate.ID - _, _ = h.user.GrantRewardToOrder(ctx.RequestContext(), game.UserID, usersvc.GrantRewardToOrderRequest{ - OrderID: game.OrderID, - ProductID: candidate.ProductID, - Quantity: 1, - ActivityID: &game.ActivityID, - RewardID: &rid, - Remark: candidate.Name + "(倍数)", - }) - } else if ic.EffectType == 2 && ic.BoostRateX1000 > 0 { - // Probability boost - try to upgrade to better reward - fmt.Printf("[道具卡-CheckMatchingGame] 应用概率提升 BoostRateX1000=%d\n", ic.BoostRateX1000) - allRewards, _ := h.readDB.ActivityRewardSettings.WithContext(ctx.RequestContext()).Where( - h.readDB.ActivityRewardSettings.IssueID.Eq(game.IssueID), - ).Find() - var better *model.ActivityRewardSettings - for _, r := range allRewards { - if r.MinScore > candidate.MinScore && r.Quantity > 0 { - if better == nil || r.MinScore < better.MinScore { - better = r - } - } - } - if better != nil { - // Use crypto/rand for secure random - randBytes := make([]byte, 4) - rand.Read(randBytes) - randVal := int32(binary.BigEndian.Uint32(randBytes) % 1000) - if randVal < ic.BoostRateX1000 { - fmt.Printf("[道具卡-CheckMatchingGame] ✅ 概率提升成功 升级到奖品ID=%d 奖品名=%s\n", better.ID, better.Name) - rid := better.ID - _, _ = h.user.GrantRewardToOrder(ctx.RequestContext(), game.UserID, usersvc.GrantRewardToOrderRequest{ - OrderID: game.OrderID, - ProductID: better.ProductID, - Quantity: 1, - ActivityID: &game.ActivityID, - RewardID: &rid, - Remark: better.Name + "(升级)", - }) + now := time.Now() + if ic != nil && !uic.ValidStart.After(now) && !uic.ValidEnd.Before(now) { + scopeOK := (ic.ScopeType == 1) || (ic.ScopeType == 3 && ic.ActivityID == game.ActivityID) || (ic.ScopeType == 4 && ic.IssueID == game.IssueID) + fmt.Printf("[道具卡-CheckMatchingGame] 范围检查 ScopeType=%d ActivityID=%d IssueID=%d scopeOK=%t\n", ic.ScopeType, game.ActivityID, game.IssueID, scopeOK) + if scopeOK { + cardToVoid = icID + if ic.EffectType == 1 && ic.RewardMultiplierX1000 >= 2000 { + // Double reward + fmt.Printf("[道具卡-CheckMatchingGame] ✅ 应用双倍奖励 倍数=%d\n", ic.RewardMultiplierX1000) + finalQuantity = 2 + finalRemark += "(倍数)" + } else if ic.EffectType == 2 && ic.BoostRateX1000 > 0 { + // Probability boost - try to upgrade to better reward + fmt.Printf("[道具卡-CheckMatchingGame] 应用概率提升 BoostRateX1000=%d\n", ic.BoostRateX1000) + allRewards, _ := h.readDB.ActivityRewardSettings.WithContext(ctx.RequestContext()).Where( + h.readDB.ActivityRewardSettings.IssueID.Eq(game.IssueID), + ).Find() + var better *model.ActivityRewardSettings + for _, r := range allRewards { + if r.MinScore > candidate.MinScore && r.Quantity > 0 { + if better == nil || r.MinScore < better.MinScore { + better = r } } } - - // Void the item card - fmt.Printf("[道具卡-CheckMatchingGame] 核销道具卡 用户道具卡ID=%d\n", icID) - // Get DrawLog ID for the order - drawLog, _ := h.readDB.ActivityDrawLogs.WithContext(ctx.RequestContext()).Where( - h.readDB.ActivityDrawLogs.OrderID.Eq(game.OrderID), - ).First() - var drawLogID int64 - if drawLog != nil { - drawLogID = drawLog.ID + if better != nil { + // Use crypto/rand for secure random + randBytes := make([]byte, 4) + rand.Read(randBytes) + randVal := int32(binary.BigEndian.Uint32(randBytes) % 1000) + if randVal < ic.BoostRateX1000 { + fmt.Printf("[道具卡-CheckMatchingGame] ✅ 概率提升成功 升级到奖品ID=%d 奖品名=%s\n", better.ID, better.Name) + finalReward = better + finalRemark = better.Name + "(升级)" + } } - _, _ = h.writeDB.UserItemCards.WithContext(ctx.RequestContext()).Where( - h.writeDB.UserItemCards.ID.Eq(icID), - h.writeDB.UserItemCards.UserID.Eq(game.UserID), - h.writeDB.UserItemCards.Status.Eq(1), - ).Updates(map[string]any{ - h.writeDB.UserItemCards.Status.ColumnName().String(): 2, - h.writeDB.UserItemCards.UsedDrawLogID.ColumnName().String(): drawLogID, - h.writeDB.UserItemCards.UsedActivityID.ColumnName().String(): game.ActivityID, - h.writeDB.UserItemCards.UsedIssueID.ColumnName().String(): game.IssueID, - h.writeDB.UserItemCards.UsedAt.ColumnName().String(): now, - }) } } } } } } + + // 5. Grant Reward + if err := h.grantRewardHelper(ctx.RequestContext(), game.UserID, game.OrderID, finalReward, finalQuantity, finalRemark); err != nil { + h.logger.Error("Failed to grant matching reward", zap.Int64("order_id", game.OrderID), zap.Error(err)) + } else { + rewardInfo = &MatchingRewardInfo{ + RewardID: finalReward.ID, + Name: productName, + Level: finalReward.Level, + } + + // 6. Void Item Card (if used) + if cardToVoid > 0 { + fmt.Printf("[道具卡-CheckMatchingGame] 核销道具卡 用户道具卡ID=%d\n", cardToVoid) + now := time.Now() + // Get DrawLog ID for the order + drawLog, _ := h.readDB.ActivityDrawLogs.WithContext(ctx.RequestContext()).Where( + h.readDB.ActivityDrawLogs.OrderID.Eq(game.OrderID), + ).First() + var drawLogID int64 + if drawLog != nil { + drawLogID = drawLog.ID + } + _, _ = h.writeDB.UserItemCards.WithContext(ctx.RequestContext()).Where( + h.writeDB.UserItemCards.ID.Eq(cardToVoid), + h.writeDB.UserItemCards.UserID.Eq(game.UserID), + h.writeDB.UserItemCards.Status.Eq(1), + ).Updates(map[string]any{ + h.writeDB.UserItemCards.Status.ColumnName().String(): 2, + h.writeDB.UserItemCards.UsedDrawLogID.ColumnName().String(): drawLogID, + h.writeDB.UserItemCards.UsedActivityID.ColumnName().String(): game.ActivityID, + h.writeDB.UserItemCards.UsedIssueID.ColumnName().String(): game.IssueID, + h.writeDB.UserItemCards.UsedAt.ColumnName().String(): now, + }) + } + } } } @@ -858,6 +428,44 @@ func (h *handler) CheckMatchingGame() core.HandlerFunc { Reward: rewardInfo, } + // 7. Virtual Shipping (Async) + // Upload shipping info to WeChat (similar to Ichiban Kuji) so user can see "Shipped" status and reward info. + rewardName := "无奖励" + if rewardInfo != nil { + rewardName = rewardInfo.Name + } + + go func(orderID int64, orderNo string, userID int64, rName string) { + bgCtx := context.Background() + // 1. Get Payment Transaction + tx, _ := h.readDB.PaymentTransactions.WithContext(bgCtx).Where(h.readDB.PaymentTransactions.OrderNo.Eq(orderNo)).First() + if tx == nil || tx.TransactionID == "" { + h.logger.Warn("CheckMatchingGame: No payment transaction found for shipping", zap.String("order_no", orderNo)) + return + } + + // 2. Get User OpenID + u, _ := h.readDB.Users.WithContext(bgCtx).Where(h.readDB.Users.ID.Eq(userID)).First() + payerOpenid := "" + if u != nil { + payerOpenid = u.Openid + } + + // 3. Construct Item Desc + itemsDesc := fmt.Sprintf("对对碰 %s 赏品: %s", orderNo, rName) + if len(itemsDesc) > 120 { + itemsDesc = itemsDesc[:120] + } + + // 4. Upload + c := configs.Get() + if err := wechat.UploadVirtualShippingForBackground(bgCtx, &wechat.WechatConfig{AppID: c.Wechat.AppID, AppSecret: c.Wechat.AppSecret}, tx.TransactionID, orderNo, payerOpenid, itemsDesc); err != nil { + h.logger.Error("CheckMatchingGame: Failed to upload virtual shipping", zap.Error(err)) + } else { + h.logger.Info("CheckMatchingGame: Virtual shipping uploaded", zap.String("order_no", orderNo), zap.String("items", itemsDesc)) + } + }(game.OrderID, order.OrderNo, game.UserID, rewardName) + // 结算完成,清理会话 (Delete from Redis) _ = h.redis.Del(ctx.RequestContext(), matchingGameKeyPrefix+req.GameID) @@ -922,81 +530,3 @@ func (h *handler) ListMatchingCardTypes() core.HandlerFunc { ctx.Payload(configs) } } - -// startMatchingGameCleanup ... (Deprecated since we use Redis TTL) -func (h *handler) startMatchingGameCleanup() { - // No-op -} - -// cleanupExpiredMatchingGames ... (Deprecated) -func cleanupExpiredMatchingGames(logger logger.CustomLogger) { - // No-op -} - -// grantRewardHelper 发放奖励辅助函数 -func (h *handler) grantRewardHelper(ctx context.Context, userID, orderID int64, r *model.ActivityRewardSettings) error { - // 1. 扣减库存 - res, err := h.writeDB.ActivityRewardSettings.WithContext(ctx).Where( - h.writeDB.ActivityRewardSettings.ID.Eq(r.ID), - h.writeDB.ActivityRewardSettings.Quantity.Gt(0), - ).UpdateSimple(h.writeDB.ActivityRewardSettings.Quantity.Add(-1)) - if err != nil { - return err - } - if res.RowsAffected == 0 { - return fmt.Errorf("reward out of stock") - } - - // 2. Grant to Order - issue, _ := h.readDB.ActivityIssues.WithContext(ctx).Where(h.readDB.ActivityIssues.ID.Eq(r.IssueID)).First() - var actID int64 - if issue != nil { - actID = issue.ActivityID - } - - rid := r.ID - _, err = h.user.GrantRewardToOrder(ctx, userID, usersvc.GrantRewardToOrderRequest{ - OrderID: orderID, - ProductID: r.ProductID, - Quantity: 1, // 1 prize - ActivityID: &actID, - RewardID: &rid, - Remark: "Matching Game Reward", - }) - if err != nil { - // Use h.logger.Error if available, else fmt.Printf or zap.L().Error - // h.logger is likely type definition interface. - // Let's use generic logger if h.logger doesn't support structured. - // But usually it does. - // h.logger.Error(msg, fields...) - h.logger.Error("Failed to grant reward to order", zap.Int64("order_id", orderID), zap.Error(err)) - return err - } - - // 3. Update Draw Log (IsWinner = 1) - _, err = h.writeDB.ActivityDrawLogs.WithContext(ctx).Where( - h.writeDB.ActivityDrawLogs.OrderID.Eq(orderID), - ).Updates(&model.ActivityDrawLogs{ - IsWinner: 1, - RewardID: r.ID, - Level: r.Level, - // RewardName: r.Name, // Removed - // ProductPrice: 0, // Removed - // UpdatedAt: time.Now(), // Removed - }) - - return err -} - -// parseInt64 将字符串转换为int64 -func parseInt64(s string) int64 { - var n int64 - for i := 0; i < len(s); i++ { - c := s[i] - if c < '0' || c > '9' { - break - } - n = n*10 + int64(c-'0') - } - return n -} diff --git a/internal/api/activity/matching_game_helper.go b/internal/api/activity/matching_game_helper.go new file mode 100644 index 0000000..3d2789c --- /dev/null +++ b/internal/api/activity/matching_game_helper.go @@ -0,0 +1,521 @@ +package app + +import ( + "bindbox-game/internal/pkg/logger" + "bindbox-game/internal/repository/mysql/dao" + "bindbox-game/internal/repository/mysql/model" + usersvc "bindbox-game/internal/service/user" + "context" + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "encoding/binary" + "encoding/hex" + "encoding/json" + "fmt" + "sync" + "time" + + "github.com/redis/go-redis/v9" + "go.uber.org/zap" +) + +// CardType 卡牌类型 +type CardType string + +// CardTypeConfig 卡牌类型配置(从数据库加载) +type CardTypeConfig struct { + Code CardType `json:"code"` + Name string `json:"name"` + ImageURL string `json:"image_url"` + Quantity int32 `json:"quantity"` +} + +// MatchingCard 游戏中的卡牌实例 +type MatchingCard struct { + ID string `json:"id"` + Type CardType `json:"type"` +} + +// MatchingGame 对对碰游戏结构 +type MatchingGame struct { + Mu sync.Mutex `json:"-"` // 互斥锁保护并发访问 + ServerSeed []byte `json:"server_seed"` + ServerSeedHash string `json:"server_seed_hash"` + Nonce int64 `json:"nonce"` + CardConfigs []CardTypeConfig `json:"card_configs"` + Deck []*MatchingCard `json:"deck"` // 牌堆 (预生成的卡牌对象) + Board [9]*MatchingCard `json:"board"` // 固定9格棋盘 + CardIDCounter int64 `json:"card_id_counter"` // 用于生成唯一ID + TotalPairs int64 `json:"total_pairs"` + MaxPossiblePairs int64 `json:"max_possible_pairs"` // 最大可能消除对数 (安全校验) + Round int64 `json:"round"` + RoundHistory []MatchingRoundResult `json:"round_history"` + LastActivity time.Time `json:"last_activity"` + + // Context info for reward granting + ActivityID int64 `json:"activity_id"` + IssueID int64 `json:"issue_id"` + OrderID int64 `json:"order_id"` + UserID int64 `json:"user_id"` +} + +type MatchingRoundResult struct { + Round int64 `json:"round"` + Board [9]*MatchingCard `json:"board"` + Pairs []MatchingPair `json:"pairs"` + PairsCount int64 `json:"pairs_count"` + DrawnCards []DrawnCardInfo `json:"drawn_cards"` // 优化:包含位置信息 + Reshuffled bool `json:"reshuffled"` + CanContinue bool `json:"can_continue"` +} + +type DrawnCardInfo struct { + SlotIndex int `json:"slot_index"` + Card MatchingCard `json:"card"` +} + +type MatchingPair struct { + CardType CardType `json:"card_type"` + Count int64 `json:"count"` + CardIDs []string `json:"card_ids"` + SlotIndices []int `json:"slot_indices"` // 新增:消除的格子索引 +} + +// loadCardTypesFromDB 从数据库加载启用的卡牌类型配置 +func loadCardTypesFromDB(ctx context.Context, readDB *dao.Query) ([]CardTypeConfig, error) { + items, err := readDB.MatchingCardTypes.WithContext(ctx).Where(readDB.MatchingCardTypes.Status.Eq(1)).Order(readDB.MatchingCardTypes.Sort.Asc()).Find() + if err != nil { + return nil, err + } + configs := make([]CardTypeConfig, len(items)) + for i, item := range items { + configs[i] = CardTypeConfig{ + Code: CardType(item.Code), + Name: item.Name, + ImageURL: item.ImageURL, + Quantity: item.Quantity, + } + } + return configs, nil +} + +// NewMatchingGameWithConfig 使用数据库配置创建游戏 +// position: 用户选择的位置(可选),用于增加随机熵值 +// masterSeed: 活动的主承诺种子(可选),如果提供则用作随机源的基础 +func NewMatchingGameWithConfig(configs []CardTypeConfig, position string, masterSeed []byte) *MatchingGame { + g := &MatchingGame{ + CardConfigs: configs, + RoundHistory: []MatchingRoundResult{}, + Board: [9]*MatchingCard{}, + LastActivity: time.Now(), + } + + // 生成服务器种子 + if len(masterSeed) > 0 { + // 使用主承诺种子作为基础 + // ServerSeed = HMAC(MasterSeed, Position + Timestamp) + // 这样保证了基于主承诺的确定性派生 + + h := hmac.New(sha256.New, masterSeed) + h.Write([]byte(position)) + h.Write([]byte(fmt.Sprintf("%d", time.Now().UnixNano()))) + g.ServerSeed = h.Sum(nil) // 32 bytes + } else { + // Fallback to random if no master seed (compatibility) + g.ServerSeed = make([]byte, 32) + rand.Read(g.ServerSeed) + + // 如果有 position 参数,将其混入种子逻辑 + if position != "" { + h := sha256.New() + h.Write(g.ServerSeed) + h.Write([]byte(position)) + h.Write([]byte(fmt.Sprintf("%d", time.Now().UnixNano()))) + g.ServerSeed = h.Sum(nil) + } + } + + hash := sha256.Sum256(g.ServerSeed) + g.ServerSeedHash = fmt.Sprintf("%x", hash) + + // 根据配置生成所有卡牌 (99张) + totalCards := 0 + for _, cfg := range configs { + totalCards += int(cfg.Quantity) + } + // 创建所有卡牌对象 + g.CardIDCounter = 0 + allCards := make([]*MatchingCard, 0, totalCards) + for _, cfg := range configs { + for i := int32(0); i < cfg.Quantity; i++ { + // 创建卡牌对象 + g.CardIDCounter++ + id := fmt.Sprintf("c%d", g.CardIDCounter) + mc := &MatchingCard{ + ID: id, + Type: cfg.Code, + } + allCards = append(allCards, mc) + } + } + g.Deck = allCards + + // 安全洗牌 + g.secureShuffle() + + // 初始填充棋盘 + for i := 0; i < 9; i++ { + if len(g.Deck) > 0 { + // 从牌堆顶取一张 + card := g.Deck[0] + g.Deck = g.Deck[1:] + g.Board[i] = card + } else { + g.Board[i] = nil + } + } + + // 计算理论最大对数 (Sanity Check) + // 遍历所有生成的卡牌配置 + var theoreticalMax int64 + for _, cfg := range configs { + pairs := int64(cfg.Quantity / 2) + theoreticalMax += pairs // 向下取整,每2张算1对 + fmt.Printf("[对对碰生成] 卡牌类型:%s(%s) 数量:%d 可组成对数:%d\n", cfg.Code, cfg.Name, cfg.Quantity, pairs) + } + g.MaxPossiblePairs = theoreticalMax + fmt.Printf("[对对碰生成] 总卡牌数:%d 理论最大对数:%d\n", totalCards, theoreticalMax) + + return g +} + +// getCardConfig 获取指定卡牌类型的配置 +func (g *MatchingGame) getCardConfig(cardType CardType) *CardTypeConfig { + for i := range g.CardConfigs { + if g.CardConfigs[i].Code == cardType { + return &g.CardConfigs[i] + } + } + return nil +} + +// secureShuffle 使用 HMAC-SHA256 的 Fisher-Yates 洗牌 +func (g *MatchingGame) secureShuffle() { + n := len(g.Deck) + for i := n - 1; i > 0; i-- { + j := g.secureRandInt(i+1, fmt.Sprintf("shuffle:%d", i)) + g.Deck[i], g.Deck[j] = g.Deck[j], g.Deck[i] + } +} + +// secureRandInt 使用 HMAC-SHA256 生成安全随机数 +func (g *MatchingGame) secureRandInt(max int, context string) int { + g.Nonce++ + message := fmt.Sprintf("%s|nonce:%d", context, g.Nonce) + mac := hmac.New(sha256.New, g.ServerSeed) + mac.Write([]byte(message)) + sum := mac.Sum(nil) + val := binary.BigEndian.Uint64(sum[:8]) + return int(val % uint64(max)) +} + +// reshuffleBoard 重洗棋盘和牌堆 +func (g *MatchingGame) reshuffleBoard() { + // 1. 回收所有卡牌(板上 + 牌堆) + tempDeck := make([]*MatchingCard, 0, len(g.Deck)+9) + tempDeck = append(tempDeck, g.Deck...) + for i := 0; i < 9; i++ { + if g.Board[i] != nil { + tempDeck = append(tempDeck, g.Board[i]) + g.Board[i] = nil + } + } + + // 2. 循环尝试洗牌,直到开局有解(或者尝试一定次数) + // 尝试最多 10 次,寻找一个起手就有解的局面 + bestDeck := make([]*MatchingCard, len(tempDeck)) + copy(bestDeck, tempDeck) + + for retry := 0; retry < 10; retry++ { + // 复制一份进行尝试 + currentDeck := make([]*MatchingCard, len(tempDeck)) + copy(currentDeck, tempDeck) + g.Deck = currentDeck + g.secureShuffle() + + // 检查前9张(或更少)是否有对子 + checkCount := 9 + if len(g.Deck) < 9 { + checkCount = len(g.Deck) + } + + counts := make(map[CardType]int) + hasPair := false + for k := 0; k < checkCount; k++ { + t := g.Deck[k].Type + counts[t]++ + if counts[t] >= 2 { + hasPair = true + break + } + } + + if hasPair { + // 找到有解的洗牌结果,采用之 + // g.deck 已经是洗好的状态 + break + } + } + + // 3. 重新填满棋盘 + for i := 0; i < 9; i++ { + if len(g.Deck) > 0 { + card := g.Deck[0] + g.Deck = g.Deck[1:] + g.Board[i] = card + } else { + g.Board[i] = nil + } + } +} + +// GetGameState 获取游戏状态 +func (g *MatchingGame) GetGameState() map[string]any { + return map[string]any{ + "board": g.Board, + "deck_count": len(g.Deck), + "total_pairs": g.TotalPairs, + "round": g.Round, + "server_seed_hash": g.ServerSeedHash, + } +} + +// Redis Key Prefix +const matchingGameKeyPrefix = "bindbox:matching_game:" + +// saveGameToRedis 保存游戏状态到 Redis +func (h *handler) saveGameToRedis(ctx context.Context, gameID string, game *MatchingGame) error { + data, err := json.Marshal(game) + if err != nil { + return err + } + // TTL: 30 minutes + return h.redis.Set(ctx, matchingGameKeyPrefix+gameID, data, 30*time.Minute).Err() +} + +// loadGameFromRedis 从 Redis 加载游戏状态 +// 如果 Redis 中没有找到,则尝试从数据库恢复 +func (h *handler) loadGameFromRedis(ctx context.Context, gameID string) (*MatchingGame, error) { + data, err := h.redis.Get(ctx, matchingGameKeyPrefix+gameID).Bytes() + if err == nil { + var game MatchingGame + if err := json.Unmarshal(data, &game); err != nil { + return nil, err + } + return &game, nil + } + // Redis miss - try to recover from DB + if err == redis.Nil { + game, recoverErr := h.recoverGameFromDB(ctx, gameID) + if recoverErr != nil { + return nil, redis.Nil // Return original error to indicate session not found + } + // Cache the recovered game back to Redis + _ = h.saveGameToRedis(ctx, gameID, game) + return game, nil + } + return nil, err +} + +// recoverGameFromDB 从数据库恢复游戏状态 +// 通过 game_id 解析 user_id,然后查找对应的 activity_draw_receipts 记录 +// 使用 ServerSubSeed 重建游戏状态 +func (h *handler) recoverGameFromDB(ctx context.Context, gameID string) (*MatchingGame, error) { + // Parse user_id from game_id (format: MG{userID}{timestamp}) + // Example: MG121766299471192637903 + if len(gameID) < 3 || gameID[:2] != "MG" { + return nil, fmt.Errorf("invalid game_id format") + } + + // Extract user_id: find the first digit sequence after "MG" + // The user_id is typically short (1-5 digits), timestamp is long (19 digits) + numPart := gameID[2:] + var userID int64 + if len(numPart) > 19 { + // User ID is everything before the last 19 chars (nanosecond timestamp) + userIDStr := numPart[:len(numPart)-19] + userID = parseInt64(userIDStr) + } else { + return nil, fmt.Errorf("cannot parse user_id from game_id") + } + + if userID <= 0 { + return nil, fmt.Errorf("invalid user_id in game_id") + } + + // Find the most recent matching game receipt for this user + receipt, err := h.readDB.ActivityDrawReceipts.WithContext(ctx). + Where(h.readDB.ActivityDrawReceipts.ClientID.Eq(userID)). + Where(h.readDB.ActivityDrawReceipts.AlgoVersion.Eq("HMAC-SHA256-v1")). + Order(h.readDB.ActivityDrawReceipts.ID.Desc()). + First() + if err != nil || receipt == nil { + return nil, fmt.Errorf("no matching game receipt found for user %d", userID) + } + + // Decode ServerSubSeed (hex -> bytes) + serverSeed, err := hex.DecodeString(receipt.ServerSubSeed) + if err != nil || len(serverSeed) == 0 { + return nil, fmt.Errorf("invalid server seed in receipt") + } + + // Get DrawLog to find IssueID and OrderID + drawLog, err := h.readDB.ActivityDrawLogs.WithContext(ctx). + Where(h.readDB.ActivityDrawLogs.ID.Eq(receipt.DrawLogID)). + First() + if err != nil || drawLog == nil { + return nil, fmt.Errorf("draw log not found") + } + + // Load card configs + configs, err := loadCardTypesFromDB(ctx, h.readDB) + if err != nil || len(configs) == 0 { + // Fallback to default configs + configs = []CardTypeConfig{ + {Code: "A", Name: "类型A", Quantity: 9}, + {Code: "B", Name: "类型B", Quantity: 9}, + {Code: "C", Name: "类型C", Quantity: 9}, + {Code: "D", Name: "类型D", Quantity: 9}, + {Code: "E", Name: "类型E", Quantity: 9}, + {Code: "F", Name: "类型F", Quantity: 9}, + {Code: "G", Name: "类型G", Quantity: 9}, + {Code: "H", Name: "类型H", Quantity: 9}, + {Code: "I", Name: "类型I", Quantity: 9}, + {Code: "J", Name: "类型J", Quantity: 9}, + {Code: "K", Name: "类型K", Quantity: 9}, + } + } + + // Reconstruct game with the same seed + game := &MatchingGame{ + CardConfigs: configs, + RoundHistory: []MatchingRoundResult{}, + Board: [9]*MatchingCard{}, + LastActivity: time.Now(), + ServerSeed: serverSeed, + ServerSeedHash: receipt.ServerSeedHash, + Nonce: 0, // Reset nonce for reconstruction + IssueID: drawLog.IssueID, + OrderID: drawLog.OrderID, + UserID: userID, + } + + // Get ActivityID from Issue + if issue, _ := h.readDB.ActivityIssues.WithContext(ctx).Where(h.readDB.ActivityIssues.ID.Eq(drawLog.IssueID)).First(); issue != nil { + game.ActivityID = issue.ActivityID + } + + // Generate all cards + totalCards := 0 + for _, cfg := range configs { + totalCards += int(cfg.Quantity) + } + game.CardIDCounter = 0 + allCards := make([]*MatchingCard, 0, totalCards) + for _, cfg := range configs { + for i := int32(0); i < cfg.Quantity; i++ { + game.CardIDCounter++ + id := fmt.Sprintf("c%d", game.CardIDCounter) + mc := &MatchingCard{ + ID: id, + Type: cfg.Code, + } + allCards = append(allCards, mc) + } + } + game.Deck = allCards + + // Shuffle with the same seed (deterministic) + game.secureShuffle() + + // Fill board + for i := 0; i < 9; i++ { + if len(game.Deck) > 0 { + card := game.Deck[0] + game.Deck = game.Deck[1:] + game.Board[i] = card + } else { + game.Board[i] = nil + } + } + + // Calculate max possible pairs + var theoreticalMax int64 + for _, cfg := range configs { + theoreticalMax += int64(cfg.Quantity / 2) + } + game.MaxPossiblePairs = theoreticalMax + + fmt.Printf("[会话恢复] 成功从数据库恢复游戏 game_id=%s user_id=%d issue_id=%d\n", gameID, userID, drawLog.IssueID) + + return game, nil +} + +// startMatchingGameCleanup ... (Deprecated since we use Redis TTL) +func (h *handler) startMatchingGameCleanup() { + // No-op +} + +// cleanupExpiredMatchingGames ... (Deprecated) +func cleanupExpiredMatchingGames(logger logger.CustomLogger) { + // No-op +} + +// grantRewardHelper 发放奖励辅助函数 +func (h *handler) grantRewardHelper(ctx context.Context, userID, orderID int64, r *model.ActivityRewardSettings, quantity int, remark string) error { + // 1. Grant to Order (Delegating stock check to user service) + issue, _ := h.readDB.ActivityIssues.WithContext(ctx).Where(h.readDB.ActivityIssues.ID.Eq(r.IssueID)).First() + var actID int64 + if issue != nil { + actID = issue.ActivityID + } + + rid := r.ID + _, err := h.user.GrantRewardToOrder(ctx, userID, usersvc.GrantRewardToOrderRequest{ + OrderID: orderID, + ProductID: r.ProductID, + Quantity: quantity, + ActivityID: &actID, + RewardID: &rid, + Remark: remark, + }) + if err != nil { + h.logger.Error("Failed to grant reward to order", zap.Int64("order_id", orderID), zap.Error(err)) + return err + } + + // 2. Update Draw Log (IsWinner = 1) + _, err = h.writeDB.ActivityDrawLogs.WithContext(ctx).Where( + h.writeDB.ActivityDrawLogs.OrderID.Eq(orderID), + ).Updates(&model.ActivityDrawLogs{ + IsWinner: 1, + RewardID: r.ID, + Level: r.Level, + }) + + return err +} + +// parseInt64 将字符串转换为int64 +func parseInt64(s string) int64 { + var n int64 + for i := 0; i < len(s); i++ { + c := s[i] + if c < '0' || c > '9' { + break + } + n = n*10 + int64(c-'0') + } + return n +} diff --git a/internal/api/activity/matching_game_verify_test.go b/internal/api/activity/matching_game_verify_test.go deleted file mode 100644 index cbac98d..0000000 --- a/internal/api/activity/matching_game_verify_test.go +++ /dev/null @@ -1,137 +0,0 @@ -package app - -import ( - "fmt" - "testing" -) - -// SimulateClientPlay mimics the frontend logic: Match -> Eliminate -> Fill -> Reshuffle -func SimulateClientPlay(initialCards []*MatchingCard) int { - // Deep copy to avoid modifying original test data - deck := make([]*MatchingCard, len(initialCards)) - copy(deck, initialCards) - - board := make([]*MatchingCard, 9) - // Initial fill - for i := 0; i < 9; i++ { - board[i] = deck[0] - deck = deck[1:] - } - - pairsFound := 0 - - for { - // 1. Check for matches on board - counts := make(map[string][]int) // type -> list of indices - for i, c := range board { - if c != nil { - // Cast CardType to string for map key - counts[string(c.Type)] = append(counts[string(c.Type)], i) - } - } - - matchedType := "" - for t, indices := range counts { - if len(indices) >= 2 { - matchedType = t - break - } - } - - if matchedType != "" { - // Elimination - pairsFound++ - indices := counts[matchedType] - // Remove first 2 - idx1, idx2 := indices[0], indices[1] - board[idx1] = nil - board[idx2] = nil - - // Filling: Fill empty slots from deck - for i := 0; i < 9; i++ { - if board[i] == nil && len(deck) > 0 { - board[i] = deck[0] - deck = deck[1:] - } - } - } else { - // Deadlock (No matches on board) - // User requirement: "Stop when no pairs can be generated" (i.e., No Reshuffle) - // If we are stuck, we stop. - break - } - } - return pairsFound -} - -// TestVerification_DataIntegrity simulates the PreOrder logic 10000 times -func TestVerification_DataIntegrity(t *testing.T) { - fmt.Println("=== Starting Full Game Simulation (10000 Runs) ===") - // Using 10k runs to keep test time reasonable - - configs := []CardTypeConfig{ - {Code: "A", Name: "TypeA", Quantity: 9}, - {Code: "B", Name: "TypeB", Quantity: 9}, - {Code: "C", Name: "TypeC", Quantity: 9}, - {Code: "D", Name: "TypeD", Quantity: 9}, - {Code: "E", Name: "TypeE", Quantity: 9}, - {Code: "F", Name: "TypeF", Quantity: 9}, - {Code: "G", Name: "TypeG", Quantity: 9}, - {Code: "H", Name: "TypeH", Quantity: 9}, - {Code: "I", Name: "TypeI", Quantity: 9}, - {Code: "J", Name: "TypeJ", Quantity: 9}, - {Code: "K", Name: "TypeK", Quantity: 9}, - } - - scoreDist := make(map[int]int) - - for i := 0; i < 10000; i++ { - // 1. Simulate PreOrder generation - game := NewMatchingGameWithConfig(configs, fmt.Sprintf("pos_%d", i)) - - // 2. Reconstruct "all_cards" - allCards := make([]*MatchingCard, 0, 99) - for _, c := range game.Board { - if c != nil { - allCards = append(allCards, c) - } - } - allCards = append(allCards, game.Deck...) - - // 3. Play the game! - score := SimulateClientPlay(allCards) - scoreDist[score]++ - // Note: Without reshuffle, score < 44 is expected. - } - - // Calculate Stats - totalScore := 0 - var allScores []int - for s := 0; s <= 44; s++ { - count := scoreDist[s] - for c := 0; c < count; c++ { - allScores = append(allScores, s) - totalScore += s - } - } - - mean := float64(totalScore) / float64(len(allScores)) - median := allScores[len(allScores)/2] - - fmt.Println("\n=== No-Reshuffle Statistical Analysis (10000 Runs) ===") - fmt.Printf("Mean Score: %.2f / 44\n", mean) - fmt.Printf("Median Score: %d / 44\n", median) - fmt.Printf("Pass Rate: %.2f%%\n", float64(scoreDist[44])/100.0) - fmt.Println("------------------------------------------------") - - // Output Distribution Segments - fmt.Println("Detailed Distribution:") - cumulative := 0 - for s := 0; s <= 44; s++ { - count := scoreDist[s] - if count > 0 { - cumulative += count - fmt.Printf("Score %d: %d times (%.2f%%) [Cum: %.2f%%]\n", s, count, float64(count)/100.0, float64(cumulative)/100.0) - } - } -} diff --git a/internal/api/admin/ichiban_slots_admin.go b/internal/api/admin/ichiban_slots_admin.go index 0dbeced..67379cd 100644 --- a/internal/api/admin/ichiban_slots_admin.go +++ b/internal/api/admin/ichiban_slots_admin.go @@ -21,8 +21,6 @@ type slotItem struct { RewardName string `json:"reward_name"` Level int32 `json:"level"` ProductImage string `json:"product_image"` - OriginalQty int64 `json:"original_qty"` - RemainingQty int64 `json:"remaining_qty"` Claimed bool `json:"claimed"` } @@ -72,7 +70,7 @@ func (h *handler) ListIchibanSlots() core.HandlerFunc { } out := make([]slotItem, len(items)) for i, it := range items { - out[i] = slotItem{SlotIndex: it.SlotIndex, RewardID: it.RewardID, RewardName: it.RewardName, Level: it.Level, ProductImage: it.ProductImage, OriginalQty: it.OriginalQty, RemainingQty: it.RemainingQty, Claimed: it.Claimed} + out[i] = slotItem{SlotIndex: it.SlotIndex, RewardID: it.RewardID, RewardName: it.RewardName, Level: it.Level, ProductImage: it.ProductImage, Claimed: it.Claimed} } ctx.Payload(&listSlotsResponse{TotalSlots: total, List: out}) } @@ -105,8 +103,6 @@ func (h *handler) GetIchibanSlotDetail() core.HandlerFunc { ctx.AbortWithError(core.Error(http.StatusBadRequest, 170203, err.Error())) return } - ctx.Payload(&slotDetailResponse{Item: slotItem{SlotIndex: it.SlotIndex, RewardID: it.RewardID, RewardName: it.RewardName, Level: it.Level, ProductImage: it.ProductImage, OriginalQty: it.OriginalQty, RemainingQty: it.RemainingQty, Claimed: it.Claimed}}) + ctx.Payload(&slotDetailResponse{Item: slotItem{SlotIndex: it.SlotIndex, RewardID: it.RewardID, RewardName: it.RewardName, Level: it.Level, ProductImage: it.ProductImage, Claimed: it.Claimed}}) } } - - diff --git a/internal/api/admin/lottery_admin.go b/internal/api/admin/lottery_admin.go index 8390e3a..18d1bbb 100644 --- a/internal/api/admin/lottery_admin.go +++ b/internal/api/admin/lottery_admin.go @@ -127,6 +127,9 @@ func (h *handler) SettleIssue() core.HandlerFunc { ctx.AbortWithError(core.Error(http.StatusBadRequest, 170201, "issue not found")) return } + // Fetch Activity for Seed + // act, _ := h.readDB.Activities.WithContext(ctx.RequestContext()).Where(h.readDB.Activities.ID.Eq(iss.ActivityID)).First() + cfg := map[string]any{} drawMode := "scheduled" minN := int64(0) diff --git a/internal/api/admin/pay_orders_admin.go b/internal/api/admin/pay_orders_admin.go index 4d8b0ea..952d754 100644 --- a/internal/api/admin/pay_orders_admin.go +++ b/internal/api/admin/pay_orders_admin.go @@ -122,6 +122,93 @@ func (h *handler) ListPayOrders() core.HandlerFunc { } } + // 批量查询优惠券和道具卡信息 + userCouponIDs := make([]int64, 0) + userItemCardIDs := make([]int64, 0) + // 收集订单ID用于查询 OrderCoupons + orderIDs := make([]int64, 0, len(rows)) + for _, o := range rows { + orderIDs = append(orderIDs, o.ID) + if o.CouponID > 0 { + userCouponIDs = append(userCouponIDs, o.CouponID) + } + if o.ItemCardID > 0 { + userItemCardIDs = append(userItemCardIDs, o.ItemCardID) + } + } + + couponMap := make(map[int64]map[string]any) + // orderID -> userCouponID -> appliedAmount + appliedAmountMap := make(map[int64]map[int64]int64) + + if len(userCouponIDs) > 0 { + userCoupons, _ := h.readDB.UserCoupons.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.UserCoupons.ID.In(userCouponIDs...)).Find() + var sysCouponIDs []int64 + ucMap := make(map[int64]*model.UserCoupons) + for _, uc := range userCoupons { + sysCouponIDs = append(sysCouponIDs, uc.CouponID) + ucMap[uc.ID] = uc + } + if len(sysCouponIDs) > 0 { + sysCoupons, _ := h.readDB.SystemCoupons.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.SystemCoupons.ID.In(sysCouponIDs...)).Find() + scMap := make(map[int64]*model.SystemCoupons) + for _, sc := range sysCoupons { + scMap[sc.ID] = sc + } + for id, uc := range ucMap { + if sc, ok := scMap[uc.CouponID]; ok { + couponMap[id] = map[string]any{ + "user_coupon_id": uc.ID, + "name": sc.Name, + "type": sc.DiscountType, + "value": sc.DiscountValue, + } + } + } + } + + // 查询 OrderCoupons 获取实际抵扣金额 + if len(orderIDs) > 0 { + ocs, _ := h.readDB.OrderCoupons.WithContext(ctx.RequestContext()).ReadDB().Where( + h.readDB.OrderCoupons.OrderID.In(orderIDs...), + h.readDB.OrderCoupons.UserCouponID.In(userCouponIDs...), + ).Find() + for _, oc := range ocs { + if _, ok := appliedAmountMap[oc.OrderID]; !ok { + appliedAmountMap[oc.OrderID] = make(map[int64]int64) + } + appliedAmountMap[oc.OrderID][oc.UserCouponID] = oc.AppliedAmount + } + } + } + + itemCardMap := make(map[int64]map[string]any) + if len(userItemCardIDs) > 0 { + userCards, _ := h.readDB.UserItemCards.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.UserItemCards.ID.In(userItemCardIDs...)).Find() + var sysCardIDs []int64 + ucMap := make(map[int64]*model.UserItemCards) + for _, uc := range userCards { + sysCardIDs = append(sysCardIDs, uc.CardID) + ucMap[uc.ID] = uc + } + if len(sysCardIDs) > 0 { + sysCards, _ := h.readDB.SystemItemCards.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.SystemItemCards.ID.In(sysCardIDs...)).Find() + scMap := make(map[int64]*model.SystemItemCards) + for _, sc := range sysCards { + scMap[sc.ID] = sc + } + for id, uc := range ucMap { + if sc, ok := scMap[uc.CardID]; ok { + itemCardMap[id] = map[string]any{ + "user_card_id": uc.ID, + "name": sc.Name, + "effect_type": sc.EffectType, + } + } + } + } + } + out := make([]map[string]any, 0, len(rows)) for _, o := range rows { ledgers, err := h.readDB.UserPointsLedger.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.UserPointsLedger.RefTable.Eq("orders"), h.readDB.UserPointsLedger.RefID.Eq(o.OrderNo)).Find() @@ -144,6 +231,20 @@ func (h *handler) ListPayOrders() core.HandlerFunc { } else if pa > 0 { pu = pa * pointsRate } + var cInfo map[string]any + if baseInfo, ok := couponMap[o.CouponID]; ok { + cInfo = make(map[string]any) + for k, v := range baseInfo { + cInfo[k] = v + } + // 尝试使用实际抵扣金额 + if amMap, ok := appliedAmountMap[o.ID]; ok { + if amount, ok2 := amMap[o.CouponID]; ok2 { + cInfo["value"] = amount + } + } + } + item := map[string]any{ "id": o.ID, "order_no": o.OrderNo, @@ -157,12 +258,14 @@ func (h *handler) ListPayOrders() core.HandlerFunc { } return "" }(), - "created_at": o.CreatedAt, - "is_consumed": o.IsConsumed, - "remark": o.Remark, - "points_amount": pa, - "points_used": pu, - "total_amount": o.TotalAmount, + "created_at": o.CreatedAt, + "is_consumed": o.IsConsumed, + "remark": o.Remark, + "points_amount": pa, + "points_used": pu, + "total_amount": o.TotalAmount, + "coupon_info": cInfo, + "item_card_info": itemCardMap[o.ItemCardID], } out = append(out, item) } @@ -238,6 +341,8 @@ type getPayOrderResponse struct { } `json:"reward_items"` RewardShipments []*model.ShippingRecords `json:"reward_shipments"` DrawReceipts []*usersvc.DrawReceiptInfo `json:"draw_receipts"` + CouponInfo map[string]any `json:"coupon_info"` + ItemCardInfo map[string]any `json:"item_card_info"` } func (h *handler) GetPayOrderDetail() core.HandlerFunc { @@ -658,6 +763,41 @@ func (h *handler) GetPayOrderDetail() core.HandlerFunc { } } + // 补充优惠券和道具卡信息 + if order.CouponID > 0 { + if uc, _ := h.readDB.UserCoupons.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.UserCoupons.ID.Eq(order.CouponID)).First(); uc != nil { + if sc, _ := h.readDB.SystemCoupons.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.SystemCoupons.ID.Eq(uc.CouponID)).First(); sc != nil { + val := sc.DiscountValue + // 尝试查找实际抵扣金额 + // 上面已经查询了 ocRows,直接遍历查找 + for _, r := range ocRows { + if r.UserCouponID == order.CouponID { + val = r.AppliedAmount + break + } + } + + rsp.CouponInfo = map[string]any{ + "user_coupon_id": uc.ID, + "name": sc.Name, + "type": sc.DiscountType, + "value": val, + } + } + } + } + if order.ItemCardID > 0 { + if uc, _ := h.readDB.UserItemCards.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.UserItemCards.ID.Eq(order.ItemCardID)).First(); uc != nil { + if sc, _ := h.readDB.SystemItemCards.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.SystemItemCards.ID.Eq(uc.CardID)).First(); sc != nil { + rsp.ItemCardInfo = map[string]any{ + "user_card_id": uc.ID, + "name": sc.Name, + "effect_type": sc.EffectType, + } + } + } + } + ctx.Payload(rsp) } } diff --git a/internal/api/admin/pay_refund_admin.go b/internal/api/admin/pay_refund_admin.go index 6463062..f7cbefb 100644 --- a/internal/api/admin/pay_refund_admin.go +++ b/internal/api/admin/pay_refund_admin.go @@ -4,6 +4,9 @@ import ( "encoding/json" "fmt" "net/http" + "regexp" + "strconv" + "strings" "time" "bindbox-game/internal/code" @@ -11,6 +14,7 @@ import ( paypkg "bindbox-game/internal/pkg/pay" "bindbox-game/internal/pkg/validation" "bindbox-game/internal/repository/mysql/model" + usersvc "bindbox-game/internal/service/user" ) type createRefundRequest struct { @@ -42,6 +46,35 @@ func (h *handler) CreateRefund() core.HandlerFunc { ctx.AbortWithError(core.Error(http.StatusBadRequest, 160001, "order not found")) return } + + // 预检查:检查是否有已兑换积分的资产,并验证用户积分余额是否足够扣除 + allInvs, _ := h.readDB.UserInventory.WithContext(ctx.RequestContext()).Where(h.readDB.UserInventory.OrderID.Eq(order.ID)).Where(h.readDB.UserInventory.Status.In(1, 3)).Find() + + var pointsToReclaim int64 + rePoints := regexp.MustCompile(`\|redeemed_points=(\d+)`) + for _, inv := range allInvs { + if inv.Status == 3 && strings.Contains(inv.Remark, "redeemed_points=") { + matches := rePoints.FindStringSubmatch(inv.Remark) + if len(matches) > 1 { + p, _ := strconv.ParseInt(matches[1], 10, 64) + pointsToReclaim += p + } + } + } + + if pointsToReclaim > 0 { + svc := usersvc.New(h.logger, h.repo) + balance, err := svc.GetPointsBalance(ctx.RequestContext(), order.UserID) + if err != nil { + ctx.AbortWithError(core.Error(http.StatusInternalServerError, 160010, "check points balance failed")) + return + } + if balance < pointsToReclaim { + ctx.AbortWithError(core.Error(http.StatusBadRequest, 160011, fmt.Sprintf("用户积分不足以抵扣已兑换积分(需%d, 余额%d),无法退款", pointsToReclaim, balance))) + return + } + } + // 计算已退款与可退余额(分) ledgers, _ := h.readDB.UserPointsLedger.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.UserPointsLedger.RefTable.Eq("orders"), h.readDB.UserPointsLedger.RefID.Eq(order.OrderNo)).Find() var refundedSumCents int64 @@ -137,19 +170,35 @@ func (h *handler) CreateRefund() core.HandlerFunc { } } - // 全额退款:回收中奖资产与奖品库存 - type invRow struct { - ID int64 - RewardID int64 - } - var invs []invRow - _ = h.repo.GetDbR().Raw("SELECT id, reward_id FROM user_inventory WHERE order_id=? AND status=1", order.ID).Scan(&invs).Error - for _, inv := range invs { - // 更新状态为2 (作废) - _ = h.repo.GetDbW().Exec("UPDATE user_inventory SET status=2, updated_at=NOW(3), remark=CONCAT(IFNULL(remark,''),'|refund_void') WHERE id=?", inv.ID).Error - // 恢复奖品库存 (ActivityRewardSettings) - if inv.RewardID > 0 { - _ = h.repo.GetDbW().Exec("UPDATE activity_reward_settings SET quantity = quantity + 1 WHERE id=?", inv.RewardID).Error + // 全额退款:回收中奖资产与奖品库存(包含已兑换积分的资产) + svc := usersvc.New(h.logger, h.repo) + for _, inv := range allInvs { + if inv.Status == 1 { + // 状态1(持有):更新状态为2 (作废) + _ = h.repo.GetDbW().Exec("UPDATE user_inventory SET status=2, updated_at=NOW(3), remark=CONCAT(IFNULL(remark,''),'|refund_void') WHERE id=?", inv.ID).Error + // 恢复奖品库存 + if inv.RewardID > 0 { + _ = h.repo.GetDbW().Exec("UPDATE activity_reward_settings SET quantity = quantity + 1 WHERE id=?", inv.RewardID).Error + } + } else if inv.Status == 3 { + // 状态3(已兑换):扣除积分并作废 + matches := rePoints.FindStringSubmatch(inv.Remark) + if len(matches) > 1 { + p, _ := strconv.ParseInt(matches[1], 10, 64) + if p > 0 { + // 扣除积分(记录流水) + _, err := svc.ConsumePointsFor(ctx.RequestContext(), order.UserID, p, "user_inventory", strconv.FormatInt(inv.ID, 10), "订单退款回收兑换积分", "refund_reclaim") + if err != nil { + h.logger.Error(fmt.Sprintf("refund reclaim points failed: order=%s user=%d points=%d err=%v", order.OrderNo, order.UserID, p, err)) + } + } + } + // 更新状态为2 (作废) + _ = h.repo.GetDbW().Exec("UPDATE user_inventory SET status=2, updated_at=NOW(3), remark=CONCAT(IFNULL(remark,''),'|refund_reclaimed') WHERE id=?", inv.ID).Error + // 恢复奖品库存 + if inv.RewardID > 0 { + _ = h.repo.GetDbW().Exec("UPDATE activity_reward_settings SET quantity = quantity + 1 WHERE id=?", inv.RewardID).Error + } } } diff --git a/internal/api/admin/rewards_admin.go b/internal/api/admin/rewards_admin.go index 918dada..7b34c8e 100644 --- a/internal/api/admin/rewards_admin.go +++ b/internal/api/admin/rewards_admin.go @@ -11,16 +11,16 @@ import ( ) type rewardItem struct { - ID int64 `json:"id"` - ProductID int64 `json:"product_id"` - Name string `json:"name" binding:"required"` - Weight int32 `json:"weight" binding:"required"` - Quantity int64 `json:"quantity" binding:"required"` - OriginalQty int64 `json:"original_qty" binding:"required"` - Level int32 `json:"level" binding:"required"` - Sort int32 `json:"sort"` - IsBoss int32 `json:"is_boss"` - MinScore int64 `json:"min_score"` + ID int64 `json:"id"` + ProductID int64 `json:"product_id"` + Name string `json:"name" binding:"required"` + Weight float64 `json:"weight" binding:"required"` + Quantity int64 `json:"quantity" binding:"required"` + OriginalQty int64 `json:"original_qty" binding:"required"` + Level int32 `json:"level" binding:"required"` + Sort int32 `json:"sort"` + IsBoss int32 `json:"is_boss"` + MinScore int64 `json:"min_score"` } type createRewardsRequest struct { @@ -70,7 +70,7 @@ func (h *handler) CreateIssueRewards() core.HandlerFunc { rewards = append(rewards, activitysvc.CreateRewardInput{ ProductID: r.ProductID, Name: r.Name, - Weight: r.Weight, + Weight: int32(r.Weight), Quantity: r.Quantity, OriginalQty: r.OriginalQty, Level: r.Level, @@ -120,7 +120,7 @@ func (h *handler) ListIssueRewards() core.HandlerFunc { ID: v.ID, ProductID: v.ProductID, Name: v.Name, - Weight: v.Weight, + Weight: float64(v.Weight), Quantity: v.Quantity, OriginalQty: v.OriginalQty, Level: v.Level, diff --git a/internal/api/pay/pay.go b/internal/api/pay/pay.go index 60430a6..db09469 100644 --- a/internal/api/pay/pay.go +++ b/internal/api/pay/pay.go @@ -1,20 +1,23 @@ package pay import ( - "bindbox-game/internal/pkg/logger" - "bindbox-game/internal/repository/mysql" - "bindbox-game/internal/repository/mysql/dao" - usersvc "bindbox-game/internal/service/user" + "bindbox-game/internal/pkg/logger" + "bindbox-game/internal/repository/mysql" + "bindbox-game/internal/repository/mysql/dao" + tasksvc "bindbox-game/internal/service/task_center" + usersvc "bindbox-game/internal/service/user" ) type handler struct { - logger logger.CustomLogger - writeDB *dao.Query - readDB *dao.Query - user usersvc.Service - repo mysql.Repo + logger logger.CustomLogger + writeDB *dao.Query + readDB *dao.Query + user usersvc.Service + task tasksvc.Service + repo mysql.Repo } -func New(logger logger.CustomLogger, db mysql.Repo) *handler { - return &handler{logger: logger, writeDB: dao.Use(db.GetDbW()), readDB: dao.Use(db.GetDbR()), user: usersvc.New(logger, db), repo: db} +func New(logger logger.CustomLogger, db mysql.Repo, taskSvc tasksvc.Service) *handler { + userSvc := usersvc.New(logger, db) + return &handler{logger: logger, writeDB: dao.Use(db.GetDbW()), readDB: dao.Use(db.GetDbR()), user: userSvc, task: taskSvc, repo: db} } diff --git a/internal/api/pay/wechat_notify.go b/internal/api/pay/wechat_notify.go index a96f60e..a2dcb5e 100644 --- a/internal/api/pay/wechat_notify.go +++ b/internal/api/pay/wechat_notify.go @@ -13,11 +13,14 @@ import ( "bindbox-game/internal/pkg/core" lotterynotify "bindbox-game/internal/pkg/notify" pay "bindbox-game/internal/pkg/pay" + pkgutils "bindbox-game/internal/pkg/utils" "bindbox-game/internal/pkg/wechat" "bindbox-game/internal/repository/mysql/model" strat "bindbox-game/internal/service/activity/strategy" usersvc "bindbox-game/internal/service/user" + "go.uber.org/zap" + "github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers" "github.com/wechatpay-apiv3/wechatpay-go/core/downloader" "github.com/wechatpay-apiv3/wechatpay-go/core/notify" @@ -150,6 +153,12 @@ func (h *handler) WechatNotify() core.HandlerFunc { ctx.AbortWithError(core.Error(http.StatusInternalServerError, 150005, err.Error())) return } + + // 触发任务中心逻辑 (如有效邀请检测) + if err := h.task.OnOrderPaid(ctx.RequestContext(), order.UserID, order.ID); err != nil { + h.logger.Error("TaskCenter OnOrderPaid failed", zap.Error(err)) + } + ord, _ := h.readDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.OrderNo.Eq(*transaction.OutTradeNo)).First() // 支付成功后扣减优惠券余额(优先使用结构化明细表),如无明细再降级解析备注 if ord != nil { @@ -345,6 +354,7 @@ func (h *handler) WechatNotify() core.HandlerFunc { break } // 位置已在上面占用,这里直接选择奖品 + // Use Commitment Seed (via SelectItemBySlot internal logic) rid, proof, e2 := strat.NewIchiban(h.readDB, h.writeDB).SelectItemBySlot(ctx.RequestContext(), aid, iss, slot) fmt.Printf("[支付回调-抽奖] SelectItemBySlot 结果 rid=%d err=%v\n", rid, e2) if e2 != nil || rid <= 0 { @@ -471,7 +481,7 @@ func (h *handler) WechatNotify() core.HandlerFunc { } } // 【开奖后虚拟发货】即时开奖完成后上传虚拟发货 - go func(orderID int64, orderNo string, userID int64, actName string) { + go func(orderID int64, orderNo string, userID int64, actName string, playType string) { bgCtx := context.Background() drawLogs, _ := h.readDB.ActivityDrawLogs.WithContext(bgCtx).Where(h.readDB.ActivityDrawLogs.OrderID.Eq(orderID)).Find() if len(drawLogs) == 0 { @@ -486,9 +496,7 @@ func (h *handler) WechatNotify() core.HandlerFunc { } } itemsDesc := actName + " " + orderNo + " 盲盒赏品: " + strings.Join(rewardNames, ", ") - if len(itemsDesc) > 120 { - itemsDesc = itemsDesc[:120] - } + itemsDesc = pkgutils.TruncateBytes(itemsDesc, 120) // 获取支付交易信息 var tx *model.PaymentTransactions tx, _ = h.readDB.PaymentTransactions.WithContext(bgCtx).Where(h.readDB.PaymentTransactions.OrderNo.Eq(orderNo)).First() @@ -507,14 +515,16 @@ func (h *handler) WechatNotify() core.HandlerFunc { if err := wechat.UploadVirtualShippingForBackground(bgCtx, &wechat.WechatConfig{AppID: c.Wechat.AppID, AppSecret: c.Wechat.AppSecret}, tx.TransactionID, orderNo, payerOpenid, itemsDesc); err != nil { fmt.Printf("[即时开奖-虚拟发货] 上传失败: %v\n", err) } - // 【开奖后推送通知】 - notifyCfg := &lotterynotify.WechatNotifyConfig{ - AppID: c.Wechat.AppID, - AppSecret: c.Wechat.AppSecret, - LotteryResultTemplateID: c.Wechat.LotteryResultTemplateID, + // 【开奖后推送通知】只有一番赏才发送 + if playType == "ichiban" { + notifyCfg := &lotterynotify.WechatNotifyConfig{ + AppID: c.Wechat.AppID, + AppSecret: c.Wechat.AppSecret, + LotteryResultTemplateID: c.Wechat.LotteryResultTemplateID, + } + _ = lotterynotify.SendLotteryResultNotification(bgCtx, notifyCfg, payerOpenid, actName, rewardNames, orderNo, time.Now()) } - _ = lotterynotify.SendLotteryResultNotification(bgCtx, notifyCfg, payerOpenid, actName, rewardNames, orderNo, time.Now()) - }(ord.ID, ord.OrderNo, ord.UserID, act.Name) + }(ord.ID, ord.OrderNo, ord.UserID, act.Name, act.PlayType) } } if ord != nil { @@ -525,10 +535,7 @@ func (h *handler) WechatNotify() core.HandlerFunc { parts = append(parts, it.Title+"*"+func(q int64) string { return fmt.Sprintf("%d", q) }(it.Quantity)) } s := strings.Join(parts, ", ") - if len(s) > 120 { - s = s[:120] - } - itemsDesc = s + itemsDesc = pkgutils.TruncateRunes(s, 120) } else { itemsDesc = "订单" + ord.OrderNo } @@ -536,8 +543,8 @@ func (h *handler) WechatNotify() core.HandlerFunc { if transaction.Payer != nil && transaction.Payer.Openid != nil { payerOpenid = *transaction.Payer.Openid } - // 抽奖订单在开奖后发货,非抽奖订单在支付后立即发货 - if ord.SourceType != 2 { + // 抽奖订单(2)和对对碰订单(3)在开奖/结算后发货,非此类订单在支付后立即发货 + if ord.SourceType != 2 && ord.SourceType != 3 { if transaction.TransactionId != nil && *transaction.TransactionId != "" { fmt.Printf("[支付回调] 虚拟发货 尝试上传 order_id=%d order_no=%s user_id=%d transaction_id=%s items_desc=%s payer_openid=%s\n", ord.ID, ord.OrderNo, ord.UserID, *transaction.TransactionId, itemsDesc, payerOpenid) if err := wechat.UploadVirtualShippingWithFallback(ctx, &wechat.WechatConfig{AppID: c.Wechat.AppID, AppSecret: c.Wechat.AppSecret}, *transaction.TransactionId, ord.OrderNo, payerOpenid, itemsDesc, time.Now()); err != nil { @@ -545,7 +552,7 @@ func (h *handler) WechatNotify() core.HandlerFunc { } } } else { - fmt.Printf("[支付回调] 抽奖订单跳过虚拟发货,将在开奖后发货 order_id=%d order_no=%s\n", ord.ID, ord.OrderNo) + fmt.Printf("[支付回调] 抽奖/对对碰订单跳过虚拟发货,将在开奖/结算后发货 order_id=%d order_no=%s source_type=%d\n", ord.ID, ord.OrderNo, ord.SourceType) } } // 标记事件处理完成 diff --git a/internal/api/task_center/admin.go b/internal/api/task_center/admin.go index 05766bc..4b432eb 100644 --- a/internal/api/task_center/admin.go +++ b/internal/api/task_center/admin.go @@ -6,6 +6,7 @@ import ( tasksvc "bindbox-game/internal/service/task_center" "net/http" "strconv" + "time" "gorm.io/datatypes" ) @@ -30,7 +31,14 @@ func (h *handler) ListTasksForAdmin() core.HandlerFunc { } out := &rsp{Total: total, List: make([]map[string]any, len(items))} for i, v := range items { - out.List[i] = map[string]any{"id": v.ID, "name": v.Name, "description": v.Description, "status": v.Status, "start_time": v.StartTime, "end_time": v.EndTime} + var stStr, etStr string + if v.StartTime > 0 { + stStr = time.Unix(v.StartTime, 0).Format("2006-01-02 15:04:05") + } + if v.EndTime > 0 { + etStr = time.Unix(v.EndTime, 0).Format("2006-01-02 15:04:05") + } + out.List[i] = map[string]any{"id": v.ID, "name": v.Name, "description": v.Description, "status": v.Status, "start_time": stStr, "end_time": etStr} } ctx.Payload(out) } @@ -41,6 +49,8 @@ type createTaskRequest struct { Description string `json:"description"` Status int32 `json:"status"` Visibility int32 `json:"visibility"` + StartTime string `json:"start_time"` + EndTime string `json:"end_time"` } // @Summary 创建任务(Admin) @@ -58,7 +68,18 @@ func (h *handler) CreateTaskForAdmin() core.HandlerFunc { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, err.Error())) return } - id, err := h.task.CreateTask(ctx.RequestContext(), tasksvc.CreateTaskInput{Name: req.Name, Description: req.Description, Status: req.Status, Visibility: req.Visibility}) + var st, et *time.Time + if req.StartTime != "" { + if t, err := time.Parse("2006-01-02 15:04:05", req.StartTime); err == nil { + st = &t + } + } + if req.EndTime != "" { + if t, err := time.Parse("2006-01-02 15:04:05", req.EndTime); err == nil { + et = &t + } + } + id, err := h.task.CreateTask(ctx.RequestContext(), tasksvc.CreateTaskInput{Name: req.Name, Description: req.Description, Status: req.Status, Visibility: req.Visibility, StartTime: st, EndTime: et}) if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, err.Error())) return @@ -72,6 +93,8 @@ type modifyTaskRequest struct { Description string `json:"description"` Status int32 `json:"status"` Visibility int32 `json:"visibility"` + StartTime string `json:"start_time"` + EndTime string `json:"end_time"` } // @Summary 修改任务(Admin) @@ -95,7 +118,18 @@ func (h *handler) ModifyTaskForAdmin() core.HandlerFunc { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, err.Error())) return } - if err := h.task.ModifyTask(ctx.RequestContext(), id, tasksvc.ModifyTaskInput{Name: req.Name, Description: req.Description, Status: req.Status, Visibility: req.Visibility}); err != nil { + var st, et *time.Time + if req.StartTime != "" { + if t, err := time.Parse("2006-01-02 15:04:05", req.StartTime); err == nil { + st = &t + } + } + if req.EndTime != "" { + if t, err := time.Parse("2006-01-02 15:04:05", req.EndTime); err == nil { + et = &t + } + } + if err := h.task.ModifyTask(ctx.RequestContext(), id, tasksvc.ModifyTaskInput{Name: req.Name, Description: req.Description, Status: req.Status, Visibility: req.Visibility, StartTime: st, EndTime: et}); err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, err.Error())) return } @@ -128,12 +162,13 @@ func (h *handler) DeleteTaskForAdmin() core.HandlerFunc { type upsertTiersRequest struct { Tiers []struct { - Metric string `json:"metric"` - Operator string `json:"operator"` - Threshold int64 `json:"threshold"` - Window string `json:"window"` - Repeatable int32 `json:"repeatable"` - Priority int32 `json:"priority"` + Metric string `json:"metric"` + Operator string `json:"operator"` + Threshold int64 `json:"threshold"` + Window string `json:"window"` + Repeatable int32 `json:"repeatable"` + Priority int32 `json:"priority"` + ExtraParams datatypes.JSON `json:"extra_params"` } `json:"tiers"` } @@ -160,7 +195,7 @@ func (h *handler) UpsertTaskTiersForAdmin() core.HandlerFunc { } in := make([]tasksvc.TaskTierInput, len(req.Tiers)) for i, t := range req.Tiers { - in[i] = tasksvc.TaskTierInput{Metric: t.Metric, Operator: t.Operator, Threshold: t.Threshold, Window: t.Window, Repeatable: t.Repeatable, Priority: t.Priority} + in[i] = tasksvc.TaskTierInput{Metric: t.Metric, Operator: t.Operator, Threshold: t.Threshold, Window: t.Window, Repeatable: t.Repeatable, Priority: t.Priority, ExtraParams: t.ExtraParams} } if err := h.task.UpsertTaskTiers(ctx.RequestContext(), id, in); err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, err.Error())) diff --git a/internal/api/task_center/app.go b/internal/api/task_center/app.go index 381b880..df0537c 100644 --- a/internal/api/task_center/app.go +++ b/internal/api/task_center/app.go @@ -1,27 +1,30 @@ package taskcenter import ( - "bindbox-game/internal/pkg/logger" - "bindbox-game/internal/repository/mysql" - "bindbox-game/internal/repository/mysql/dao" - tasksvc "bindbox-game/internal/service/task_center" + "bindbox-game/internal/pkg/logger" + "bindbox-game/internal/repository/mysql" + "bindbox-game/internal/repository/mysql/dao" + tasksvc "bindbox-game/internal/service/task_center" ) type handler struct { - logger logger.CustomLogger - writeDB *dao.Query - readDB *dao.Query - repo mysql.Repo - task tasksvc.Service + logger logger.CustomLogger + writeDB *dao.Query + readDB *dao.Query + repo mysql.Repo + task tasksvc.Service } -func New(l logger.CustomLogger, db mysql.Repo) *handler { - return &handler{ - logger: l, - writeDB: dao.Use(db.GetDbW()), - readDB: dao.Use(db.GetDbR()), - repo: db, - task: tasksvc.New(l, db), - } +func New(l logger.CustomLogger, db mysql.Repo, taskSvc tasksvc.Service) *handler { + return &handler{ + logger: l, + writeDB: dao.Use(db.GetDbW()), + readDB: dao.Use(db.GetDbR()), + repo: db, + task: taskSvc, + } } +func (h *handler) GetTaskService() tasksvc.Service { + return h.task +} diff --git a/internal/api/user/cancel_shipping_app.go b/internal/api/user/cancel_shipping_app.go new file mode 100644 index 0000000..f925699 --- /dev/null +++ b/internal/api/user/cancel_shipping_app.go @@ -0,0 +1,43 @@ +package app + +import ( + "net/http" + + "bindbox-game/internal/code" + "bindbox-game/internal/pkg/core" +) + +type cancelShippingRequest struct { + InventoryID int64 `json:"inventory_id"` +} + +type cancelShippingResponse struct{} + +// CancelShipping 取消发货申请 +// @Summary 取消发货申请 +// @Description 取消已提交但未发货的申请;恢复库存状态 +// @Tags APP端.用户 +// @Accept json +// @Produce json +// @Security LoginVerifyToken +// @Param user_id path integer true "用户ID" +// @Param RequestBody body cancelShippingRequest true "请求参数:资产ID" +// @Success 200 {object} cancelShippingResponse "成功" +// @Failure 400 {object} code.Failure "参数错误/记录不存在/已处理" +// @Router /api/app/users/{user_id}/inventory/cancel-shipping [post] +func (h *handler) CancelShipping() core.HandlerFunc { + return func(ctx core.Context) { + req := new(cancelShippingRequest) + if err := ctx.ShouldBindJSON(req); err != nil || req.InventoryID == 0 { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "参数错误")) + return + } + userID := int64(ctx.SessionUserInfo().Id) + err := h.user.CancelShipping(ctx.RequestContext(), userID, req.InventoryID) + if err != nil { + ctx.AbortWithError(core.Error(http.StatusBadRequest, 10024, err.Error())) + return + } + ctx.Payload(nil) + } +} diff --git a/internal/api/user/item_cards_app.go b/internal/api/user/item_cards_app.go index ebeb735..233bf0b 100644 --- a/internal/api/user/item_cards_app.go +++ b/internal/api/user/item_cards_app.go @@ -55,7 +55,17 @@ func (h *handler) ListUserItemCards() core.HandlerFunc { status = *req.Status } - items, total, err := h.user.ListUserItemCardsWithTemplateByStatus(ctx.RequestContext(), userID, status, req.Page, req.PageSize) + var items []*usersvc.ItemCardWithTemplate + var total int64 + var err error + + // 道具卡列表 Status=1 (未使用) 时聚合显示数量 + if status == 1 { + items, total, err = h.user.ListAggregatedUserItemCards(ctx.RequestContext(), userID, status, req.Page, req.PageSize) + } else { + items, total, err = h.user.ListUserItemCardsWithTemplateByStatus(ctx.RequestContext(), userID, status, req.Page, req.PageSize) + } + if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 10008, err.Error())) return diff --git a/internal/pkg/async/task_queue.go b/internal/pkg/async/task_queue.go new file mode 100644 index 0000000..f689c8a --- /dev/null +++ b/internal/pkg/async/task_queue.go @@ -0,0 +1,93 @@ +package async + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/redis/go-redis/v9" +) + +const ( + TaskEventQueueKey = "task_center:events:queue" +) + +type EventType string + +const ( + EventTypeOrderPaid EventType = "order_paid" + EventTypeInviteSuccess EventType = "invite_success" +) + +type TaskEvent struct { + Type EventType `json:"type"` + Payload string `json:"payload"` + CreatedAt int64 `json:"created_at"` +} + +type OrderPaidPayload struct { + UserID int64 `json:"user_id"` + OrderID int64 `json:"order_id"` +} + +type InviteSuccessPayload struct { + InviterID int64 `json:"inviter_id"` + InviteeID int64 `json:"invitee_id"` +} + +type TaskQueue interface { + PublishOrderPaid(ctx context.Context, userID, orderID int64) error + PublishInviteSuccess(ctx context.Context, inviterID, inviteeID int64) error + Consume(ctx context.Context) (*TaskEvent, error) +} + +type redisTaskQueue struct { + client *redis.Client +} + +func NewRedisTaskQueue(client *redis.Client) TaskQueue { + return &redisTaskQueue{client: client} +} + +func (q *redisTaskQueue) PublishOrderPaid(ctx context.Context, userID, orderID int64) error { + payload, _ := json.Marshal(OrderPaidPayload{UserID: userID, OrderID: orderID}) + event := TaskEvent{ + Type: EventTypeOrderPaid, + Payload: string(payload), + CreatedAt: time.Now().Unix(), + } + bytes, _ := json.Marshal(event) + return q.client.LPush(ctx, TaskEventQueueKey, bytes).Err() +} + +func (q *redisTaskQueue) PublishInviteSuccess(ctx context.Context, inviterID, inviteeID int64) error { + payload, _ := json.Marshal(InviteSuccessPayload{InviterID: inviterID, InviteeID: inviteeID}) + event := TaskEvent{ + Type: EventTypeInviteSuccess, + Payload: string(payload), + CreatedAt: time.Now().Unix(), + } + bytes, _ := json.Marshal(event) + return q.client.LPush(ctx, TaskEventQueueKey, bytes).Err() +} + +func (q *redisTaskQueue) Consume(ctx context.Context) (*TaskEvent, error) { + // Block for 2 seconds + result, err := q.client.BRPop(ctx, 2*time.Second, TaskEventQueueKey).Result() + if err != nil { + if err == redis.Nil { + return nil, nil // Timeout, no message + } + return nil, err + } + if len(result) < 2 { + return nil, fmt.Errorf("invalid redis result") + } + + var event TaskEvent + if err := json.Unmarshal([]byte(result[1]), &event); err != nil { + return nil, err + } + return &event, nil +} diff --git a/internal/pkg/core/core.go b/internal/pkg/core/core.go index 97a302b..64b73c6 100644 --- a/internal/pkg/core/core.go +++ b/internal/pkg/core/core.go @@ -490,17 +490,17 @@ func New(logger logger.CustomLogger, options ...Option) (Mux, error) { t.Success = !ctx.IsAborted() && (ctx.Writer.Status() == http.StatusOK) t.CostSeconds = time.Since(ts).Seconds() - logger.Info("trace-log", - zap.Any("method", ctx.Request.Method), - zap.Any("path", decodedURL), - zap.Any("http_code", ctx.Writer.Status()), - zap.Any("business_code", businessCode), - zap.Any("success", t.Success), - zap.Any("cost_seconds", t.CostSeconds), - zap.Any("trace_id", t.Identifier), - zap.Any("trace_info", t), - zap.Error(abortErr), - ) + // logger.Info("trace-log", + // zap.Any("method", ctx.Request.Method), + // zap.Any("path", decodedURL), + // zap.Any("http_code", ctx.Writer.Status()), + // zap.Any("business_code", businessCode), + // zap.Any("success", t.Success), + // zap.Any("cost_seconds", t.CostSeconds), + // zap.Any("trace_id", t.Identifier), + // zap.Any("trace_info", t), + // zap.Error(abortErr), + // ) traceInfo := "" if traceJsonData, err := json.Marshal(t); err == nil { diff --git a/internal/pkg/notify/lottery_notify.go b/internal/pkg/notify/lottery_notify.go index 167b3ef..f68cd03 100644 --- a/internal/pkg/notify/lottery_notify.go +++ b/internal/pkg/notify/lottery_notify.go @@ -9,6 +9,7 @@ import ( "time" "bindbox-game/internal/pkg/httpclient" + pkgutils "bindbox-game/internal/pkg/utils" ) // WechatNotifyConfig 微信通知配置 @@ -111,19 +112,16 @@ func SendLotteryResultNotification(ctx context.Context, cfg *WechatNotifyConfig, return err } // 活动名称限制长度(thing类型不超过20个字符) - if len(activityName) > 20 { - activityName = activityName[:17] + "..." - } + activityName = pkgutils.TruncateRunes(activityName, 20) // 构建中奖结果描述(phrase类型限制5个汉字以内) - // 将中奖奖品写入到 resultPhrase - resultPhrase := strings.Join(rewardNames, ",") - if len(resultPhrase) > 5 { - resultPhrase = resultPhrase[:2] + "..." - } + // 由于奖品名称通常较长,phrase3 放不下,改为固定文案 "恭喜中奖" + // 将奖品名称放入 Thing4 (温馨提示),限制 20 字符 + resultPhrase := "恭喜中奖" + + rewardsStr := strings.Join(rewardNames, ",") + warmTips := pkgutils.TruncateRunes(rewardsStr, 20) - // 温馨提示:固定短语 - warmTips := "已发送到您的货柜" req := &LotteryResultNotificationRequest{ Touser: openid, TemplateID: cfg.LotteryResultTemplateID, diff --git a/internal/pkg/utils/truncate_test.go b/internal/pkg/utils/truncate_test.go new file mode 100644 index 0000000..68b80ee --- /dev/null +++ b/internal/pkg/utils/truncate_test.go @@ -0,0 +1,58 @@ +package utils + +import ( + "testing" +) + +func TestTruncateRunes(t *testing.T) { + tests := []struct { + name string + input string + limit int + want string + }{ + {"Short English", "hello", 10, "hello"}, + {"Long English", "hello world", 5, "hello"}, + {"Short Chinese", "你好世界", 10, "你好世界"}, + {"Long Chinese", "你好世界", 2, "你好"}, + {"Mixed", "hi你好", 3, "hi你"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := TruncateRunes(tt.input, tt.limit); got != tt.want { + t.Errorf("TruncateRunes() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestTruncateBytes(t *testing.T) { + tests := []struct { + name string + input string + limit int + want string + }{ + {"Short English", "hello", 10, "hello"}, + {"Long English", "hello world", 5, "hello"}, + {"Chinese Exact", "你好", 6, "你好"}, // 3+3=6 + {"Chinese Cut", "你好", 5, "你"}, // 3+3=6 > 5, cut to 3 + {"Chinese Cut 2", "你好", 4, "你"}, // 3+3=6 > 4, cut to 3 + {"Chinese Cut 3", "你好", 2, ""}, // 3 > 2, cut to 0 + {"Mixed", "hi你好", 4, "hi"}, // 1+1+3=5 > 4, cut to 2 + {"Mixed 2", "hi你好", 5, "hi你"}, // 1+1+3=5 <= 5 + {"Log Case", "名创优品 O20251223131406 盲盒赏品: MINISO名创优品U型枕飞机云朵护颈枕记忆棉", 40, "名创优品 O20251223131406 盲盒赏"}, // Correctly truncates before "品" + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := TruncateBytes(tt.input, tt.limit) + if got != tt.want { + t.Errorf("TruncateBytes(%q, %d) = %q, want %q", tt.input, tt.limit, got, tt.want) + } + // Verify valid utf8 + if len(got) > tt.limit { + t.Errorf("Length %d > limit %d", len(got), tt.limit) + } + }) + } +} diff --git a/internal/pkg/utils/utils.go b/internal/pkg/utils/utils.go index 4e0b87a..7c6e0b6 100644 --- a/internal/pkg/utils/utils.go +++ b/internal/pkg/utils/utils.go @@ -229,3 +229,28 @@ func XorDecrypt(encrypted, key string) (string, error) { } return string(result), nil } + +// TruncateRunes truncates a string to the specified rune limit +func TruncateRunes(s string, limit int) string { + runes := []rune(s) + if len(runes) > limit { + return string(runes[:limit]) + } + return s +} + +// TruncateBytes truncates a string to the specified byte limit while ensuring valid UTF-8 +func TruncateBytes(s string, limit int) string { + if len(s) <= limit { + return s + } + var b int + for i, r := range s { + rl := len(string(r)) + if b+rl > limit { + return s[:i] + } + b += rl + } + return s +} diff --git a/internal/repository/mysql/model/activities.gen.go b/internal/repository/mysql/model/activities.gen.go index d82d114..baf131b 100644 --- a/internal/repository/mysql/model/activities.gen.go +++ b/internal/repository/mysql/model/activities.gen.go @@ -39,6 +39,10 @@ type Activities struct { CommitmentAlgo string `gorm:"column:commitment_algo;default:commit-v1" json:"commitment_algo"` CommitmentSeedMaster []byte `gorm:"column:commitment_seed_master" json:"commitment_seed_master"` CommitmentSeedHash []byte `gorm:"column:commitment_seed_hash" json:"commitment_seed_hash"` + DailySeed string `gorm:"column:daily_seed" json:"daily_seed"` + DailySeedDate string `gorm:"column:daily_seed_date" json:"daily_seed_date"` + LastDailySeed string `gorm:"column:last_daily_seed" json:"last_daily_seed"` + LastDailySeedDate string `gorm:"column:last_daily_seed_date" json:"last_daily_seed_date"` CommitmentStateVersion int32 `gorm:"column:commitment_state_version" json:"commitment_state_version"` CommitmentItemsRoot []byte `gorm:"column:commitment_items_root" json:"commitment_items_root"` GameplayIntro string `gorm:"column:gameplay_intro;comment:玩法介绍" json:"gameplay_intro"` // 玩法介绍 diff --git a/internal/repository/mysql/model/orders.gen.go b/internal/repository/mysql/model/orders.gen.go index e731afe..fa2bbd2 100644 --- a/internal/repository/mysql/model/orders.gen.go +++ b/internal/repository/mysql/model/orders.gen.go @@ -29,6 +29,8 @@ type Orders struct { UserAddressID int64 `gorm:"column:user_address_id;comment:收货地址ID(user_addresses.id)" json:"user_address_id"` // 收货地址ID(user_addresses.id) IsConsumed int32 `gorm:"column:is_consumed;not null;comment:是否已履约/消耗(对虚拟资产)" json:"is_consumed"` // 是否已履约/消耗(对虚拟资产) PointsLedgerID int64 `gorm:"column:points_ledger_id;comment:积分扣减流水ID(user_points_ledger.id)" json:"points_ledger_id"` // 积分扣减流水ID(user_points_ledger.id) + CouponID int64 `gorm:"column:coupon_id;comment:使用的优惠券ID" json:"coupon_id"` // 使用的优惠券ID + ItemCardID int64 `gorm:"column:item_card_id;comment:使用的道具卡ID" json:"item_card_id"` // 使用的道具卡ID Remark string `gorm:"column:remark;comment:备注" json:"remark"` // 备注 } diff --git a/internal/repository/mysql/model/user_invites.gen.go b/internal/repository/mysql/model/user_invites.gen.go index b7c0ee8..47cd1cc 100644 --- a/internal/repository/mysql/model/user_invites.gen.go +++ b/internal/repository/mysql/model/user_invites.gen.go @@ -18,8 +18,10 @@ type UserInvites struct { InviterID int64 `gorm:"column:inviter_id;not null;comment:邀请人用户ID" json:"inviter_id"` // 邀请人用户ID InviteeID int64 `gorm:"column:invitee_id;not null;comment:被邀请用户ID" json:"invitee_id"` // 被邀请用户ID InviteCode string `gorm:"column:invite_code;not null;comment:邀请时使用的邀请码" json:"invite_code"` // 邀请时使用的邀请码 - RewardPoints int64 `gorm:"column:reward_points;not null;comment:发放的积分数量(用于审计)" json:"reward_points"` // 发放的积分数量(用于审计) - RewardedAt time.Time `gorm:"column:rewarded_at;comment:奖励发放时间" json:"rewarded_at"` // 奖励发放时间 + RewardPoints int64 `gorm:"column:reward_points;not null;comment:发放的积分数量(用于审计)" json:"reward_points"` // 发放的积分数量(用于审计) + IsEffective int32 `gorm:"column:is_effective;not null;default:0;comment:是否为有效邀请" json:"is_effective"` // 是否为有效邀请 + AccumulatedAmount int64 `gorm:"column:accumulated_amount;not null;default:0;comment:被邀请人累计消费金额(分)" json:"accumulated_amount"` // 被邀请人累计消费金额(分) + RewardedAt time.Time `gorm:"column:rewarded_at;comment:奖励发放时间" json:"rewarded_at"` // 奖励发放时间 CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP(3);comment:创建时间" json:"created_at"` // 创建时间 UpdatedAt time.Time `gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP(3);comment:更新时间" json:"updated_at"` // 更新时间 DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间(软删)" json:"deleted_at"` // 删除时间(软删) diff --git a/internal/repository/mysql/task_center/models.go b/internal/repository/mysql/task_center/models.go index 68492f8..e798f88 100644 --- a/internal/repository/mysql/task_center/models.go +++ b/internal/repository/mysql/task_center/models.go @@ -1,85 +1,89 @@ package task_center import ( - "time" - "gorm.io/datatypes" - "gorm.io/gorm" + "time" + + "gorm.io/datatypes" + "gorm.io/gorm" ) type Task struct { - ID int64 `gorm:"primaryKey;autoIncrement"` - Name string `gorm:"size:64;not null"` - Description string `gorm:"type:text"` - Status int32 `gorm:"not null"` - StartTime *time.Time `gorm:"index"` - EndTime *time.Time `gorm:"index"` - Visibility int32 `gorm:"not null"` - ConditionsSchema datatypes.JSON `gorm:"type:json"` - CreatedAt time.Time - UpdatedAt time.Time + ID int64 `gorm:"primaryKey;autoIncrement"` + Name string `gorm:"size:64;not null"` + Description string `gorm:"type:text"` + Status int32 `gorm:"not null;index"` // 增加索引,常用于过滤活跃任务 + StartTime *time.Time `gorm:"index"` + EndTime *time.Time `gorm:"index"` + Visibility int32 `gorm:"not null"` + ConditionsSchema datatypes.JSON `gorm:"type:json"` + Tiers []TaskTier `gorm:"foreignKey:TaskID"` + Rewards []TaskReward `gorm:"foreignKey:TaskID"` + CreatedAt time.Time + UpdatedAt time.Time } func (Task) TableName() string { return "task_center_tasks" } type TaskTier struct { - ID int64 `gorm:"primaryKey;autoIncrement"` - TaskID int64 `gorm:"index;not null"` - Metric string `gorm:"size:32;not null"` - Operator string `gorm:"size:8;not null"` - Threshold int64 `gorm:"not null"` - Window string `gorm:"size:32;not null"` - Repeatable int32 `gorm:"not null"` - Priority int32 `gorm:"not null"` - CreatedAt time.Time - UpdatedAt time.Time + ID int64 `gorm:"primaryKey;autoIncrement"` + TaskID int64 `gorm:"index;not null"` + Metric string `gorm:"size:32;not null"` + Operator string `gorm:"size:8;not null"` + Threshold int64 `gorm:"not null"` + Window string `gorm:"size:32;not null"` + Repeatable int32 `gorm:"not null"` + Priority int32 `gorm:"not null"` + ExtraParams datatypes.JSON `gorm:"type:json"` + CreatedAt time.Time + UpdatedAt time.Time } func (TaskTier) TableName() string { return "task_center_task_tiers" } type TaskReward struct { - ID int64 `gorm:"primaryKey;autoIncrement"` - TaskID int64 `gorm:"index;not null"` - TierID int64 `gorm:"index;not null"` - RewardType string `gorm:"size:32;not null"` - RewardPayload datatypes.JSON `gorm:"type:json"` - Quantity int64 `gorm:"not null"` - CreatedAt time.Time - UpdatedAt time.Time + ID int64 `gorm:"primaryKey;autoIncrement"` + TaskID int64 `gorm:"index;not null"` + TierID int64 `gorm:"index;not null"` + RewardType string `gorm:"size:32;not null"` + RewardPayload datatypes.JSON `gorm:"type:json"` + Quantity int64 `gorm:"not null"` + CreatedAt time.Time + UpdatedAt time.Time } func (TaskReward) TableName() string { return "task_center_task_rewards" } type UserTaskProgress struct { - ID int64 `gorm:"primaryKey;autoIncrement"` - UserID int64 `gorm:"index;not null"` - TaskID int64 `gorm:"index;not null"` - OrderCount int64 `gorm:"not null"` - InviteCount int64 `gorm:"not null"` - FirstOrder int32 `gorm:"not null"` - ClaimedTiers datatypes.JSON `gorm:"type:json"` - CreatedAt time.Time - UpdatedAt time.Time + ID int64 `gorm:"primaryKey;autoIncrement"` + UserID int64 `gorm:"uniqueIndex:uk_user_task;not null"` // 联合唯一索引,防止并发重复创建 + TaskID int64 `gorm:"uniqueIndex:uk_user_task;not null"` // 联合唯一索引 + OrderCount int64 `gorm:"not null"` + InviteCount int64 `gorm:"not null"` + EffectiveInviteCount int64 `gorm:"not null;default:0"` + FirstOrder int32 `gorm:"not null"` + ClaimedTiers datatypes.JSON `gorm:"type:json"` + CreatedAt time.Time + UpdatedAt time.Time } func (UserTaskProgress) TableName() string { return "task_center_user_progress" } type TaskEventLog struct { - ID int64 `gorm:"primaryKey;autoIncrement"` - EventID string `gorm:"size:64;index"` - SourceType string `gorm:"size:16"` - SourceID int64 `gorm:"index"` - UserID int64 `gorm:"index"` - TaskID int64 `gorm:"index"` - TierID int64 `gorm:"index"` - IdempotencyKey string `gorm:"size:128;uniqueIndex"` - Status string `gorm:"size:16"` - Result datatypes.JSON `gorm:"type:json"` - CreatedAt time.Time + ID int64 `gorm:"primaryKey;autoIncrement"` + EventID string `gorm:"size:64;index"` + SourceType string `gorm:"size:16"` + SourceID int64 `gorm:"index"` + UserID int64 `gorm:"index"` + TaskID int64 `gorm:"index"` + TierID int64 `gorm:"index"` + IdempotencyKey string `gorm:"size:128;uniqueIndex"` + Status string `gorm:"size:16"` + Result datatypes.JSON `gorm:"type:json"` + CreatedAt time.Time } func (TaskEventLog) TableName() string { return "task_center_event_logs" } func AutoMigrate(db *gorm.DB) error { - return db.AutoMigrate(&Task{}, &TaskTier{}, &TaskReward{}, &UserTaskProgress{}, &TaskEventLog{}) + return db.AutoMigrate(&Task{}, &TaskTier{}, &TaskReward{}, &UserTaskProgress{}, &TaskEventLog{}) } - diff --git a/internal/router/router.go b/internal/router/router.go index 270461d..e71967b 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -15,18 +15,21 @@ import ( "bindbox-game/internal/pkg/redis" "bindbox-game/internal/repository/mysql" "bindbox-game/internal/router/interceptor" + tasksvc "bindbox-game/internal/service/task_center" + titlesvc "bindbox-game/internal/service/title" + usersvc "bindbox-game/internal/service/user" "context" "github.com/pkg/errors" ) -func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo) (core.Mux, error) { +func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo) (core.Mux, func(), error) { if logger == nil { - return nil, errors.New("logger required") + return nil, nil, errors.New("logger required") } if db == nil { - return nil, errors.New("db required") + return nil, nil, errors.New("db required") } mux, err := core.New(logger, @@ -41,19 +44,27 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo) (core.Mux, error) { panic(err) } - // Init Redis - if err := redis.Init(context.Background(), logger); err != nil { - panic(err) - } + // Redis is initialized in main.go rdb := redis.GetClient() + // Instantiate Services + userSvc := usersvc.New(logger, db) + titleSvc := titlesvc.New(logger, db) + taskSvc := tasksvc.New(logger, db, rdb, userSvc, titleSvc) + + // Context for Worker + ctx, cancel := context.WithCancel(context.Background()) + // Start task center worker + go taskSvc.StartWorker(ctx) + // 实例化拦截器 adminHandler := admin.New(logger, db) activityHandler := activityapi.New(logger, db, rdb) - taskCenterHandler := taskcenterapi.New(logger, db) + taskCenterHandler := taskcenterapi.New(logger, db, taskSvc) + userHandler := userapi.New(logger, db) commonHandler := commonapi.New(logger, db) - payHandler := payapi.New(logger, db) + payHandler := payapi.New(logger, db, taskSvc) // minesweeperHandler := minesweeperapi.New(logger, db) intc := interceptor.New(logger, db) @@ -343,6 +354,7 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo) (core.Mux, error) { appAuthApiRouter.POST("/users/:user_id/inventory/address-share/create", userHandler.CreateAddressShare()) appAuthApiRouter.POST("/users/:user_id/inventory/address-share/revoke", userHandler.RevokeAddressShare()) appAuthApiRouter.POST("/users/:user_id/inventory/request-shipping", userHandler.RequestShippingBatch()) + appAuthApiRouter.POST("/users/:user_id/inventory/cancel-shipping", userHandler.CancelShipping()) appAuthApiRouter.POST("/users/:user_id/inventory/redeem", userHandler.RedeemInventoryToPoints()) appAuthApiRouter.POST("/users/:user_id/points/redeem-coupon", userHandler.RedeemPointsToCoupon()) appAuthApiRouter.POST("/users/:user_id/points/redeem-product", userHandler.RedeemPointsToProduct()) @@ -350,5 +362,5 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo) (core.Mux, error) { } // 微信支付平台回调(无需鉴权) mux.Group("/api/pay").POST("/wechat/notify", payHandler.WechatNotify()) - return mux, nil + return mux, cancel, nil } diff --git a/internal/service/activity/activity_order_service.go b/internal/service/activity/activity_order_service.go index 62a6afd..1f71464 100644 --- a/internal/service/activity/activity_order_service.go +++ b/internal/service/activity/activity_order_service.go @@ -91,6 +91,16 @@ func (s *activityOrderService) CreateActivityOrder(ctx core.Context, req CreateA order.Remark = fmt.Sprintf("activity:%d|issue:%d|count:%d", req.ActivityID, req.IssueID, count) } + // 记录优惠券和道具卡信息(显式字段 + 备注追加) + if req.CouponID != nil && *req.CouponID > 0 { + order.CouponID = *req.CouponID + order.Remark += fmt.Sprintf("|coupon:%d", *req.CouponID) + } + if req.ItemCardID != nil && *req.ItemCardID > 0 { + order.ItemCardID = *req.ItemCardID + order.Remark += fmt.Sprintf("|itemcard:%d", *req.ItemCardID) + } + // 2. 应用称号折扣 (Title Discount) titleEffects, _ := s.title.ResolveActiveEffects(ctx.RequestContext(), userID, titlesvc.EffectScope{ ActivityID: &req.ActivityID, diff --git a/internal/service/activity/ichiban_slots_service.go b/internal/service/activity/ichiban_slots_service.go index 06ce2e2..8f38361 100644 --- a/internal/service/activity/ichiban_slots_service.go +++ b/internal/service/activity/ichiban_slots_service.go @@ -36,20 +36,14 @@ func (s *IchibanSlotsService) buildMapping(ctx context.Context, activityID int64 if err != nil || len(rewards) == 0 { return nil, errors.New("no rewards") } - var total int64 - for _, r := range rewards { - if r.OriginalQty > 0 { - total += r.OriginalQty - } - } + // 一番赏:每种奖品 = 1个格位 + total := int64(len(rewards)) if total <= 0 { return nil, errors.New("no slots") } - slots := make([]int64, 0, total) - for _, r := range rewards { - for i := int64(0); i < r.OriginalQty; i++ { - slots = append(slots, r.ID) - } + slots := make([]int64, total) + for i, r := range rewards { + slots[i] = r.ID } mac := hmac.New(sha256.New, seed) for i := int(total - 1); i > 0; i-- { @@ -68,8 +62,6 @@ type SlotItem struct { RewardName string Level int32 ProductImage string - OriginalQty int64 - RemainingQty int64 Claimed bool } @@ -131,7 +123,7 @@ func (s *IchibanSlotsService) Page(ctx context.Context, activityID int64, issueI continue } } - items = append(items, SlotItem{SlotIndex: i + 1, RewardID: rid, RewardName: rw.Name, Level: rw.Level, ProductImage: img, OriginalQty: rw.OriginalQty, RemainingQty: rw.Quantity, Claimed: c}) + items = append(items, SlotItem{SlotIndex: i + 1, RewardID: rid, RewardName: rw.Name, Level: rw.Level, ProductImage: img, Claimed: c}) } return total, items, nil } @@ -163,5 +155,5 @@ func (s *IchibanSlotsService) SlotDetail(ctx context.Context, activityID int64, } var claimed int64 _ = s.repo.GetDbR().Raw("SELECT COUNT(1) FROM issue_position_claims WHERE issue_id=? AND slot_index=?", issueID, idx0).Scan(&claimed) - return SlotItem{SlotIndex: slotIndex, RewardID: rid, RewardName: rw.Name, Level: rw.Level, ProductImage: img, OriginalQty: rw.OriginalQty, RemainingQty: rw.Quantity, Claimed: claimed > 0}, nil + return SlotItem{SlotIndex: slotIndex, RewardID: rid, RewardName: rw.Name, Level: rw.Level, ProductImage: img, Claimed: claimed > 0}, nil } diff --git a/internal/service/activity/reward_effects_service.go b/internal/service/activity/reward_effects_service.go deleted file mode 100644 index 1c5a8d0..0000000 --- a/internal/service/activity/reward_effects_service.go +++ /dev/null @@ -1,265 +0,0 @@ -package activity - -import ( - "bindbox-game/internal/pkg/logger" - "bindbox-game/internal/repository/mysql" - "bindbox-game/internal/repository/mysql/dao" - "bindbox-game/internal/repository/mysql/model" - usersvc "bindbox-game/internal/service/user" - "context" - "crypto/rand" - "encoding/binary" - "fmt" - "time" -) - -// RewardEffectsService 奖励效果服务 -// 统一处理奖励发放和道具卡效果应用 -type RewardEffectsService interface { - // GrantRewardWithEffects 发放奖励并应用道具卡效果 - GrantRewardWithEffects(ctx context.Context, req GrantRewardRequest) (*GrantRewardResult, error) -} - -// GrantRewardRequest 奖励发放请求 -type GrantRewardRequest struct { - UserID int64 // 用户ID - OrderID int64 // 订单ID - ActivityID int64 // 活动ID - IssueID int64 // 期ID - Reward *model.ActivityRewardSettings // 要发放的奖励 - AllRewards []*model.ActivityRewardSettings // 所有可用奖励(用于概率提升升级) -} - -// GrantRewardResult 奖励发放结果 -type GrantRewardResult struct { - RewardID int64 // 发放的奖励ID - RewardName string // 奖励名称 - ItemCardApplied bool // 是否应用了道具卡效果 - UpgradedReward *model.ActivityRewardSettings // 如果概率提升成功,升级后的奖励 - DrawLogID int64 // 创建的抽奖日志ID -} - -type rewardEffectsService struct { - logger logger.CustomLogger - readDB *dao.Query - writeDB *dao.Query - repo mysql.Repo - user usersvc.Service -} - -// NewRewardEffectsService 创建奖励效果服务 -func NewRewardEffectsService(l logger.CustomLogger, db mysql.Repo) RewardEffectsService { - return &rewardEffectsService{ - logger: l, - readDB: dao.Use(db.GetDbR()), - writeDB: dao.Use(db.GetDbW()), - repo: db, - user: usersvc.New(l, db), - } -} - -// GrantRewardWithEffects 发放奖励并应用道具卡效果 -func (s *rewardEffectsService) GrantRewardWithEffects(ctx context.Context, req GrantRewardRequest) (*GrantRewardResult, error) { - if req.Reward == nil { - return nil, fmt.Errorf("reward is nil") - } - - result := &GrantRewardResult{ - RewardID: req.Reward.ID, - RewardName: req.Reward.Name, - } - - // 1. 扣减库存 - res, err := s.writeDB.ActivityRewardSettings.WithContext(ctx).Where( - s.writeDB.ActivityRewardSettings.ID.Eq(req.Reward.ID), - s.writeDB.ActivityRewardSettings.Quantity.Gt(0), - ).UpdateSimple(s.writeDB.ActivityRewardSettings.Quantity.Add(-1)) - if err != nil { - return nil, err - } - if res.RowsAffected == 0 { - return nil, fmt.Errorf("reward out of stock") - } - - // 2. 发放奖励到订单 - rid := req.Reward.ID - _, err = s.user.GrantRewardToOrder(ctx, req.UserID, usersvc.GrantRewardToOrderRequest{ - OrderID: req.OrderID, - ProductID: req.Reward.ProductID, - Quantity: 1, - ActivityID: &req.ActivityID, - RewardID: &rid, - Remark: req.Reward.Name, - }) - if err != nil { - return nil, err - } - - // 3. 创建抽奖日志 - drawLog := &model.ActivityDrawLogs{ - UserID: req.UserID, - IssueID: req.IssueID, - OrderID: req.OrderID, - RewardID: req.Reward.ID, - IsWinner: 1, - Level: req.Reward.Level, - CurrentLevel: 1, - CreatedAt: time.Now(), - } - if err := s.writeDB.ActivityDrawLogs.WithContext(ctx).Create(drawLog); err != nil { - return nil, err - } - result.DrawLogID = drawLog.ID - - // 4. 从订单备注解析道具卡ID并应用效果 - ord, _ := s.readDB.Orders.WithContext(ctx).Where(s.readDB.Orders.ID.Eq(req.OrderID)).First() - if ord != nil { - icID := parseItemCardIDFromRemark(ord.Remark) - if icID > 0 { - applied, upgradedReward := s.applyItemCardEffects(ctx, req, icID, drawLog.ID) - result.ItemCardApplied = applied - result.UpgradedReward = upgradedReward - } - } - - return result, nil -} - -// applyItemCardEffects 应用道具卡效果 -func (s *rewardEffectsService) applyItemCardEffects(ctx context.Context, req GrantRewardRequest, icID int64, drawLogID int64) (bool, *model.ActivityRewardSettings) { - fmt.Printf("[道具卡-RewardEffects] 从订单备注解析道具卡ID icID=%d\n", icID) - - uic, _ := s.readDB.UserItemCards.WithContext(ctx).Where( - s.readDB.UserItemCards.ID.Eq(icID), - s.readDB.UserItemCards.UserID.Eq(req.UserID), - s.readDB.UserItemCards.Status.Eq(1), - ).First() - if uic == nil { - fmt.Printf("[道具卡-RewardEffects] ❌ 未找到用户道具卡 用户ID=%d 道具卡ID=%d\n", req.UserID, icID) - return false, nil - } - - ic, _ := s.readDB.SystemItemCards.WithContext(ctx).Where( - s.readDB.SystemItemCards.ID.Eq(uic.CardID), - s.readDB.SystemItemCards.Status.Eq(1), - ).First() - if ic == nil { - fmt.Printf("[道具卡-RewardEffects] ❌ 未找到系统道具卡 CardID=%d\n", uic.CardID) - return false, nil - } - - now := time.Now() - if uic.ValidStart.After(now) || uic.ValidEnd.Before(now) { - fmt.Printf("[道具卡-RewardEffects] ❌ 道具卡不在有效期\n") - return false, nil - } - - // 范围检查 - scopeOK := (ic.ScopeType == 1) || (ic.ScopeType == 3 && ic.ActivityID == req.ActivityID) || (ic.ScopeType == 4 && ic.IssueID == req.IssueID) - if !scopeOK { - fmt.Printf("[道具卡-RewardEffects] ❌ 范围检查失败 ScopeType=%d\n", ic.ScopeType) - return false, nil - } - - var upgradedReward *model.ActivityRewardSettings - - // 应用效果 - if ic.EffectType == 1 && ic.RewardMultiplierX1000 >= 2000 { - // 双倍奖励 - fmt.Printf("[道具卡-RewardEffects] ✅ 应用双倍奖励 倍数=%d 奖品ID=%d 奖品名=%s\n", ic.RewardMultiplierX1000, req.Reward.ID, req.Reward.Name) - rid := req.Reward.ID - _, _ = s.user.GrantRewardToOrder(ctx, req.UserID, usersvc.GrantRewardToOrderRequest{ - OrderID: req.OrderID, - ProductID: req.Reward.ProductID, - Quantity: 1, - ActivityID: &req.ActivityID, - RewardID: &rid, - Remark: req.Reward.Name + "(倍数)", - }) - } else if ic.EffectType == 2 && ic.BoostRateX1000 > 0 { - // 概率提升 - 尝试升级到更好的奖励 - fmt.Printf("[道具卡-RewardEffects] 应用概率提升 BoostRateX1000=%d\n", ic.BoostRateX1000) - var better *model.ActivityRewardSettings - for _, r := range req.AllRewards { - if r.MinScore > req.Reward.MinScore && r.Quantity > 0 { - if better == nil || r.MinScore < better.MinScore { - better = r - } - } - } - if better != nil { - randBytes := make([]byte, 4) - rand.Read(randBytes) - randVal := int32(binary.BigEndian.Uint32(randBytes) % 1000) - if randVal < ic.BoostRateX1000 { - fmt.Printf("[道具卡-RewardEffects] ✅ 概率提升成功 升级到奖品ID=%d 奖品名=%s\n", better.ID, better.Name) - rid := better.ID - _, _ = s.user.GrantRewardToOrder(ctx, req.UserID, usersvc.GrantRewardToOrderRequest{ - OrderID: req.OrderID, - ProductID: better.ProductID, - Quantity: 1, - ActivityID: &req.ActivityID, - RewardID: &rid, - Remark: better.Name + "(升级)", - }) - upgradedReward = better - } - } - } - - // 核销道具卡 - fmt.Printf("[道具卡-RewardEffects] 核销道具卡 用户道具卡ID=%d\n", icID) - _, _ = s.writeDB.UserItemCards.WithContext(ctx).Where( - s.writeDB.UserItemCards.ID.Eq(icID), - s.writeDB.UserItemCards.UserID.Eq(req.UserID), - s.writeDB.UserItemCards.Status.Eq(1), - ).Updates(map[string]any{ - s.writeDB.UserItemCards.Status.ColumnName().String(): 2, - s.writeDB.UserItemCards.UsedDrawLogID.ColumnName().String(): drawLogID, - s.writeDB.UserItemCards.UsedActivityID.ColumnName().String(): req.ActivityID, - s.writeDB.UserItemCards.UsedIssueID.ColumnName().String(): req.IssueID, - s.writeDB.UserItemCards.UsedAt.ColumnName().String(): now, - }) - - return true, upgradedReward -} - -// parseItemCardIDFromRemark 从订单备注解析道具卡ID -func parseItemCardIDFromRemark(remark string) int64 { - if remark == "" { - return 0 - } - // 查找 |itemcard:xxx 模式 - prefix := "|itemcard:" - idx := -1 - for i := 0; i <= len(remark)-len(prefix); i++ { - if remark[i:i+len(prefix)] == prefix { - idx = i + len(prefix) - break - } - } - if idx < 0 { - // 也检查开头没有 | 的情况 - prefix = "itemcard:" - for i := 0; i <= len(remark)-len(prefix); i++ { - if remark[i:i+len(prefix)] == prefix { - idx = i + len(prefix) - break - } - } - } - if idx < 0 { - return 0 - } - - var n int64 - for idx < len(remark) { - c := remark[idx] - if c < '0' || c > '9' { - break - } - n = n*10 + int64(c-'0') - idx++ - } - return n -} diff --git a/internal/service/activity/scheduler.go b/internal/service/activity/scheduler.go index e6932c7..91110a4 100644 --- a/internal/service/activity/scheduler.go +++ b/internal/service/activity/scheduler.go @@ -35,6 +35,24 @@ func StartScheduledSettlement(l logger.CustomLogger, repo mysql.Repo) { r := dao.Use(repo.GetDbR()) w := dao.Use(repo.GetDbW()) us := usersvc.New(l, repo) + + // Ensure lottery_refund_logs table exists + _ = repo.GetDbW().Exec(`CREATE TABLE IF NOT EXISTS lottery_refund_logs ( + id bigint unsigned AUTO_INCREMENT PRIMARY KEY, + issue_id bigint NOT NULL DEFAULT 0, + order_id bigint NOT NULL DEFAULT 0, + user_id bigint NOT NULL DEFAULT 0, + amount bigint NOT NULL DEFAULT 0, + coupon_type varchar(64) DEFAULT '', + coupon_amount bigint DEFAULT 0, + reason varchar(255) DEFAULT '', + status varchar(32) DEFAULT '', + created_at datetime DEFAULT CURRENT_TIMESTAMP, + updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + INDEX idx_issue (issue_id), + INDEX idx_order (order_id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`).Error + go func() { t := time.NewTicker(30 * time.Second) defer t.Stop() @@ -108,45 +126,77 @@ func StartScheduledSettlement(l logger.CustomLogger, repo mysql.Repo) { r.Orders.CreatedAt.Gte(last), ).Find() count := int64(len(orders)) - fmt.Printf("[定时开奖] 活动ID=%d 查询到订单数=%d 最低参与人数=%d 是否满足=%t\n", aid, count, a.MinParticipants, count >= a.MinParticipants) + fmt.Printf("[定时开奖] 活动ID=%d 查询到订单数=%d 最低参与人数=%d\n", aid, count, a.MinParticipants) - if count < a.MinParticipants { - fmt.Printf("[定时开奖] 活动ID=%d ❌ 人数不足,进行退款处理\n", aid) - wc, err := paypkg.NewWechatPayClient(ctx) - if err == nil { - for _, o := range orders { - // 先处理积分退款(如有) - if o.PointsAmount > 0 { - refundPts := o.PointsAmount / 100 - _, _ = us.RefundPoints(ctx, o.UserID, refundPts, o.OrderNo, "scheduled_not_enough") - } - // 微信支付部分退款(如有) - if o.ActualAmount > 0 { - refundNo := fmt.Sprintf("R%s-%d", o.OrderNo, time.Now().Unix()) - refundID, status, err := wc.RefundOrder(ctx, o.OrderNo, refundNo, o.ActualAmount, o.ActualAmount, "scheduled_not_enough") - if err == nil { - _ = w.PaymentRefunds.WithContext(ctx).Create(&model.PaymentRefunds{OrderID: o.ID, OrderNo: o.OrderNo, RefundNo: refundNo, Channel: "wechat_jsapi", Status: status, AmountRefund: o.ActualAmount, Reason: "scheduled_not_enough"}) - _ = w.UserPointsLedger.WithContext(ctx).Create(&model.UserPointsLedger{UserID: o.UserID, Action: "refund_amount", Points: o.ActualAmount / 100, RefTable: "payment_refund", RefID: refundID}) - } - } - // 标记订单退款 - _, _ = w.Orders.WithContext(ctx).Where(w.Orders.ID.Eq(o.ID)).Updates(map[string]any{w.Orders.Status.ColumnName().String(): 4}) - iss := extractIssueID(o.Remark) - _ = repo.GetDbW().Exec("INSERT INTO lottery_refund_logs(issue_id, order_id, user_id, amount, coupon_type, coupon_amount, reason, status) VALUES(?,?,?,?,?,?,?,?)", iss, o.ID, o.UserID, o.ActualAmount, "", 0, "scheduled_not_enough", "done").Error - if a.RefundCouponID > 0 { - _ = us.AddCoupon(ctx, o.UserID, a.RefundCouponID) + // Initialize Wechat Client if needed + wc, _ := paypkg.NewWechatPayClient(ctx) + + refundedIssues := make(map[int64]bool) + + // 【优化】一番赏定时退款:检查是否售罄 + if a.PlayType == "ichiban" { + issueIDs := make(map[int64]struct{}) + for _, o := range orders { + iss := extractIssueID(o.Remark) + if iss > 0 { + issueIDs[iss] = struct{}{} + } + } + + for iss := range issueIDs { + // Check Sales + // 一番赏:每种奖品 = 1个格位 + totalSlots, _ := r.ActivityRewardSettings.WithContext(ctx).Where(r.ActivityRewardSettings.IssueID.Eq(iss)).Count() + + soldSlots, _ := r.IssuePositionClaims.WithContext(ctx).Where(r.IssuePositionClaims.IssueID.Eq(iss)).Count() + + fmt.Printf("[定时开奖-一番赏] 检查售罄 IssueID=%d Sold=%d Total=%d\n", iss, soldSlots, totalSlots) + + if soldSlots < totalSlots { + fmt.Printf("[定时开奖-一番赏] ❌ IssueID=%d 未售罄,执行全额退款\n", iss) + refundedIssues[iss] = true + + // Find ALL valid orders for this issue + issueOrders, _ := r.Orders.WithContext(ctx).Where( + r.Orders.Status.Eq(2), + r.Orders.SourceType.Eq(2), + r.Orders.Remark.Like(fmt.Sprintf("%%issue:%d|%%", iss)), + ).Find() + + for _, o := range issueOrders { + refundOrder(ctx, o, "ichiban_not_sold_out", wc, r, w, us, a.RefundCouponID) } } } + } + + shouldRefund := false + if a.PlayType != "ichiban" { + if count < a.MinParticipants { + shouldRefund = true + } + } + + if shouldRefund { + fmt.Printf("[定时开奖] 活动ID=%d ❌ 人数不足,进行退款处理\n", aid) + for _, o := range orders { + refundOrder(ctx, o, "scheduled_not_enough", wc, r, w, us, a.RefundCouponID) + } } else { - fmt.Printf("[定时开奖] 活动ID=%d ✅ 人数满足,开始开奖处理\n", aid) + fmt.Printf("[定时开奖] 活动ID=%d ✅ 人数满足(或一番赏模式),开始开奖处理\n", aid) if a.PlayType == "ichiban" { fmt.Printf("[定时开奖] 活动ID=%d 一番赏模式开奖,订单数=%d\n", aid, len(orders)) // 一番赏定时开奖逻辑 ichibanSel := strat.NewIchiban(r, w) for _, o := range orders { - uid := o.UserID iss := extractIssueID(o.Remark) + + if refundedIssues[iss] { + fmt.Printf("[定时开奖-一番赏] OrderID=%d IssueID=%d 已退款,跳过开奖\n", o.ID, iss) + continue + } + + uid := o.UserID fmt.Printf("[定时开奖-一番赏] 处理订单 OrderID=%d UserID=%d IssueID=%d\n", o.ID, uid, iss) // 检查是否已经处理过 @@ -177,6 +227,7 @@ func StartScheduledSettlement(l logger.CustomLogger, repo mysql.Repo) { } // 使用 claim 中的 slot_index 直接获取奖品 + // Use Commitment (via SelectItemBySlot internal logic) rid, proof, err := ichibanSel.SelectItemBySlot(ctx, aid, iss, claim.SlotIndex) if err != nil || rid <= 0 { fmt.Printf("[定时开奖-一番赏] ❌ SelectItemBySlot失败 err=%v rid=%d\n", err, rid) @@ -234,11 +285,12 @@ func StartScheduledSettlement(l logger.CustomLogger, repo mysql.Repo) { return act.Name } return "活动" - }()) + }(), "ichiban") } } else { // 默认玩法逻辑 sel := strat.NewDefault(r, w) + // Daily Seed removed for _, o := range orders { uid := o.UserID iss := extractIssueID(o.Remark) @@ -272,14 +324,14 @@ func StartScheduledSettlement(l logger.CustomLogger, repo mysql.Repo) { fmt.Printf("[定时开奖-默认] ✅ 保存凭据成功 DrawLogID=%d IssueID=%d\n", drawLog.ID, iss) } } - // 【开奖后虚拟发货】定时开奖后上传虚拟发货 + // 【开奖后虚拟发货】定时开奖后上传虚拟发货(非一番赏不发通知) uploadVirtualShippingForScheduledDraw(ctx, r, o.ID, o.OrderNo, uid, func() string { act, _ := r.Activities.WithContext(ctx).Where(r.Activities.ID.Eq(aid)).First() if act != nil { return act.Name } return "活动" - }()) + }(), "default") } } } @@ -299,7 +351,9 @@ func StartScheduledSettlement(l logger.CustomLogger, repo mysql.Repo) { } // 即时开奖:处理所有已支付且未记录抽奖日志的订单 - var instantActs []struct{ ID int64 } + var instantActs []struct { + ID int64 + } _ = repo.GetDbR().WithContext(ctx).Raw("SELECT id FROM activities WHERE draw_mode='instant'").Scan(&instantActs) if len(instantActs) > 0 { sel2 := strat.NewDefault(r, w) @@ -309,6 +363,7 @@ func StartScheduledSettlement(l logger.CustomLogger, repo mysql.Repo) { r.Orders.SourceType.Eq(2), r.Orders.Remark.Like(fmt.Sprintf("lottery:activity:%d|%%", ia.ID)), ).Find() + // Daily Seed removed for _, o2 := range orders2 { uid := o2.UserID iss := extractIssueID(o2.Remark) @@ -401,7 +456,8 @@ func extractCount(remark string) int64 { // uploadVirtualShippingForScheduledDraw 定时开奖后上传虚拟发货 // 收集中奖产品名称并调用微信虚拟发货API -func uploadVirtualShippingForScheduledDraw(ctx context.Context, r *dao.Query, orderID int64, orderNo string, userID int64, actName string) { +// playType: 活动玩法类型,只有 ichiban 时才发送开奖结果通知 +func uploadVirtualShippingForScheduledDraw(ctx context.Context, r *dao.Query, orderID int64, orderNo string, userID int64, actName string, playType string) { // 获取开奖记录 drawLogs, _ := r.ActivityDrawLogs.WithContext(ctx).Where(r.ActivityDrawLogs.OrderID.Eq(orderID)).Find() if len(drawLogs) == 0 { @@ -438,11 +494,83 @@ func uploadVirtualShippingForScheduledDraw(ctx context.Context, r *dao.Query, or if err := wechat.UploadVirtualShippingForBackground(ctx, cfg, tx.TransactionID, orderNo, payerOpenid, itemsDesc); err != nil { fmt.Printf("[定时开奖-虚拟发货] 上传失败: %v\n", err) } - // 【定时开奖后推送通知】 - notifyCfg := ¬ify.WechatNotifyConfig{ - AppID: c.Wechat.AppID, - AppSecret: c.Wechat.AppSecret, - LotteryResultTemplateID: c.Wechat.LotteryResultTemplateID, + // 【定时开奖后推送通知】只有一番赏才发送 + if playType == "ichiban" { + notifyCfg := ¬ify.WechatNotifyConfig{ + AppID: c.Wechat.AppID, + AppSecret: c.Wechat.AppSecret, + LotteryResultTemplateID: c.Wechat.LotteryResultTemplateID, + } + _ = notify.SendLotteryResultNotification(ctx, notifyCfg, payerOpenid, actName, rewardNames, orderNo, time.Now()) + } +} + +func refundOrder(ctx context.Context, o *model.Orders, reason string, wc *paypkg.WechatPayClient, r *dao.Query, w *dao.Query, us usersvc.Service, refundCouponID int64) { + // 1. Refund Points + if o.PointsAmount > 0 { + refundPts := o.PointsAmount / 100 + _, _ = us.RefundPoints(ctx, o.UserID, refundPts, o.OrderNo, reason) + } + + // 2. Refund WeChat + if o.ActualAmount > 0 && wc != nil { + refundNo := fmt.Sprintf("R%s-%d", o.OrderNo, time.Now().Unix()) + refundID, status, err := wc.RefundOrder(ctx, o.OrderNo, refundNo, o.ActualAmount, o.ActualAmount, reason) + if err == nil { + _ = w.PaymentRefunds.WithContext(ctx).Create(&model.PaymentRefunds{ + OrderID: o.ID, + OrderNo: o.OrderNo, + RefundNo: refundNo, + Channel: "wechat_jsapi", + Status: status, + AmountRefund: o.ActualAmount, + Reason: reason, + SuccessTime: time.Now(), + }) + _ = w.UserPointsLedger.WithContext(ctx).Create(&model.UserPointsLedger{UserID: o.UserID, Action: "refund_amount", Points: o.ActualAmount / 100, RefTable: "payment_refund", RefID: refundID}) + } else { + fmt.Printf("[Refund] WeChat refund failed for order %s: %v\n", o.OrderNo, err) + } + } + + // 3. Refund Used Coupons + ocs, _ := r.OrderCoupons.WithContext(ctx).Where(r.OrderCoupons.OrderID.Eq(o.ID)).Find() + for _, oc := range ocs { + // Restore user coupon status to 1 (Unused) and clear usage info + _, err := w.UserCoupons.WithContext(ctx).Where(w.UserCoupons.ID.Eq(oc.UserCouponID)).Updates(map[string]interface{}{ + "status": 1, + "used_order_id": 0, + "used_at": nil, + }) + if err != nil { + fmt.Printf("[Refund] Failed to restore coupon %d for order %s: %v\n", oc.UserCouponID, o.OrderNo, err) + } else { + fmt.Printf("[Refund] Restored coupon %d for order %s\n", oc.UserCouponID, o.OrderNo) + } + } + + // 3.5. 一番赏退款:删除 issue_position_claims 记录,恢复格位 + iss := extractIssueID(o.Remark) + if iss > 0 { + result, err := w.IssuePositionClaims.WithContext(ctx).Where( + w.IssuePositionClaims.OrderID.Eq(o.ID), + ).Delete() + if err != nil { + fmt.Printf("[Refund] Failed to delete position claims for order %d: %v\n", o.ID, err) + } else if result.RowsAffected > 0 { + fmt.Printf("[Refund] ✅ Restored %d slot position(s) for order %d issue %d\n", result.RowsAffected, o.ID, iss) + } + } + + // 4. Update Order Status + _, _ = w.Orders.WithContext(ctx).Where(w.Orders.ID.Eq(o.ID)).Updates(map[string]any{w.Orders.Status.ColumnName().String(): 4}) + + // 5. Log Refund + iss = extractIssueID(o.Remark) + _ = w.Orders.WithContext(ctx).UnderlyingDB().Exec("INSERT INTO lottery_refund_logs(issue_id, order_id, user_id, amount, coupon_type, coupon_amount, reason, status) VALUES(?,?,?,?,?,?,?,?)", iss, o.ID, o.UserID, o.ActualAmount, "", 0, reason, "done").Error + + // 6. Compensation + if refundCouponID > 0 { + _ = us.AddCoupon(ctx, o.UserID, refundCouponID) } - _ = notify.SendLotteryResultNotification(ctx, notifyCfg, payerOpenid, actName, rewardNames, orderNo, time.Now()) } diff --git a/internal/service/activity/strategy/default.go b/internal/service/activity/strategy/default.go index 78adfad..ecfcc05 100644 --- a/internal/service/activity/strategy/default.go +++ b/internal/service/activity/strategy/default.go @@ -43,15 +43,30 @@ func (s *defaultStrategy) SelectItem(ctx context.Context, activityID int64, issu return 0, nil, errors.New("no weight") } - // 使用 crypto/rand 生成加密安全的随机种子 - seed := make([]byte, 32) - if _, err := rand.Read(seed); err != nil { - return 0, nil, errors.New("crypto rand failed") + // Determine seed key: use Activity Commitment + var seedKey []byte + // Fallback to Activity Commitment if possible + // We need to fetch activity first + act, _ := s.read.Activities.WithContext(ctx).Where(s.read.Activities.ID.Eq(activityID)).First() + if act != nil && len(act.CommitmentSeedMaster) > 0 { + seedKey = act.CommitmentSeedMaster + } else { + // Absolute fallback if no commitment found (shouldn't happen in strict mode, but safe for dev) + seedKey = make([]byte, 32) + if _, err := rand.Read(seedKey); err != nil { + return 0, nil, errors.New("crypto rand failed") + } } - // 使用 HMAC-SHA256 生成加密安全的随机数 - mac := hmac.New(sha256.New, seed) - mac.Write([]byte(fmt.Sprintf("draw:issue:%d|user:%d", issueID, userID))) + // To ensure uniqueness per draw when using a fixed CommitmentSeedMaster, mix in a random salt + salt := make([]byte, 16) + if _, err := rand.Read(salt); err != nil { + return 0, nil, errors.New("crypto rand salt failed") + } + + // Use HMAC-SHA256 to generate random number derived from seed + context + salt + mac := hmac.New(sha256.New, seedKey) + mac.Write([]byte(fmt.Sprintf("draw:issue:%d|user:%d|salt:%x", issueID, userID, salt))) sum := mac.Sum(nil) rnd := int64(binary.BigEndian.Uint64(sum[:8]) % uint64(total)) @@ -70,7 +85,14 @@ func (s *defaultStrategy) SelectItem(ctx context.Context, activityID int64, issu if picked == 0 { return 0, nil, errors.New("pick failed") } - proof := map[string]any{"weights_total": total, "rand": rnd, "seed_hash": fmt.Sprintf("%x", sha256.Sum256(seed))} + + proof := map[string]any{ + "weights_total": total, + "rand": rnd, + "seed_hash": fmt.Sprintf("%x", sha256.Sum256(seedKey)), + "salt": fmt.Sprintf("%x", salt), + "seed_type": "commitment", + } return picked, proof, nil } diff --git a/internal/service/activity/strategy/ichiban.go b/internal/service/activity/strategy/ichiban.go index 47dc590..4844693 100644 --- a/internal/service/activity/strategy/ichiban.go +++ b/internal/service/activity/strategy/ichiban.go @@ -32,27 +32,23 @@ func (s *ichibanStrategy) SelectItemBySlot(ctx context.Context, activityID int64 if err != nil || len(rewards) == 0 { return 0, nil, errors.New("no rewards") } - var totalSlots int64 - for _, r := range rewards { - if r.OriginalQty > 0 { - totalSlots += r.OriginalQty - } - } + // 一番赏:每种奖品 = 1个格位,无数量概念 + totalSlots := int64(len(rewards)) if totalSlots <= 0 { return 0, nil, errors.New("no slots") } if slotIndex < 0 || slotIndex >= totalSlots { return 0, nil, errors.New("slot out of range") } - // build list - slots := make([]int64, 0, totalSlots) - for _, r := range rewards { - for i := int64(0); i < r.OriginalQty; i++ { - slots = append(slots, r.ID) - } + // build list: 每个reward直接对应一个slot + slots := make([]int64, totalSlots) + for i, r := range rewards { + slots[i] = r.ID } - // deterministic shuffle by server seed - mac := hmac.New(sha256.New, act.CommitmentSeedMaster) + // deterministic shuffle by CommitmentSeedMaster + seedKey := act.CommitmentSeedMaster + + mac := hmac.New(sha256.New, seedKey) for i := int(totalSlots - 1); i > 0; i-- { mac.Reset() mac.Write([]byte(fmt.Sprintf("shuffle:%d|issue:%d", i, issueID))) @@ -63,27 +59,21 @@ func (s *ichibanStrategy) SelectItemBySlot(ctx context.Context, activityID int64 picked := slots[slotIndex] // Calculate seed hash for proof - sha := sha256.Sum256(act.CommitmentSeedMaster) + sha := sha256.Sum256(seedKey) seedHash := fmt.Sprintf("%x", sha) proof := map[string]any{ "total_slots": totalSlots, "slot_index": slotIndex, "seed_hash": seedHash, + "seed_type": "commitment", } return picked, proof, nil } func (s *ichibanStrategy) GrantReward(ctx context.Context, userID int64, rewardID int64) error { - result, err := s.write.ActivityRewardSettings.WithContext(ctx).Where( - s.write.ActivityRewardSettings.ID.Eq(rewardID), - s.write.ActivityRewardSettings.Quantity.Gt(0), - ).UpdateSimple(s.write.ActivityRewardSettings.Quantity.Add(-1)) - if err != nil { - return err - } - if result.RowsAffected == 0 { - return errors.New("sold out or reward not found") - } + // 一番赏模式下不再需要扣减数量,因为每个奖品对应唯一格位 + // 格位占用通过 issue_position_claims 表来追踪,而非 quantity 字段 + // 这里保留接口兼容性,实际的占用检查在调用方完成 return nil } diff --git a/internal/service/activity/strategy/ichiban_test.go b/internal/service/activity/strategy/ichiban_test.go index 4d3f096..0fbf8d5 100644 --- a/internal/service/activity/strategy/ichiban_test.go +++ b/internal/service/activity/strategy/ichiban_test.go @@ -2,6 +2,8 @@ package strategy import ( "context" + "crypto/sha256" + "fmt" "testing" "gorm.io/driver/sqlite" "gorm.io/gorm" @@ -85,3 +87,56 @@ func TestIchibanGrantReward_Decrement(t *testing.T) { got, _ := q.ActivityRewardSettings.Where(q.ActivityRewardSettings.ID.Eq(r.ID)).First() if got.Quantity != 1 { t.Fatalf("quantity not decremented: %d", got.Quantity) } } + +func TestIchibanProofHasSeedHash(t *testing.T) { + // 1. Setup In-Memory DB + db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) + if err != nil { + t.Fatalf("db open err: %v", err) + } + + // 2. Migrate Tables + db.Exec(`CREATE TABLE activities (id INTEGER PRIMARY KEY AUTOINCREMENT, commitment_seed_master BLOB, commitment_state_version INTEGER, deleted_at DATETIME);`) + db.Exec(`CREATE TABLE activity_reward_settings (id INTEGER PRIMARY KEY AUTOINCREMENT, created_at DATETIME, updated_at DATETIME, issue_id INTEGER NOT NULL, product_id INTEGER, name TEXT NOT NULL, weight INTEGER NOT NULL, quantity INTEGER NOT NULL, original_qty INTEGER NOT NULL, level INTEGER NOT NULL, sort INTEGER NOT NULL, is_boss INTEGER NOT NULL, deleted_at DATETIME);`) + + q := dao.Use(db) + + // 3. Insert Test Data + seedBytes := []byte("testseedvalue") + expectedHash := fmt.Sprintf("%x", sha256.Sum256(seedBytes)) + + db.Exec("INSERT INTO activities (id, commitment_seed_master, commitment_state_version) VALUES (?, ?, ?)", 100, seedBytes, 1) + + r1 := &model.ActivityRewardSettings{IssueID: 10, Name: "A", Weight: 10, Quantity: 10, OriginalQty: 5, Level: 1, Sort: 1} + q.ActivityRewardSettings.Create(r1) + + // 4. Test SelectItemBySlot + s := NewIchiban(q, q) + ctx := context.Background() + + _, proof, err := s.SelectItemBySlot(ctx, 100, 10, 0) + if err != nil { + t.Fatalf("SelectItemBySlot failed: %v", err) + } + + // 5. Verify seed_hash is in proof + if proof == nil { + t.Fatal("proof is nil") + } + + val, ok := proof["seed_hash"] + if !ok { + t.Fatal("seed_hash missing from proof") + } + + seedHash, ok := val.(string) + if !ok { + t.Fatalf("seed_hash is not a string, got %T", val) + } + + if seedHash != expectedHash { + t.Fatalf("seed_hash mismatch. got %s, want %s", seedHash, expectedHash) + } + + t.Logf("Success: seed_hash found in proof: %s", seedHash) +} diff --git a/internal/service/activity/strategy/ichiban_verify_placeholder.go b/internal/service/activity/strategy/ichiban_verify_placeholder.go deleted file mode 100644 index 2eb0e6b..0000000 --- a/internal/service/activity/strategy/ichiban_verify_placeholder.go +++ /dev/null @@ -1,9 +0,0 @@ -package strategy - -// Mock objects for testing (simplified) -// Since we can't easily mock the full DAO chain without a comprehensive mock library or interface, -// we will rely on checking the logic I just added: manual inspection of the code change was logically sound. -// However, to be thorough, I'll write a test that mocks the DB interactions if possible, or use the existing test file `ichiban_test.go` as a base. - -// Let's assume `ichiban_test.go` exists (I saw it in the file list earlier). -// I will read it first to see if I can add a case there. diff --git a/internal/service/activity/strategy/ichiban_verify_test.go b/internal/service/activity/strategy/ichiban_verify_test.go deleted file mode 100644 index a35631b..0000000 --- a/internal/service/activity/strategy/ichiban_verify_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package strategy - -import ( - "bindbox-game/internal/repository/mysql/dao" - "bindbox-game/internal/repository/mysql/model" - "context" - "crypto/sha256" - "fmt" - "testing" - - "gorm.io/driver/sqlite" - "gorm.io/gorm" -) - -func TestIchibanProofHasSeedHash(t *testing.T) { - // 1. Setup In-Memory DB - db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) - if err != nil { - t.Fatalf("db open err: %v", err) - } - - // 2. Migrate Tables - db.Exec(`CREATE TABLE activities (id INTEGER PRIMARY KEY AUTOINCREMENT, commitment_seed_master BLOB, commitment_state_version INTEGER, deleted_at DATETIME);`) - db.Exec(`CREATE TABLE activity_reward_settings (id INTEGER PRIMARY KEY AUTOINCREMENT, created_at DATETIME, updated_at DATETIME, issue_id INTEGER NOT NULL, product_id INTEGER, name TEXT NOT NULL, weight INTEGER NOT NULL, quantity INTEGER NOT NULL, original_qty INTEGER NOT NULL, level INTEGER NOT NULL, sort INTEGER NOT NULL, is_boss INTEGER NOT NULL, deleted_at DATETIME);`) - - q := dao.Use(db) - - // 3. Insert Test Data - seedBytes := []byte("testseedvalue") - expectedHash := fmt.Sprintf("%x", sha256.Sum256(seedBytes)) - - db.Exec("INSERT INTO activities (id, commitment_seed_master, commitment_state_version) VALUES (?, ?, ?)", 100, seedBytes, 1) - - r1 := &model.ActivityRewardSettings{IssueID: 10, Name: "A", Weight: 10, Quantity: 10, OriginalQty: 5, Level: 1, Sort: 1} - q.ActivityRewardSettings.Create(r1) - - // 4. Test SelectItemBySlot - s := NewIchiban(q, q) - ctx := context.Background() - - _, proof, err := s.SelectItemBySlot(ctx, 100, 10, 0) - if err != nil { - t.Fatalf("SelectItemBySlot failed: %v", err) - } - - // 5. Verify seed_hash is in proof - if proof == nil { - t.Fatal("proof is nil") - } - - val, ok := proof["seed_hash"] - if !ok { - t.Fatal("seed_hash missing from proof") - } - - seedHash, ok := val.(string) - if !ok { - t.Fatalf("seed_hash is not a string, got %T", val) - } - - if seedHash != expectedHash { - t.Fatalf("seed_hash mismatch. got %s, want %s", seedHash, expectedHash) - } - - t.Logf("Success: seed_hash found in proof: %s", seedHash) -} diff --git a/internal/service/task_center/cache.go b/internal/service/task_center/cache.go new file mode 100644 index 0000000..42cf6c8 --- /dev/null +++ b/internal/service/task_center/cache.go @@ -0,0 +1,48 @@ +package taskcenter + +import ( + tcmodel "bindbox-game/internal/repository/mysql/task_center" + "context" + "encoding/json" + "time" + + "gorm.io/gorm" +) + +const activeTasksCacheKey = "task_center:active_tasks" + +func (s *service) invalidateCache(ctx context.Context) error { + if s.redis == nil { + return nil + } + return s.redis.Del(ctx, activeTasksCacheKey).Err() +} + +func (s *service) getActiveTasks(ctx context.Context) ([]tcmodel.Task, error) { + // 1. Try Redis + if s.redis != nil { + val, err := s.redis.Get(ctx, activeTasksCacheKey).Result() + if err == nil { + var tasks []tcmodel.Task + if err := json.Unmarshal([]byte(val), &tasks); err == nil { + return tasks, nil + } + } + } + + // 2. Fallback to DB + var tasks []tcmodel.Task + if err := s.repo.GetDbR().Preload("Tiers", func(db *gorm.DB) *gorm.DB { + return db.Order("priority asc, id asc") + }).Preload("Rewards").Where("status=1").Find(&tasks).Error; err != nil { + return nil, err + } + + // 3. Write back to Redis + if s.redis != nil && len(tasks) > 0 { + b, _ := json.Marshal(tasks) + _ = s.redis.Set(ctx, activeTasksCacheKey, string(b), 1*time.Hour).Err() + } + + return tasks, nil +} diff --git a/internal/service/task_center/constants.go b/internal/service/task_center/constants.go new file mode 100644 index 0000000..8b950f7 --- /dev/null +++ b/internal/service/task_center/constants.go @@ -0,0 +1,33 @@ +package taskcenter + +const ( + // Task Windows + WindowDaily = "daily" + WindowWeekly = "weekly" + WindowInfinite = "infinite" + + // Task Metrics + MetricFirstOrder = "first_order" + MetricOrderCount = "order_count" + MetricInviteCount = "invite_count" + MetricEffectiveInviteCount = "effective_invite_count" + + // Operators + OperatorGTE = ">=" + OperatorEQ = "=" + + // Reward Types + RewardTypePoints = "points" + RewardTypeCoupon = "coupon" + RewardTypeItemCard = "item_card" + RewardTypeTitle = "title" + + // Event Sources + SourceTypeOrder = "order" + SourceTypeInvite = "invite" + SourceTypeEffectiveInvite = "effective_invite" + SourceTypeTaskCenter = "task_center" + + // Event Log Status + EventStatusGranted = "granted" +) diff --git a/internal/service/task_center/service.go b/internal/service/task_center/service.go index 120310d..19c30f2 100644 --- a/internal/service/task_center/service.go +++ b/internal/service/task_center/service.go @@ -1,6 +1,7 @@ package taskcenter import ( + "bindbox-game/internal/pkg/async" "bindbox-game/internal/pkg/logger" "bindbox-game/internal/repository/mysql" "bindbox-game/internal/repository/mysql/dao" @@ -14,6 +15,8 @@ import ( titlesvc "bindbox-game/internal/service/title" usersvc "bindbox-game/internal/service/user" + "github.com/redis/go-redis/v9" + "go.uber.org/zap" "gorm.io/datatypes" "gorm.io/gorm" "gorm.io/gorm/clause" @@ -32,6 +35,7 @@ type Service interface { ClaimTier(ctx context.Context, userID int64, taskID int64, tierID int64) error OnOrderPaid(ctx context.Context, userID int64, orderID int64) error OnInviteSuccess(ctx context.Context, inviterID int64, inviteeID int64) error + StartWorker(ctx context.Context) } type service struct { @@ -39,18 +43,26 @@ type service struct { readDB *dao.Query writeDB *dao.Query repo mysql.Repo + redis *redis.Client + queue async.TaskQueue userSvc usersvc.Service titleSvc titlesvc.Service } -func New(l logger.CustomLogger, db mysql.Repo) Service { +func New(l logger.CustomLogger, db mysql.Repo, rdb *redis.Client, userSvc usersvc.Service, titleSvc titlesvc.Service) Service { + var q async.TaskQueue + if rdb != nil { + q = async.NewRedisTaskQueue(rdb) + } return &service{ logger: l, readDB: dao.Use(db.GetDbR()), writeDB: dao.Use(db.GetDbW()), repo: db, - userSvc: usersvc.New(l, db), - titleSvc: titlesvc.New(l, db), + redis: rdb, + queue: q, + userSvc: userSvc, + titleSvc: titleSvc, } } @@ -99,22 +111,24 @@ type ModifyTaskInput struct { } type TaskTierInput struct { - Metric string - Operator string - Threshold int64 - Window string - Repeatable int32 - Priority int32 + Metric string + Operator string + Threshold int64 + Window string + Repeatable int32 + Priority int32 + ExtraParams datatypes.JSON } type TaskTierItem struct { - ID int64 `json:"id"` - Metric string `json:"metric"` - Operator string `json:"operator"` - Threshold int64 `json:"threshold"` - Window string `json:"window"` - Repeatable int32 `json:"repeatable"` - Priority int32 `json:"priority"` + ID int64 `json:"id"` + Metric string `json:"metric"` + Operator string `json:"operator"` + Threshold int64 `json:"threshold"` + Window string `json:"window"` + Repeatable int32 `json:"repeatable"` + Priority int32 `json:"priority"` + ExtraParams datatypes.JSON `json:"extra_params"` } type TaskRewardInput struct { @@ -145,10 +159,14 @@ func (s *service) ListTasks(ctx context.Context, in ListTasksInput) (items []Tas if err = q.Count(&total).Error; err != nil { return nil, 0, err } - if err = q.Offset((in.Page - 1) * in.PageSize).Limit(in.PageSize).Order("id desc").Find(&rows).Error; err != nil { + if err = q.Preload("Tiers", func(db *gorm.DB) *gorm.DB { + return db.Order("priority asc, id asc") + }).Preload("Rewards", func(db *gorm.DB) *gorm.DB { + return db.Order("id asc") + }).Offset((in.Page - 1) * in.PageSize).Limit(in.PageSize).Order("id desc").Find(&rows).Error; err != nil { return nil, 0, err } - items = make([]TaskItem, len(rows)) + out := make([]TaskItem, len(rows)) for i, v := range rows { var st, et int64 if v.StartTime != nil { @@ -157,17 +175,19 @@ func (s *service) ListTasks(ctx context.Context, in ListTasksInput) (items []Tas if v.EndTime != nil { et = v.EndTime.Unix() } - items[i] = TaskItem{ID: v.ID, Name: v.Name, Description: v.Description, Status: v.Status, StartTime: st, EndTime: et, Visibility: v.Visibility} + out[i] = TaskItem{ID: v.ID, Name: v.Name, Description: v.Description, Status: v.Status, StartTime: st, EndTime: et, Visibility: v.Visibility} // 填充 Tiers - if tiers, err := s.ListTaskTiers(ctx, v.ID); err == nil { - items[i].Tiers = tiers + out[i].Tiers = make([]TaskTierItem, len(v.Tiers)) + for j, t := range v.Tiers { + out[i].Tiers[j] = TaskTierItem{ID: t.ID, Metric: t.Metric, Operator: t.Operator, Threshold: t.Threshold, Window: t.Window, Repeatable: t.Repeatable, Priority: t.Priority, ExtraParams: t.ExtraParams} } // 填充 Rewards - if rewards, err := s.ListTaskRewards(ctx, v.ID); err == nil { - items[i].Rewards = rewards + out[i].Rewards = make([]TaskRewardItem, len(v.Rewards)) + for j, r := range v.Rewards { + out[i].Rewards[j] = TaskRewardItem{ID: r.ID, TierID: r.TierID, RewardType: r.RewardType, RewardPayload: r.RewardPayload, Quantity: r.Quantity} } } - return items, total, nil + return out, total, nil } func (s *service) GetUserProgress(ctx context.Context, userID int64, taskID int64) (*UserProgress, error) { @@ -211,17 +231,23 @@ func (s *service) CreateTask(ctx context.Context, in CreateTaskInput) (int64, er if err := db.Create(row).Error; err != nil { return 0, err } - return row.ID, nil + return row.ID, s.invalidateCache(ctx) } func (s *service) ModifyTask(ctx context.Context, id int64, in ModifyTaskInput) error { db := s.repo.GetDbW() - return db.Model(&tcmodel.Task{}).Where("id=?", id).Updates(map[string]any{"name": in.Name, "description": in.Description, "status": in.Status, "start_time": in.StartTime, "end_time": in.EndTime, "visibility": in.Visibility}).Error + if err := db.Model(&tcmodel.Task{}).Where("id=?", id).Updates(map[string]any{"name": in.Name, "description": in.Description, "status": in.Status, "start_time": in.StartTime, "end_time": in.EndTime, "visibility": in.Visibility}).Error; err != nil { + return err + } + return s.invalidateCache(ctx) } func (s *service) DeleteTask(ctx context.Context, id int64) error { db := s.repo.GetDbW() - return db.Where("id=?", id).Delete(&tcmodel.Task{}).Error + if err := db.Where("id=?", id).Delete(&tcmodel.Task{}).Error; err != nil { + return err + } + return s.invalidateCache(ctx) } func (s *service) ListTaskTiers(ctx context.Context, taskID int64) ([]TaskTierItem, error) { @@ -232,7 +258,7 @@ func (s *service) ListTaskTiers(ctx context.Context, taskID int64) ([]TaskTierIt } out := make([]TaskTierItem, len(rows)) for i, v := range rows { - out[i] = TaskTierItem{ID: v.ID, Metric: v.Metric, Operator: v.Operator, Threshold: v.Threshold, Window: v.Window, Repeatable: v.Repeatable, Priority: v.Priority} + out[i] = TaskTierItem{ID: v.ID, Metric: v.Metric, Operator: v.Operator, Threshold: v.Threshold, Window: v.Window, Repeatable: v.Repeatable, Priority: v.Priority, ExtraParams: v.ExtraParams} } return out, nil } @@ -243,12 +269,12 @@ func (s *service) UpsertTaskTiers(ctx context.Context, taskID int64, tiers []Tas return err } for _, t := range tiers { - row := &tcmodel.TaskTier{TaskID: taskID, Metric: t.Metric, Operator: t.Operator, Threshold: t.Threshold, Window: t.Window, Repeatable: t.Repeatable, Priority: t.Priority} + row := &tcmodel.TaskTier{TaskID: taskID, Metric: t.Metric, Operator: t.Operator, Threshold: t.Threshold, Window: t.Window, Repeatable: t.Repeatable, Priority: t.Priority, ExtraParams: t.ExtraParams} if err := db.Create(row).Error; err != nil { return err } } - return nil + return s.invalidateCache(ctx) } func (s *service) ListTaskRewards(ctx context.Context, taskID int64) ([]TaskRewardItem, error) { @@ -275,15 +301,70 @@ func (s *service) UpsertTaskRewards(ctx context.Context, taskID int64, rewards [ return err } } - return nil + return s.invalidateCache(ctx) } func (s *service) OnOrderPaid(ctx context.Context, userID int64, orderID int64) error { - var tasks []tcmodel.Task - if err := s.repo.GetDbR().Where("status=1").Find(&tasks).Error; err != nil { + if s.queue != nil { + return s.queue.PublishOrderPaid(ctx, userID, orderID) + } + return s.processOrderPaid(ctx, userID, orderID) +} + +func (s *service) processOrderPaid(ctx context.Context, userID int64, orderID int64) error { + // 1. 获取订单金额 + ord, err := s.readDB.Orders.WithContext(ctx).Where(s.readDB.Orders.ID.Eq(orderID)).First() + if err != nil { + return err + } + amount := ord.ActualAmount + + // 2. 更新邀请人累计金额并检查是否触发有效邀请 + var inviterID int64 + var oldAmount int64 + var newAmount int64 + + // 使用事务更新 UserInvites + err = s.writeDB.Transaction(func(tx *dao.Query) error { + uInv, err := tx.UserInvites.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).Where(tx.UserInvites.InviteeID.Eq(userID)).First() + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil + } + return err + } + inviterID = uInv.InviterID + oldAmount = uInv.AccumulatedAmount + newAmount = oldAmount + amount + + updates := map[string]any{ + "accumulated_amount": newAmount, + } + _, err = tx.UserInvites.WithContext(ctx).Where(tx.UserInvites.ID.Eq(uInv.ID)).Updates(updates) + return err + }) + if err != nil { + return err + } + + // 3. 处理普通任务 + tasks, err := s.getActiveTasks(ctx) + if err != nil { return err } for _, t := range tasks { + // Filter tasks: Only process if it has order related metrics + hasOrderMetric := false + for _, tier := range t.Tiers { + if tier.Metric == MetricFirstOrder || tier.Metric == MetricOrderCount { + hasOrderMetric = true + break + } + } + if !hasOrderMetric { + continue + } + var p tcmodel.UserTaskProgress err := s.repo.GetDbW().Transaction(func(tx *gorm.DB) error { if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("user_id=? AND task_id=?", userID, t.ID).First(&p).Error; err != nil { @@ -293,7 +374,7 @@ func (s *service) OnOrderPaid(ctx context.Context, userID int64, orderID int64) } return err } - if err := s.checkAndResetDailyProgress(ctx, tx, t.ID, &p); err != nil { + if err := s.checkAndResetDailyProgress(ctx, tx, &t, &p); err != nil { return err } p.OrderCount++ @@ -303,21 +384,95 @@ func (s *service) OnOrderPaid(ctx context.Context, userID int64, orderID int64) return tx.Save(&p).Error }) if err != nil { - return err + s.logger.Error("failed to update progress", zap.Error(err)) + continue } if err := s.matchAndGrant(ctx, &t, &p, "order", orderID, fmt.Sprintf("ord:%d", orderID)); err != nil { - return err + s.logger.Error("failed to grant reward", zap.Error(err)) + } + } + + // 4. 处理邀请人任务 (有效邀请) + if inviterID > 0 { + for _, t := range tasks { + tiers := t.Tiers + // 检查该任务是否有 effective_invite_count 类型的 Tier,且是否刚好跨越阈值 + triggered := false + for _, tier := range tiers { + if tier.Metric == MetricEffectiveInviteCount { + var extra struct { + AmountThreshold int64 `json:"amount_threshold"` + } + if len(tier.ExtraParams) > 0 { + _ = json.Unmarshal([]byte(tier.ExtraParams), &extra) + } + threshold := extra.AmountThreshold + if threshold <= 0 { + threshold = 1 // 默认任意金额 + } + + // 如果之前的累计金额未达到阈值,而现在的累计金额达到了阈值,则触发 + if oldAmount < threshold && newAmount >= threshold { + triggered = true + break // 该任务触发一次即可 + } + } + } + + if triggered { + var pInv tcmodel.UserTaskProgress + err := s.repo.GetDbW().Transaction(func(tx *gorm.DB) error { + if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("user_id=? AND task_id=?", inviterID, t.ID).First(&pInv).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + pInv = tcmodel.UserTaskProgress{UserID: inviterID, TaskID: t.ID, EffectiveInviteCount: 1} + return tx.Create(&pInv).Error + } + return err + } + // 有效邀请通常不重置(除非是每日任务?一般是长期任务) + if err := s.checkAndResetDailyProgress(ctx, tx, &t, &pInv); err != nil { + return err + } + pInv.EffectiveInviteCount++ + return tx.Save(&pInv).Error + }) + if err == nil { + // 尝试发放奖励 + // sourceID 使用 userID (被邀请人ID),eventID 使用特殊前缀防止混淆 + _ = s.matchAndGrant(ctx, &t, &pInv, SourceTypeEffectiveInvite, userID, fmt.Sprintf("eff_inv:%d:%d", userID, t.ID)) + } else { + s.logger.Error("failed to update inviter progress", zap.Error(err)) + } + } } } return nil } func (s *service) OnInviteSuccess(ctx context.Context, inviterID int64, inviteeID int64) error { - var tasks []tcmodel.Task - if err := s.repo.GetDbR().Where("status=1").Find(&tasks).Error; err != nil { + if s.queue != nil { + return s.queue.PublishInviteSuccess(ctx, inviterID, inviteeID) + } + return s.processInviteSuccess(ctx, inviterID, inviteeID) +} + +func (s *service) processInviteSuccess(ctx context.Context, inviterID int64, inviteeID int64) error { + tasks, err := s.getActiveTasks(ctx) + if err != nil { return err } for _, t := range tasks { + hasInviteMetric := false + for _, tier := range t.Tiers { + if tier.Metric == MetricInviteCount { + hasInviteMetric = true + break + } + } + if !hasInviteMetric { + continue + } + var p tcmodel.UserTaskProgress err := s.repo.GetDbW().Transaction(func(tx *gorm.DB) error { if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("user_id=? AND task_id=?", inviterID, t.ID).First(&p).Error; err != nil { @@ -327,7 +482,7 @@ func (s *service) OnInviteSuccess(ctx context.Context, inviterID int64, inviteeI } return err } - if err := s.checkAndResetDailyProgress(ctx, tx, t.ID, &p); err != nil { + if err := s.checkAndResetDailyProgress(ctx, tx, &t, &p); err != nil { return err } p.InviteCount++ @@ -336,19 +491,31 @@ func (s *service) OnInviteSuccess(ctx context.Context, inviterID int64, inviteeI if err != nil { return err } - if err := s.matchAndGrant(ctx, &t, &p, "invite", inviteeID, fmt.Sprintf("inv:%d", inviteeID)); err != nil { + if err := s.matchAndGrant(ctx, &t, &p, SourceTypeInvite, inviteeID, fmt.Sprintf("inv:%d", inviteeID)); err != nil { return err } } return nil } -func (s *service) checkAndResetDailyProgress(ctx context.Context, tx *gorm.DB, taskID int64, p *tcmodel.UserTaskProgress) error { - var count int64 - if err := tx.Model(&tcmodel.TaskTier{}).Where("task_id = ? AND window = ?", taskID, "daily").Count(&count).Error; err != nil { - return err +func (s *service) checkAndResetDailyProgress(ctx context.Context, tx *gorm.DB, t *tcmodel.Task, p *tcmodel.UserTaskProgress) error { + isDaily := false + if len(t.Tiers) > 0 { + for _, tier := range t.Tiers { + if tier.Window == WindowDaily { + isDaily = true + break + } + } + } else { + var count int64 + if err := tx.Model(&tcmodel.TaskTier{}).Where("task_id = ? AND window = ?", t.ID, WindowDaily).Count(&count).Error; err != nil { + return err + } + isDaily = count > 0 } - if count == 0 { + + if !isDaily { return nil } now := time.Now() @@ -368,9 +535,11 @@ func (s *service) checkAndResetDailyProgress(ctx context.Context, tx *gorm.DB, t } func (s *service) matchAndGrant(ctx context.Context, t *tcmodel.Task, p *tcmodel.UserTaskProgress, sourceType string, sourceID int64, eventID string) error { - var tiers []tcmodel.TaskTier - if err := s.repo.GetDbR().Where("task_id=?", t.ID).Order("priority asc").Find(&tiers).Error; err != nil { - return err + tiers := t.Tiers + if len(tiers) == 0 { + if err := s.repo.GetDbR().Where("task_id=?", t.ID).Order("priority asc").Find(&tiers).Error; err != nil { + return err + } } var claimed []int64 if len(p.ClaimedTiers) > 0 { @@ -386,20 +555,26 @@ func (s *service) matchAndGrant(ctx context.Context, t *tcmodel.Task, p *tcmodel } hit := false switch tier.Metric { - case "first_order": + case MetricFirstOrder: hit = p.FirstOrder == 1 - case "order_count": - if tier.Operator == ">=" { + case MetricOrderCount: + if tier.Operator == OperatorGTE { hit = p.OrderCount >= tier.Threshold } else { hit = p.OrderCount == tier.Threshold } - case "invite_count": - if tier.Operator == ">=" { + case MetricInviteCount: + if tier.Operator == OperatorGTE { hit = p.InviteCount >= tier.Threshold } else { hit = p.InviteCount == tier.Threshold } + case MetricEffectiveInviteCount: + if tier.Operator == OperatorGTE { + hit = p.EffectiveInviteCount >= tier.Threshold + } else { + hit = p.EffectiveInviteCount == tier.Threshold + } } if !hit { continue diff --git a/internal/service/task_center/worker.go b/internal/service/task_center/worker.go new file mode 100644 index 0000000..66080ec --- /dev/null +++ b/internal/service/task_center/worker.go @@ -0,0 +1,80 @@ +package taskcenter + +import ( + "bindbox-game/internal/pkg/async" + "context" + "encoding/json" + "time" + + "go.uber.org/zap" +) + +func (s *service) StartWorker(ctx context.Context) { + if s.queue == nil { + s.logger.Info("Async queue not configured, worker not started") + return + } + s.logger.Info("Task center worker started") + + // Start multiple workers for concurrency + workerCount := 5 + for i := 0; i < workerCount; i++ { + go s.runWorkerLoop(ctx, i) + } +} + +func (s *service) runWorkerLoop(ctx context.Context, workerID int) { + defer func() { + if r := recover(); r != nil { + s.logger.Error("Task center worker panicked", zap.Any("recover", r), zap.Int("worker_id", workerID)) + // Restart worker after a short delay to prevent tight loops + time.Sleep(3 * time.Second) + go s.runWorkerLoop(ctx, workerID) + } + }() + + s.logger.Info("Worker routine started", zap.Int("worker_id", workerID)) + + for { + select { + case <-ctx.Done(): + s.logger.Info("Task center worker stopping", zap.Int("worker_id", workerID)) + return + default: + event, err := s.queue.Consume(ctx) + if err != nil { + s.logger.Error("Failed to consume event", zap.Error(err), zap.Int("worker_id", workerID)) + time.Sleep(1 * time.Second) + continue + } + if event == nil { + continue + } + + s.logger.Info("Processing event", zap.String("type", string(event.Type)), zap.Int("worker_id", workerID)) + + switch event.Type { + case async.EventTypeOrderPaid: + var pl async.OrderPaidPayload + if err := json.Unmarshal([]byte(event.Payload), &pl); err != nil { + s.logger.Error("Failed to unmarshal order paid payload", zap.Error(err)) + continue + } + if err := s.processOrderPaid(ctx, pl.UserID, pl.OrderID); err != nil { + s.logger.Error("Failed to process order paid", zap.Error(err)) + } + case async.EventTypeInviteSuccess: + var pl async.InviteSuccessPayload + if err := json.Unmarshal([]byte(event.Payload), &pl); err != nil { + s.logger.Error("Failed to unmarshal invite success payload", zap.Error(err)) + continue + } + if err := s.processInviteSuccess(ctx, pl.InviterID, pl.InviteeID); err != nil { + s.logger.Error("Failed to process invite success", zap.Error(err)) + } + default: + s.logger.Warn("Unknown event type", zap.String("type", string(event.Type))) + } + } + } +} diff --git a/internal/service/user/cancel_shipping.go b/internal/service/user/cancel_shipping.go new file mode 100644 index 0000000..d54c693 --- /dev/null +++ b/internal/service/user/cancel_shipping.go @@ -0,0 +1,46 @@ +package user + +import ( + "bindbox-game/internal/repository/mysql/dao" + "context" + "fmt" +) + +// CancelShipping 取消发货申请 +func (s *service) CancelShipping(ctx context.Context, userID int64, inventoryID int64) error { + // 1. 开启事务 + return s.writeDB.Transaction(func(tx *dao.Query) error { + // 2. 查询发货记录(必须是待发货状态 status=1) + sr, err := tx.ShippingRecords.WithContext(ctx). + Where(tx.ShippingRecords.InventoryID.Eq(inventoryID)). + Where(tx.ShippingRecords.UserID.Eq(userID)). + Where(tx.ShippingRecords.Status.Eq(1)). + First() + + if err != nil { + return fmt.Errorf("shipping record not found or already processed") + } + + // 3. 更新发货记录状态为已取消 (status=5) + if _, err := tx.ShippingRecords.WithContext(ctx). + Where(tx.ShippingRecords.ID.Eq(sr.ID)). + Update(tx.ShippingRecords.Status, 5); err != nil { + return err + } + + // 4. 恢复库存状态为可用 (status=1) + // 并追加备注 + // 使用原生SQL以确保CONCAT行为一致 + remark := fmt.Sprintf("|shipping_cancelled_by_user:%d", userID) + if err := tx.UserInventory.WithContext(ctx).UnderlyingDB().Exec( + "UPDATE user_inventory SET status=1, remark=CONCAT(IFNULL(remark,''), ?) WHERE id=? AND user_id=?", + remark, + inventoryID, + userID, + ).Error; err != nil { + return err + } + + return nil + }) +} diff --git a/internal/service/user/item_cards_list.go b/internal/service/user/item_cards_list.go index 442678a..6da92aa 100644 --- a/internal/service/user/item_cards_list.go +++ b/internal/service/user/item_cards_list.go @@ -14,6 +14,104 @@ type ItemCardWithTemplate struct { EffectType int32 `json:"effect_type"` StackingStrategy int32 `json:"stacking_strategy"` Remark string `json:"remark"` + Count int64 `json:"count"` +} + +// ListAggregatedUserItemCards 获取聚合后的用户道具卡列表(按卡种分组) +func (s *service) ListAggregatedUserItemCards(ctx context.Context, userID int64, status int32, page, pageSize int) (items []*ItemCardWithTemplate, total int64, err error) { + // 1. 计算分组总数 (Using UnderlyingDB for raw SQL flexibility) + var countResult []struct { + CardID int64 + Total int64 + } + + tx := s.readDB.UserItemCards.WithContext(ctx).ReadDB().UnderlyingDB(). + Model(&model.UserItemCards{}). + Where("user_id = ? AND status = ?", userID, status) + + // Count Distinct CardID + err = tx.Distinct("card_id").Count(&total).Error + if err != nil { + return nil, 0, err + } + + if page <= 0 { + page = 1 + } + if pageSize <= 0 { + pageSize = 20 + } + + // 2. 分页查询分组数据 + err = tx.Select("card_id, count(*) as total"). + Group("card_id"). + Order("card_id desc"). + Offset((page - 1) * pageSize). + Limit(pageSize). + Scan(&countResult).Error + if err != nil { + return nil, 0, err + } + + if len(countResult) == 0 { + return []*ItemCardWithTemplate{}, 0, nil + } + + // 3. 获取每组的一个实例(用于获取有效期等信息)和模板信息 + cardIDs := make([]int64, 0, len(countResult)) + for _, r := range countResult { + cardIDs = append(cardIDs, r.CardID) + } + + // 获取模板信息 + tpls := map[int64]*model.SystemItemCards{} + // Using GEN helpers for simple queries + tplList, err := s.readDB.SystemItemCards.WithContext(ctx).ReadDB().Where(s.readDB.SystemItemCards.ID.In(cardIDs...)).Find() + if err != nil { + return nil, 0, err + } + for _, t := range tplList { + tpls[t.ID] = t + } + + items = make([]*ItemCardWithTemplate, 0, len(countResult)) + for _, r := range countResult { + // Find latest instance using GEN helpers + instance, _ := s.readDB.UserItemCards.WithContext(ctx).ReadDB(). + Where(s.readDB.UserItemCards.UserID.Eq(userID), s.readDB.UserItemCards.Status.Eq(status), s.readDB.UserItemCards.CardID.Eq(r.CardID)). + Order(s.readDB.UserItemCards.ID.Desc()). + First() + + if instance == nil { + instance = &model.UserItemCards{UserID: userID, CardID: r.CardID, Status: status} + } + + tpl := tpls[r.CardID] + var name, remark string + var cardType, scopeType, effectType, stacking int32 + + if tpl != nil { + name = tpl.Name + cardType = tpl.CardType + scopeType = tpl.ScopeType + effectType = tpl.EffectType + stacking = tpl.StackingStrategy + remark = tpl.Remark + } + + items = append(items, &ItemCardWithTemplate{ + UserItemCards: instance, + Name: name, + CardType: cardType, + ScopeType: scopeType, + EffectType: effectType, + StackingStrategy: stacking, + Remark: remark, + Count: r.Total, + }) + } + + return items, total, nil } // ListUserItemCards 获取用户道具卡列表 @@ -115,6 +213,7 @@ func (s *service) ListUserItemCardsWithTemplate(ctx context.Context, userID int6 EffectType: effectType, StackingStrategy: stacking, Remark: remark, + Count: 1, // Individual record } } return items, total, nil @@ -184,6 +283,7 @@ func (s *service) ListUserItemCardsWithTemplateUsable(ctx context.Context, userI EffectType: effectType, StackingStrategy: stacking, Remark: remark, + Count: 1, } } return items, total, nil @@ -254,6 +354,7 @@ func (s *service) ListUserItemCardsWithTemplateByStatus(ctx context.Context, use EffectType: effectType, StackingStrategy: stacking, Remark: remark, + Count: 1, // Individual record } } return items, total, nil diff --git a/internal/service/user/orders_list.go b/internal/service/user/orders_list.go index 14fddc0..31f5420 100644 --- a/internal/service/user/orders_list.go +++ b/internal/service/user/orders_list.go @@ -43,6 +43,21 @@ type OrderWithItems struct { IsWinner bool `json:"is_winner"` RewardLevel int32 `json:"reward_level"` DrawReceipts []*DrawReceiptInfo `json:"draw_receipts"` + CouponInfo *CouponSimpleInfo `json:"coupon_info,omitempty"` + ItemCardInfo *ItemCardSimpleInfo `json:"item_card_info,omitempty"` +} + +type CouponSimpleInfo struct { + UserCouponID int64 `json:"user_coupon_id"` + Name string `json:"name"` + Type int32 `json:"type"` // 1:满减 2:折扣 + Value int64 `json:"value"` +} + +type ItemCardSimpleInfo struct { + UserCardID int64 `json:"user_card_id"` + Name string `json:"name"` + EffectType int32 `json:"effect_type"` } func (s *service) ListOrders(ctx context.Context, userID int64, page, pageSize int) (items []*model.Orders, total int64, err error) { @@ -123,6 +138,40 @@ func (s *service) GetOrderWithItems(ctx context.Context, userID int64, orderID i res.Items = items } + // 补充优惠券和道具卡信息 + if order.CouponID > 0 { + if uc, _ := s.readDB.UserCoupons.WithContext(ctx).ReadDB().Where(s.readDB.UserCoupons.ID.Eq(order.CouponID)).First(); uc != nil { + if sc, _ := s.readDB.SystemCoupons.WithContext(ctx).ReadDB().Where(s.readDB.SystemCoupons.ID.Eq(uc.CouponID)).First(); sc != nil { + val := sc.DiscountValue + // 尝试查询实际抵扣金额 + if oc, _ := s.readDB.OrderCoupons.WithContext(ctx).ReadDB().Where( + s.readDB.OrderCoupons.OrderID.Eq(order.ID), + s.readDB.OrderCoupons.UserCouponID.Eq(order.CouponID), + ).First(); oc != nil { + val = oc.AppliedAmount + } + + res.CouponInfo = &CouponSimpleInfo{ + UserCouponID: uc.ID, + Name: sc.Name, + Type: sc.DiscountType, + Value: val, + } + } + } + } + if order.ItemCardID > 0 { + if uc, _ := s.readDB.UserItemCards.WithContext(ctx).ReadDB().Where(s.readDB.UserItemCards.ID.Eq(order.ItemCardID)).First(); uc != nil { + if sc, _ := s.readDB.SystemItemCards.WithContext(ctx).ReadDB().Where(s.readDB.SystemItemCards.ID.Eq(uc.CardID)).First(); sc != nil { + res.ItemCardInfo = &ItemCardSimpleInfo{ + UserCardID: uc.ID, + Name: sc.Name, + EffectType: sc.EffectType, + } + } + } + } + // 补充开奖信息 logs, _ := s.readDB.ActivityDrawLogs.WithContext(ctx).ReadDB().Where(s.readDB.ActivityDrawLogs.OrderID.Eq(order.ID)).Find() if len(logs) > 0 { @@ -179,7 +228,7 @@ func (s *service) GetOrderWithItems(ctx context.Context, userID int64, orderID i ClientID: r.ClientID, Timestamp: r.Timestamp, ServerSeedHash: r.ServerSeedHash, - ServerSubSeed: r.ServerSubSeed, + ServerSubSeed: "", ClientSeed: r.ClientSeed, Nonce: r.Nonce, ItemsRoot: r.ItemsRoot, @@ -193,6 +242,11 @@ func (s *service) GetOrderWithItems(ctx context.Context, userID int64, orderID i } } + // Special handling for Ichiban Kuji: Do not show Item Card + if res.PlayType == "ichiban" { + res.ItemCardInfo = nil + } + return res, nil } @@ -356,12 +410,116 @@ func (s *service) ListOrdersWithItems(ctx context.Context, userID int64, status } } + // 批量查询优惠券和道具卡信息 + userCouponIDs := make([]int64, 0) + userItemCardIDs := make([]int64, 0) + for _, order := range orders { + if order.CouponID > 0 { + userCouponIDs = append(userCouponIDs, order.CouponID) + } + if order.ItemCardID > 0 { + userItemCardIDs = append(userItemCardIDs, order.ItemCardID) + } + } + + couponMap := make(map[int64]*CouponSimpleInfo) + // orderID -> userCouponID -> appliedAmount + appliedAmountMap := make(map[int64]map[int64]int64) + + if len(userCouponIDs) > 0 { + // 查询优惠券基本信息 + userCoupons, _ := s.readDB.UserCoupons.WithContext(ctx).ReadDB().Where(s.readDB.UserCoupons.ID.In(userCouponIDs...)).Find() + var sysCouponIDs []int64 + ucMap := make(map[int64]*model.UserCoupons) + for _, uc := range userCoupons { + sysCouponIDs = append(sysCouponIDs, uc.CouponID) + ucMap[uc.ID] = uc + } + if len(sysCouponIDs) > 0 { + sysCoupons, _ := s.readDB.SystemCoupons.WithContext(ctx).ReadDB().Where(s.readDB.SystemCoupons.ID.In(sysCouponIDs...)).Find() + scMap := make(map[int64]*model.SystemCoupons) + for _, sc := range sysCoupons { + scMap[sc.ID] = sc + } + for id, uc := range ucMap { + if sc, ok := scMap[uc.CouponID]; ok { + couponMap[id] = &CouponSimpleInfo{ + UserCouponID: uc.ID, + Name: sc.Name, + Type: sc.DiscountType, + Value: sc.DiscountValue, // 默认使用面值,后面会尝试用实际抵扣金额覆盖 + } + } + } + } + + // 查询订单实际使用的优惠券金额 + if len(orderIDs) > 0 { + ocs, _ := s.readDB.OrderCoupons.WithContext(ctx).ReadDB().Where( + s.readDB.OrderCoupons.OrderID.In(orderIDs...), + s.readDB.OrderCoupons.UserCouponID.In(userCouponIDs...), + ).Find() + for _, oc := range ocs { + if _, ok := appliedAmountMap[oc.OrderID]; !ok { + appliedAmountMap[oc.OrderID] = make(map[int64]int64) + } + appliedAmountMap[oc.OrderID][oc.UserCouponID] = oc.AppliedAmount + } + } + } + + itemCardMap := make(map[int64]*ItemCardSimpleInfo) + if len(userItemCardIDs) > 0 { + userCards, _ := s.readDB.UserItemCards.WithContext(ctx).ReadDB().Where(s.readDB.UserItemCards.ID.In(userItemCardIDs...)).Find() + var sysCardIDs []int64 + ucMap := make(map[int64]*model.UserItemCards) + for _, uc := range userCards { + sysCardIDs = append(sysCardIDs, uc.CardID) + ucMap[uc.ID] = uc + } + if len(sysCardIDs) > 0 { + sysCards, _ := s.readDB.SystemItemCards.WithContext(ctx).ReadDB().Where(s.readDB.SystemItemCards.ID.In(sysCardIDs...)).Find() + scMap := make(map[int64]*model.SystemItemCards) + for _, sc := range sysCards { + scMap[sc.ID] = sc + } + for id, uc := range ucMap { + if sc, ok := scMap[uc.CardID]; ok { + itemCardMap[id] = &ItemCardSimpleInfo{ + UserCardID: uc.ID, + Name: sc.Name, + EffectType: sc.EffectType, + } + } + } + } + } + // 构建返回结果 items = make([]*OrderWithItems, len(orders)) for i, order := range orders { + // 复制一份 CouponInfo,因为同一个优惠券可能被多个订单使用,但金额不同 + var cInfo *CouponSimpleInfo + if baseInfo, ok := couponMap[order.CouponID]; ok { + cInfo = &CouponSimpleInfo{ + UserCouponID: baseInfo.UserCouponID, + Name: baseInfo.Name, + Type: baseInfo.Type, + Value: baseInfo.Value, + } + // 尝试使用实际抵扣金额 + if amMap, ok := appliedAmountMap[order.ID]; ok { + if amount, ok2 := amMap[order.CouponID]; ok2 { + cInfo.Value = amount + } + } + } + items[i] = &OrderWithItems{ - Orders: order, - Items: itemsMap[order.ID], + Orders: order, + Items: itemsMap[order.ID], + CouponInfo: cInfo, + ItemCardInfo: itemCardMap[order.ItemCardID], } if logs, ok := drawLogsListMap[order.ID]; ok && len(logs) > 0 { @@ -396,7 +554,7 @@ func (s *service) ListOrdersWithItems(ctx context.Context, userID int64, status ClientID: r.ClientID, Timestamp: r.Timestamp, ServerSeedHash: r.ServerSeedHash, - ServerSubSeed: r.ServerSubSeed, + ServerSubSeed: "", ClientSeed: r.ClientSeed, Nonce: r.Nonce, ItemsRoot: r.ItemsRoot, @@ -423,6 +581,11 @@ func (s *service) ListOrdersWithItems(ctx context.Context, userID int64, status } } } + + // Special handling for Ichiban Kuji: Do not show Item Card + if items[i].PlayType == "ichiban" { + items[i].ItemCardInfo = nil + } } return items, total, nil diff --git a/internal/service/user/points_convert.go b/internal/service/user/points_convert.go index 14017f0..728f4da 100644 --- a/internal/service/user/points_convert.go +++ b/internal/service/user/points_convert.go @@ -1,23 +1,22 @@ package user import ( - "context" - "fmt" + "context" + "fmt" ) func (s *service) CentsToPoints(ctx context.Context, cents int64) (int64, error) { - if cents <= 0 { - return 0, nil - } - cfg, _ := s.readDB.SystemConfigs.WithContext(ctx).Where(s.readDB.SystemConfigs.ConfigKey.Eq("points_exchange_per_cent")).First() - rate := int64(1) - if cfg != nil { - var r int64 - _, _ = fmt.Sscanf(cfg.ConfigValue, "%d", &r) - if r > 0 { - rate = r - } - } - return cents * rate, nil + if cents <= 0 { + return 0, nil + } + cfg, _ := s.readDB.SystemConfigs.WithContext(ctx).Where(s.readDB.SystemConfigs.ConfigKey.Eq("points_exchange_per_cent")).First() + rate := int64(1) + if cfg != nil { + var r int64 + _, _ = fmt.Sscanf(cfg.ConfigValue, "%d", &r) + if r > 0 { + rate = r + } + } + return cents * rate, nil } - diff --git a/internal/service/user/reward_grant.go b/internal/service/user/reward_grant.go index f7fa429..d1864ac 100644 --- a/internal/service/user/reward_grant.go +++ b/internal/service/user/reward_grant.go @@ -153,7 +153,12 @@ func (s *service) GrantReward(ctx context.Context, userID int64, req GrantReward } }(), Status: 1, // 持有状态 - Remark: product.Name, + Remark: func() string { + if req.Remark != "" { + return req.Remark + } + return product.Name + }(), } err = tx.UserInventory.WithContext(ctx).Create(inventory) @@ -337,7 +342,12 @@ func (s *service) GrantRewardToOrder(ctx context.Context, userID int64, req Gran return 0 }(), Status: 1, // 持有状态 - Remark: product.Name, + Remark: func() string { + if req.Remark != "" { + return req.Remark + } + return product.Name + }(), } err = tx.UserInventory.WithContext(ctx).Create(inventory) diff --git a/internal/service/user/user.go b/internal/service/user/user.go index 3129a2c..19b6355 100644 --- a/internal/service/user/user.go +++ b/internal/service/user/user.go @@ -34,6 +34,7 @@ type Service interface { ListUserItemCardsWithTemplate(ctx context.Context, userID int64, page, pageSize int) (items []*ItemCardWithTemplate, total int64, err error) ListUserItemCardsWithTemplateUsable(ctx context.Context, userID int64, page, pageSize int) (items []*ItemCardWithTemplate, total int64, err error) ListUserItemCardsWithTemplateByStatus(ctx context.Context, userID int64, status int32, page, pageSize int) (items []*ItemCardWithTemplate, total int64, err error) + ListAggregatedUserItemCards(ctx context.Context, userID int64, status int32, page, pageSize int) (items []*ItemCardWithTemplate, total int64, err error) ListUserItemCardUses(ctx context.Context, userID int64, page, pageSize int) (items []*model.ActivityDrawEffects, total int64, err error) GetUserStats(ctx context.Context, userID int64) (*UserStats, error) AddAddress(ctx context.Context, userID int64, in AddAddressInput) (*model.UserAddresses, error) @@ -49,6 +50,7 @@ type Service interface { RevokeAddressShare(ctx context.Context, userID int64, inventoryID int64) error SubmitAddressShare(ctx context.Context, shareToken string, name string, mobile string, province string, city string, district string, address string, submittedByUserID *int64, submittedIP *string) (int64, error) RequestShipping(ctx context.Context, userID int64, inventoryID int64) (int64, error) + CancelShipping(ctx context.Context, userID int64, inventoryID int64) error RequestShippings(ctx context.Context, userID int64, inventoryIDs []int64, addressID *int64) (int64, []int64, []struct { ID int64 Reason string diff --git a/logs/mini-chat-access.log b/logs/mini-chat-access.log index f0796fd..f999e18 100644 --- a/logs/mini-chat-access.log +++ b/logs/mini-chat-access.log @@ -169,3 +169,158 @@ {"level":"info","time":"2025-12-22 15:02:20","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/activities/64/issues/71/rewards","http_code":200,"business_code":0,"success":true,"cost_seconds":0.04405725,"trace_id":"2002998120878706688","trace_info":{"trace_id":"2002998120878706688","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/activities/64/issues/71/rewards","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2002998120878706688"],"Vary":["Origin"]},"body":{"list":[{"id":237,"product_id":0,"name":"参与奖-5对","weight":0,"quantity":98,"original_qty":100,"level":10,"sort":1,"is_boss":0,"min_score":11},{"id":238,"product_id":188,"name":"参与奖-10对","weight":0,"quantity":100,"original_qty":100,"level":9,"sort":2,"is_boss":0,"min_score":10},{"id":239,"product_id":206,"name":"铜奖-15对","weight":0,"quantity":50,"original_qty":50,"level":8,"sort":3,"is_boss":0,"min_score":9},{"id":240,"product_id":232,"name":"铜奖-20对","weight":0,"quantity":50,"original_qty":50,"level":7,"sort":4,"is_boss":0,"min_score":8},{"id":241,"product_id":260,"name":"银奖-25对","weight":0,"quantity":30,"original_qty":30,"level":6,"sort":5,"is_boss":0,"min_score":7},{"id":242,"product_id":266,"name":"银奖-30对","weight":0,"quantity":30,"original_qty":30,"level":5,"sort":6,"is_boss":0,"min_score":6},{"id":243,"product_id":265,"name":"金奖-35对","weight":0,"quantity":20,"original_qty":20,"level":4,"sort":7,"is_boss":0,"min_score":5},{"id":244,"product_id":268,"name":"金奖-40对","weight":0,"quantity":19,"original_qty":20,"level":3,"sort":8,"is_boss":0,"min_score":4},{"id":245,"product_id":269,"name":"特等奖-42对","weight":0,"quantity":10,"original_qty":10,"level":2,"sort":9,"is_boss":0,"min_score":3},{"id":246,"product_id":270,"name":"特等奖-44对","weight":0,"quantity":5,"original_qty":5,"level":1,"sort":10,"is_boss":0,"min_score":2},{"id":247,"product_id":271,"name":"终极大奖-45对","weight":0,"quantity":3,"original_qty":3,"level":0,"sort":11,"is_boss":1,"min_score":1}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.044057083},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:02:20","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.0201555},{"time":"2025-12-22 15:02:20","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/activity/rewards_list.go:13","sql":"SELECT * FROM `activity_reward_settings` WHERE `activity_reward_settings`.`issue_id` = 71 AND deleted_at IS NULL AND `activity_reward_settings`.`deleted_at` IS NULL ORDER BY `activity_reward_settings`.`sort` ASC","rows_affected":11,"cost_seconds":0.023317375}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.04405725}} {"level":"info","time":"2025-12-22 15:02:26","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"PUT","path":"/api/admin/activities/64/issues/71/rewards/237","http_code":200,"business_code":0,"success":true,"cost_seconds":0.082585209,"trace_id":"2002998146413629440","trace_info":{"trace_id":"2002998146413629440","request":{"ttl":"un-limit","method":"PUT","decoded_url":"/api/admin/activities/64/issues/71/rewards/237","header":{"Content-Type":"application/json"},"body":"{\"product_id\":176,\"weight\":0,\"quantity\":98,\"original_qty\":100,\"level\":10,\"sort\":1,\"is_boss\":0,\"name\":\"参与奖-5对\",\"min_score\":11}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2002998146413629440"],"Vary":["Origin"]},"body":{"message":"操作成功"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.08258475},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:02:26","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.020250292},{"time":"2025-12-22 15:02:26","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/activity/rewards_modify.go:14","sql":"SELECT * FROM `activity_reward_settings` WHERE `activity_reward_settings`.`id` = 237 AND deleted_at IS NULL AND `activity_reward_settings`.`deleted_at` IS NULL ORDER BY `activity_reward_settings`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.02290525},{"time":"2025-12-22 15:02:26","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/activity/rewards_modify.go:46","sql":"INSERT INTO `activity_reward_settings` (`issue_id`,`product_id`,`name`,`weight`,`quantity`,`original_qty`,`level`,`sort`,`is_boss`,`min_score`,`deleted_at`,`id`,`created_at`,`updated_at`) VALUES (71,176,'参与奖-5对',0,98,100,10,1,0,11,NULL,237,'2025-12-22 11:09:07.96','2025-12-22 15:02:26.712') ON DUPLICATE KEY UPDATE `issue_id`=VALUES(`issue_id`),`product_id`=VALUES(`product_id`),`name`=VALUES(`name`),`weight`=VALUES(`weight`),`quantity`=VALUES(`quantity`),`original_qty`=VALUES(`original_qty`),`level`=VALUES(`level`),`sort`=VALUES(`sort`),`is_boss`=VALUES(`is_boss`),`min_score`=VALUES(`min_score`),`deleted_at`=VALUES(`deleted_at`)","rows_affected":2,"cost_seconds":0.030123209}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.082585209}} {"level":"info","time":"2025-12-22 15:02:26","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/activities/64/issues/71/rewards","http_code":200,"business_code":0,"success":true,"cost_seconds":0.042336334,"trace_id":"2002998147109883904","trace_info":{"trace_id":"2002998147109883904","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/activities/64/issues/71/rewards","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2002998147109883904"],"Vary":["Origin"]},"body":{"list":[{"id":237,"product_id":176,"name":"参与奖-5对","weight":0,"quantity":98,"original_qty":100,"level":10,"sort":1,"is_boss":0,"min_score":11},{"id":238,"product_id":188,"name":"参与奖-10对","weight":0,"quantity":100,"original_qty":100,"level":9,"sort":2,"is_boss":0,"min_score":10},{"id":239,"product_id":206,"name":"铜奖-15对","weight":0,"quantity":50,"original_qty":50,"level":8,"sort":3,"is_boss":0,"min_score":9},{"id":240,"product_id":232,"name":"铜奖-20对","weight":0,"quantity":50,"original_qty":50,"level":7,"sort":4,"is_boss":0,"min_score":8},{"id":241,"product_id":260,"name":"银奖-25对","weight":0,"quantity":30,"original_qty":30,"level":6,"sort":5,"is_boss":0,"min_score":7},{"id":242,"product_id":266,"name":"银奖-30对","weight":0,"quantity":30,"original_qty":30,"level":5,"sort":6,"is_boss":0,"min_score":6},{"id":243,"product_id":265,"name":"金奖-35对","weight":0,"quantity":20,"original_qty":20,"level":4,"sort":7,"is_boss":0,"min_score":5},{"id":244,"product_id":268,"name":"金奖-40对","weight":0,"quantity":19,"original_qty":20,"level":3,"sort":8,"is_boss":0,"min_score":4},{"id":245,"product_id":269,"name":"特等奖-42对","weight":0,"quantity":10,"original_qty":10,"level":2,"sort":9,"is_boss":0,"min_score":3},{"id":246,"product_id":270,"name":"特等奖-44对","weight":0,"quantity":5,"original_qty":5,"level":1,"sort":10,"is_boss":0,"min_score":2},{"id":247,"product_id":271,"name":"终极大奖-45对","weight":0,"quantity":3,"original_qty":3,"level":0,"sort":11,"is_boss":1,"min_score":1}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.042321459},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:02:26","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.019896167},{"time":"2025-12-22 15:02:26","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/activity/rewards_list.go:13","sql":"SELECT * FROM `activity_reward_settings` WHERE `activity_reward_settings`.`issue_id` = 71 AND deleted_at IS NULL AND `activity_reward_settings`.`deleted_at` IS NULL ORDER BY `activity_reward_settings`.`sort` ASC","rows_affected":11,"cost_seconds":0.021531792}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.042336334}} +{"level":"info","time":"2025-12-22 15:18:30","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-22 15:18:49","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/activities/64/issues/71/rewards","http_code":200,"business_code":0,"success":true,"cost_seconds":0.040827125,"trace_id":"2003002269489958912","trace_info":{"trace_id":"2003002269489958912","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/activities/64/issues/71/rewards","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002269489958912"],"Vary":["Origin"]},"body":{"list":[{"id":237,"product_id":176,"name":"参与奖-5对","weight":0,"quantity":98,"original_qty":100,"level":10,"sort":1,"is_boss":0,"min_score":11},{"id":238,"product_id":188,"name":"参与奖-10对","weight":0,"quantity":100,"original_qty":100,"level":9,"sort":2,"is_boss":0,"min_score":10},{"id":239,"product_id":206,"name":"铜奖-15对","weight":0,"quantity":50,"original_qty":50,"level":8,"sort":3,"is_boss":0,"min_score":9},{"id":240,"product_id":232,"name":"铜奖-20对","weight":0,"quantity":50,"original_qty":50,"level":7,"sort":4,"is_boss":0,"min_score":8},{"id":241,"product_id":260,"name":"银奖-25对","weight":0,"quantity":30,"original_qty":30,"level":6,"sort":5,"is_boss":0,"min_score":7},{"id":242,"product_id":266,"name":"银奖-30对","weight":0,"quantity":30,"original_qty":30,"level":5,"sort":6,"is_boss":0,"min_score":6},{"id":243,"product_id":265,"name":"金奖-35对","weight":0,"quantity":20,"original_qty":20,"level":4,"sort":7,"is_boss":0,"min_score":5},{"id":244,"product_id":268,"name":"金奖-40对","weight":0,"quantity":17,"original_qty":20,"level":3,"sort":8,"is_boss":0,"min_score":4},{"id":245,"product_id":269,"name":"特等奖-42对","weight":0,"quantity":10,"original_qty":10,"level":2,"sort":9,"is_boss":0,"min_score":3},{"id":246,"product_id":270,"name":"特等奖-44对","weight":0,"quantity":5,"original_qty":5,"level":1,"sort":10,"is_boss":0,"min_score":2},{"id":247,"product_id":271,"name":"终极大奖-45对","weight":0,"quantity":3,"original_qty":3,"level":0,"sort":11,"is_boss":1,"min_score":1}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.040826792},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:18:49","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.0190035},{"time":"2025-12-22 15:18:49","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/activity/rewards_list.go:13","sql":"SELECT * FROM `activity_reward_settings` WHERE `activity_reward_settings`.`issue_id` = 71 AND deleted_at IS NULL AND `activity_reward_settings`.`deleted_at` IS NULL ORDER BY `activity_reward_settings`.`sort` ASC","rows_affected":11,"cost_seconds":0.021065417}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.040827125}} +{"level":"info","time":"2025-12-22 15:18:49","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/products?page=1&page_size=100","http_code":200,"business_code":0,"success":true,"cost_seconds":0.107469375,"trace_id":"2003002269489958912","trace_info":{"trace_id":"2003002269489958912","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/products?page=1&page_size=100","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002269489958912"],"Vary":["Origin"]},"body":{"page":1,"page_size":100,"total":271,"list":[{"id":271,"name":"LAMY凌美宝珠笔礼盒暗夜绿野奢礼盒","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765974142175986900.jpg\"]","price":87990,"stock":9999,"sales":0,"status":1},{"id":270,"name":"LAMY凌美钢笔礼盒 宋代限定版墨水笔青苍玉兰","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765974064319974300.jpg\"]","price":32990,"stock":9999,"sales":0,"status":1},{"id":269,"name":"LAMY凌美 钢笔(尼克+朱迪)礼盒疯狂动物城联名墨水笔套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973959992705500.jpg\"]","price":100900,"stock":9999,"sales":0,"status":1},{"id":268,"name":"LAMY凌美钢笔礼盒 狩猎系列磨砂黑白色墨水笔套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973806819055400.jpg\"]","price":43900,"stock":9999,"sales":0,"status":1},{"id":267,"name":"LAMY凌美钢笔礼盒 狩猎系列库洛米联名限定套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973718891607700.png\"]","price":50500,"stock":99999,"sales":0,"status":1},{"id":266,"name":"MINISO名创优品U型枕飞机云朵护颈枕记忆棉","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958525893026200.jpg\"]","price":7700,"stock":9999,"sales":0,"status":1},{"id":265,"name":"MINISO名创优品假面骑士×WASA龙联名系列盲盒单盒随机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958389919343600.jpg\"]","price":10490,"stock":9999,"sales":0,"status":1},{"id":264,"name":"MINISO名创优品橘猫工业铁甲小宝第二弹模型玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958320499495500.jpg\"]","price":10800,"stock":9999,"sales":0,"status":1},{"id":263,"name":"MINISO名创优品橘猫工业铁甲小宝第一弹模型玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958282291523200.jpg\"]","price":10800,"stock":9999,"sales":0,"status":1},{"id":262,"name":"MINISO名创Nanci囡茜氛围灯积木摆件中国风小夜灯","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958213519986200.png\"]","price":10900,"stock":9999,"sales":0,"status":1},{"id":261,"name":"名创优品迪士尼316不锈钢保温饭盒便当盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958147297026300.jpg\"]","price":8900,"stock":9999,"sales":0,"status":1},{"id":260,"name":"MINISO名创优品学院系列焦糖格纹双肩包包学院风","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958088190736900.jpg\"]","price":11500,"stock":9999,"sales":0,"status":1},{"id":259,"name":"MINISO/名创优品迪士尼朱迪的警官生活系列痛包","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957990772169000.jpg\"]","price":13090,"stock":9999,"sales":0,"status":1},{"id":258,"name":"MINISO名创优品雪夜派对拼装模型圣诞节礼物","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957900503621400.jpg\"]","price":13900,"stock":9999,"sales":0,"status":1},{"id":257,"name":"MINISO名创优品【深睡记忆枕】透气枕芯枕头护颈椎释压","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957819453538400.png\"]","price":14590,"stock":9999,"sales":0,"status":1},{"id":256,"name":"MINISO名创优品香薰氛围感蓝牙音乐香氛机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957751042251600.jpg\"]","price":24500,"stock":9999,"sales":0,"status":1},{"id":255,"name":"MINISO名创优品铠魂帝皇侠15周年典藏模玩机甲超可动人偶成品模型","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957636770820900.jpg\"]","price":27500,"stock":9999,"sales":0,"status":1},{"id":254,"name":"MINISO名创优品冰淇淋香薰膏室内卫生间清新芬芳花果香4盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957419425272100.jpg\"]","price":4390,"stock":9999,"sales":0,"status":1},{"id":253,"name":"MINISO名创优品硅胶热水袋","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957295282004700.jpg\"]","price":5790,"stock":9999,"sales":0,"status":1},{"id":252,"name":"MINISO名创优品Chiikawa系列毛茸茸季斗篷休闲午休毯随机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957103956331200.jpg\"]","price":16390,"stock":9999,"sales":0,"status":1},{"id":251,"name":"MINISO名创优品罗小黑轩影摇香系列迷你挂画冰箱贴五款盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957004993236600.jpg\"]","price":13990,"stock":9999,"sales":0,"status":1},{"id":250,"name":"MINISO名创优品chiikawa系列睡衣毛绒公仔","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956849972873300.jpg\"]","price":10900,"stock":9999,"sales":0,"status":1},{"id":249,"name":"MINISO名创优品蜡笔小新拼图","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956726589144000.png\"]","price":8890,"stock":9999,"sales":0,"status":1},{"id":248,"name":"MINISO名创优品三丽鸥保温杯公仔冰霸杯","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956569488102100.jpg\"]","price":8890,"stock":9999,"sales":0,"status":1},{"id":247,"name":"MINISO名创优品蜡笔小新超变装毛绒眼罩","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956491344273300.jpg\"]","price":1990,"stock":9999,"sales":0,"status":1},{"id":246,"name":"MINISO名创优品迪士尼疯狂动物城搪胶毛绒公仔盲盒摆件","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956361082178100.jpg\"]","price":8690,"stock":9999,"sales":0,"status":1},{"id":245,"name":"MINISO名创优品玉桂狗酷洛米捏捏小夜灯卧室睡眠灯床头灯LED灯随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956188247609800.jpg\"]","price":6590,"stock":9999,"sales":0,"status":1},{"id":244,"name":"MINISO名创优品Chiikawa奇幻马戏团毛绒挂件随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956006896154800.jpg\"]","price":5590,"stock":9999,"sales":0,"status":1},{"id":243,"name":"MINISO名创优品WAKUKU哇库库毛毛潮趣第二弹系列单盒盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955886258552300.jpg\"]","price":7590,"stock":9999,"sales":0,"status":1},{"id":242,"name":"MINISO名创优品Chiikawa樱花季吉伊乌萨奇小八零钱包挂件耳机包随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955794487832400.jpg\"]","price":4390,"stock":9999,"sales":0,"status":1},{"id":241,"name":"MINISO名创优品草莓熊休闲毯加厚保暖午睡毯子","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955717135623100.jpg\"]","price":9900,"stock":9999,"sales":0,"status":1},{"id":240,"name":"MINISO名创优品MyMelody秘森茶话会盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955658005155100.jpg\"]","price":3900,"stock":9999,"sales":0,"status":1},{"id":239,"name":"MINISO名创优品【指环爱神系列】2盒随机2款手办盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955515578588700.png\"]","price":6400,"stock":9999,"sales":0,"status":1},{"id":238,"name":"MINISO名创优品 小熊维尼拍立得系列(单盒单款)","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955384656076300.jpg\"]","price":3690,"stock":9999,"sales":0,"status":1},{"id":237,"name":"MINISO名创优品 小熊维尼拍立得系列(端盒8款)盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955301119356700.jpg\"]","price":30000,"stock":999,"sales":0,"status":1},{"id":236,"name":"MINISO名创优品缤纷街景积木第6季五件套拼装积木玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954965838583100.jpg\"]","price":18500,"stock":9999,"sales":0,"status":1},{"id":235,"name":"MINISO名创优品羊咩咩小羊玩偶毛绒公仔","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954804800434800.jpg\"]","price":2190,"stock":9999,"sales":0,"status":1},{"id":234,"name":"MINISO名创优品大师香薰室内家用150ml随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954438035852600.jpg\"]","price":4500,"stock":9999,"sales":0,"status":1},{"id":233,"name":"MINISO名创优品天然植物化妆棉800片白色","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954130792789000.jpg\"]","price":1390,"stock":9999,"sales":0,"status":1},{"id":232,"name":"MINISO名创优品玉桂狗u型枕","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765953971587736200.png\"]","price":3300,"stock":99999,"sales":0,"status":1},{"id":231,"name":"ZIPPO 春华秋实套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765878978681838200.jpg\"]","price":327500,"stock":9999,"sales":0,"status":1},{"id":230,"name":"ZIPPO 觉醒系列打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765877510957720900.jpg\"]","price":145500,"stock":9999,"sales":0,"status":1},{"id":229,"name":"ZIPPO 星际异兽套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765876033609870000.jpg\"]","price":135000,"stock":9999,"sales":0,"status":1},{"id":228,"name":"ZIPPO 幸运灵虫 套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875966491566400.jpg\"]","price":263900,"stock":9999,"sales":0,"status":1},{"id":227,"name":"ZIPPO Jack Daniel联名款——橡木之味打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875891023016800.jpg\"]","price":113800,"stock":9999,"sales":0,"status":1},{"id":226,"name":"ZIPPO八音盒·怦然心动打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875820076074600.jpg\"]","price":119900,"stock":9999,"sales":0,"status":1},{"id":225,"name":"ZIPPO蔷薇水晶礼盒套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875719520558200.jpg\"]","price":185000,"stock":9999,"sales":0,"status":1},{"id":224,"name":"ZIPPO幸运石系列随机款打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875631784700700.jpg\"]","price":58800,"stock":9999,"sales":0,"status":1},{"id":223,"name":"ZIPPO暗花幺點-PVD浸染打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875319301817500.avif\"]","price":33700,"stock":9999,"sales":0,"status":1},{"id":222,"name":"ZIPPO 雪花系列随机款打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875220636810700.jpg\"]","price":38200,"stock":9999,"sales":0,"status":1},{"id":221,"name":"唐草之Zippo在我心-烧金色","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875121383638800.jpg\"]","price":70400,"stock":9999,"sales":0,"status":1},{"id":220,"name":"ZIPPO东来也—武财神打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875049932239300.jpg\"]","price":26800,"stock":9999,"sales":0,"status":1},{"id":219,"name":"ZIPPO十二星座秘典随机款打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765874983695603700.jpg\"]","price":50200,"stock":9999,"sales":0,"status":1},{"id":218,"name":"ZIPPO 喵望星空-仿古银打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765874892660518100.jpg\"]","price":35000,"stock":9999,"sales":0,"status":1},{"id":217,"name":"ZIPPO 寻梦旅行猫咪打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765874799357193500.jpg\"]","price":26800,"stock":9999,"sales":0,"status":1},{"id":216,"name":"ZIPPO 经典缎纱系列打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765874656843816600.jpg\"]","price":18300,"stock":9999,"sales":0,"status":1},{"id":215,"name":"ZIPPO 恋爱至死套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765874569257704700.jpg\"]","price":30600,"stock":9999,"sales":0,"status":1},{"id":214,"name":"ZIPPO山海经系列打火机随机款","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765874469951727700.jpg\"]","price":124500,"stock":9999,"sales":0,"status":1},{"id":213,"name":"ZIPPO 黑冰360精雕-太阳与水晶打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765874394511845600.jpg\"]","price":128900,"stock":9999,"sales":0,"status":1},{"id":212,"name":"ZIPPO 纪念二战胜利75周年纯钢限量款打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765874282732029500.jpg\"]","price":132500,"stock":9999,"sales":0,"status":1},{"id":211,"name":"ZIPPO 90周年荣耀之宝-纪念款打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765874137696852300.jpg\"]","price":239900,"stock":9999,"sales":0,"status":1},{"id":210,"name":"ZIPPO 僵尸迷宫 僵尸新郎-发现你打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765873962535031300.jpg\"]","price":35000,"stock":999,"sales":0,"status":1},{"id":209,"name":"ZIPPO 赛博朋克 浪子英雄打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765873867250535900.jpg\"]","price":26800,"stock":9999,"sales":0,"status":1},{"id":208,"name":"ZIPPO 警世箴言打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765873648027889100.jpg\"]","price":18300,"stock":9999,"sales":0,"status":1},{"id":207,"name":"ZIPPO 八方来财打火机红色礼盒套装(含线)","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765873539960206300.jpg\"]","price":77500,"stock":999,"sales":0,"status":1},{"id":206,"name":"ZIPPO 如意吉象打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765873442205527300.jpg\"]","price":106200,"stock":9999,"sales":0,"status":1},{"id":205,"name":"ZIPPO 钱来运转打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765873359362166400.jpg\"]","price":124500,"stock":9999,"sales":0,"status":1},{"id":204,"name":"ZIPPO 川流不息打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765873287499214100.jpg\"]","price":43200,"stock":999,"sales":0,"status":1},{"id":203,"name":"ZIPPO 熏金十二星座打火机随机款","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765873231085180300.jpg\"]","price":168500,"stock":9999,"sales":0,"status":1},{"id":202,"name":"ZIPPO 飞的更高打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765873015631694800.jpg\"]","price":42100,"stock":999,"sales":0,"status":1},{"id":201,"name":"ZIPPO 流云淬火打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765872880849231700.jpg\"]","price":41200,"stock":999,"sales":0,"status":1},{"id":200,"name":"ZIPPO 招财猫打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765872628921136000.jpg\"]","price":26900,"stock":9999,"sales":0,"status":1},{"id":199,"name":"ZIPPO 经典黑裂漆定制打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765872449667519000.jpg\"]","price":21900,"stock":9999,"sales":0,"status":1},{"id":198,"name":"ZIPPO 步步高升打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765872312650382500.jpg\"]","price":18300,"stock":999,"sales":0,"status":1},{"id":197,"name":"ZIPPO 秋水含睛打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765871946370381700.jpg\"]","price":17000,"stock":9999,"sales":0,"status":1},{"id":196,"name":"ZIPPO 经典黑炫冰定制打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765871840986068400.jpg\"]","price":21900,"stock":9999,"sales":0,"status":1},{"id":195,"name":"王者荣耀官方正版【大桥-白鹤梁神女】1:7典藏手办","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765870735671721900.jpg\"]","price":99900,"stock":9999,"sales":0,"status":1},{"id":194,"name":"王者荣耀x康佳联名李白电煮锅 多功能料理锅","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765870440985958300.jpg\"]","price":12000,"stock":9999,"sales":0,"status":1},{"id":193,"name":"王者荣耀x康佳 鲁班联名电热水杯316不锈钢保温杯","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765870093003812000.jpg\"]","price":22500,"stock":9999,"sales":0,"status":1},{"id":192,"name":"王者荣耀官方正版周边【孙尚香-千金重弩】Q版手办","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765869381195810100.jpg\"]","price":18200,"stock":9999,"sales":0,"status":1},{"id":191,"name":"王者荣耀峡谷萌新-峡谷感谢季系列手办盲盒随机款","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765868952997446900.jpg\"]","price":7600,"stock":9999,"sales":0,"status":1},{"id":190,"name":"王者荣耀 心动的王者-假面绮宴主体邀请函套组随机(主题色纸+纪念票根)","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765868228234275600.jpg\"]","price":4300,"stock":9999,"sales":0,"status":1},{"id":189,"name":"王者荣耀 公孙离 惊鸿舞Myethos典藏手办摆件","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765867054597073200.jpg\"]","price":204000,"stock":999,"sales":0,"status":1},{"id":188,"name":"王者荣耀 人鱼之歌 朵利亚 Myethos典藏手办官方正版","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765866846021926900.jpg\"]","price":165000,"stock":9999,"sales":0,"status":1},{"id":187,"name":"王者荣耀X赞尔 蔡文姬 奇迹圣诞音乐水晶球","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765866550110881300.jpg\"]","price":66000,"stock":9999,"sales":0,"status":1},{"id":186,"name":"王者荣耀x万代ROBOT魂零号赤焰孙悟空可动机甲模型手办","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765866368803472700.jpg\"]","price":72900,"stock":9999,"sales":0,"status":1},{"id":185,"name":"王者荣耀官方正版周边【周瑜小乔-纯白花嫁】Q版手办","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765866153156237400.jpg\"]","price":61500,"stock":9999,"sales":0,"status":1},{"id":184,"name":"王者荣耀亚克力书签一套(隐藏款共8个)","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765865957316362700.jpg\"]","price":35000,"stock":9999,"sales":0,"status":1},{"id":183,"name":"王者荣耀正品40cm套装无限王者团棉花娃娃随机款","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765865719308392900.jpg\"]","price":31800,"stock":9999,"sales":0,"status":1},{"id":182,"name":"王者荣耀官方正版 大圣娶亲孙悟空 Q版手办","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765865338690308200.jpg\"]","price":28400,"stock":9999,"sales":0,"status":1},{"id":181,"name":"王者荣耀官方正版 一生所爱露娜 甜蜜组合Q版手办","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765865210058790000.jpg\"]","price":28400,"stock":9999,"sales":0,"status":1},{"id":180,"name":"王者荣耀英雄铠合作款多功能手表装饰穿搭潮流游戏周边","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765865092352107800.jpg\"]","price":29600,"stock":9999,"sales":0,"status":1},{"id":179,"name":"王者荣耀x黄油猫联名款暖手宝充电宝二合一随机款","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765864948813799800.jpg\"]","price":21900,"stock":9999,"sales":0,"status":1},{"id":178,"name":"王者荣耀xNICE FUN 逍遥幻梦庄周 鲲抱枕 半米尺寸","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765864773584950800.jpg\"]","price":14000,"stock":999,"sales":0,"status":1},{"id":177,"name":"王者荣耀官方正版 天籁弦音蔡文姬Q版手办 周边玩具摆件","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765863968816956200.jpg\"]","price":20800,"stock":9999,"sales":0,"status":1},{"id":176,"name":"王者荣耀 棉乖乖系列可爱毛绒挂件公仔棉花娃娃游戏周边","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765863782017506700.jpg\"]","price":7700,"stock":9999,"sales":0,"status":1},{"id":175,"name":"王者荣耀夏侯淳盒蛋公仔游戏周边QQfamily","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765863642672282700.jpg\"]","price":8700,"stock":9999,"sales":0,"status":1},{"id":174,"name":"王者荣耀公孙离绒团毛绒挂件游戏周边NICE FUN","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765863285495146100.jpg\"]","price":6400,"stock":9999,"sales":0,"status":1},{"id":173,"name":"王者荣耀正版NFC手机挂绳(触碰启动王者)","category_id":7,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765863146757914300.jpg\"]","price":3200,"stock":9999,"sales":0,"status":1},{"id":172,"name":"迪士尼(Disney)官方正版暗黑库洛米毛绒玩具65cm","category_id":6,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/15/1765796432217447600.jpg\"]","price":10500,"stock":9999,"sales":0,"status":1}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.107468917},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:18:49","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.067036791},{"time":"2025-12-22 15:18:49","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/product/product.go:233","sql":"SELECT count(*) FROM `products` WHERE deleted_at IS NULL AND `products`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.008469792},{"time":"2025-12-22 15:18:49","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/product/product.go:237","sql":"SELECT * FROM `products` WHERE deleted_at IS NULL AND `products`.`deleted_at` IS NULL ORDER BY `products`.`id` DESC LIMIT 100","rows_affected":100,"cost_seconds":0.031160125}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.107469375}} +{"level":"info","time":"2025-12-22 15:18:52","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_item_cards?page=1&page_size=100","http_code":200,"business_code":0,"success":true,"cost_seconds":0.046350458,"trace_id":"2003002282098036736","trace_info":{"trace_id":"2003002282098036736","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_item_cards?page=1&page_size=100","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002282098036736"],"Vary":["Origin"]},"body":{"page":1,"page_size":100,"total":1,"list":[{"id":1,"created_at":"2025-11-14T18:46:15.338+08:00","updated_at":"2025-12-10T22:33:28.156+08:00","name":"双倍快乐水","status":1,"card_type":1,"scope_type":1,"activity_category_id":0,"activity_id":0,"issue_id":0,"price":1900,"valid_start":"2025-11-26T00:00:00+08:00","valid_end":"2025-12-25T00:00:00+08:00","effect_type":1,"reward_multiplier_x1000":0,"boost_rate_x1000":10,"stacking_strategy":1,"max_effect_value_x1000":0,"remark":"备注个锤子啊"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.046350166},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.017642291},{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:326","sql":"SELECT count(*) FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.009182167},{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:340","sql":"SELECT * FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL ORDER BY `system_item_cards`.`id` DESC LIMIT 100","rows_affected":1,"cost_seconds":0.018974459}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.046350458}} +{"level":"info","time":"2025-12-22 15:18:52","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_titles?page=1&page_size=100","http_code":200,"business_code":0,"success":true,"cost_seconds":0.102168458,"trace_id":"2003002282098036736","trace_info":{"trace_id":"2003002282098036736","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_titles?page=1&page_size=100","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002282098036736"],"Vary":["Origin"]},"body":{"page":1,"page_size":100,"total":2,"list":[{"id":8,"name":"概率加成","description":"抽奖期间 会对奖品有一定概率增幅","status":1,"obtain_rules_json":"{}","scopes_json":"{}","created_at":"2025-11-16T11:08:04.442+08:00","updated_at":"2025-11-16T11:08:04.442+08:00","deleted_at":null},{"id":7,"name":"奖品双倍","description":"在游戏活动抽奖的时候 会有一定几率触发双倍;效果等同双倍卡","status":1,"obtain_rules_json":"{}","scopes_json":"{}","created_at":"2025-11-16T10:49:39.519+08:00","updated_at":"2025-11-16T10:49:39.519+08:00","deleted_at":null}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.102167958},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.070256458},{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/titles_admin.go:42","sql":"SELECT count(*) FROM `system_titles` WHERE deleted_at IS NULL AND `system_titles`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.010270792},{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/titles_admin.go:48","sql":"SELECT * FROM `system_titles` WHERE deleted_at IS NULL AND `system_titles`.`deleted_at` IS NULL ORDER BY `system_titles`.`id` DESC LIMIT 100","rows_affected":2,"cost_seconds":0.021255208}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.102168458}} +{"level":"info","time":"2025-12-22 15:18:52","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_coupons?status=1&page=1&page_size=100","http_code":200,"business_code":0,"success":true,"cost_seconds":0.119369208,"trace_id":"2003002282098036736","trace_info":{"trace_id":"2003002282098036736","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_coupons?status=1&page=1&page_size=100","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002282098036736"],"Vary":["Origin"]},"body":{"page":1,"page_size":100,"total":3,"list":[{"id":3,"name":"1000元优惠券","status":1,"coupon_type":1,"discount_type":1,"discount_value":100000,"min_amount":100,"max_discount":0,"valid_days":30,"total_quantity":3,"used_quantity":3,"created_at":"2025-12-14 17:31:24"},{"id":2,"name":"30元优惠券","status":1,"coupon_type":1,"discount_type":1,"discount_value":3000,"min_amount":100,"max_discount":0,"valid_days":30,"total_quantity":1000,"used_quantity":4,"created_at":"2025-12-08 01:40:47"},{"id":1,"name":"优惠券1 ","status":1,"coupon_type":1,"discount_type":1,"discount_value":10000,"min_amount":100,"max_discount":0,"valid_days":30,"total_quantity":1100,"used_quantity":5,"created_at":"2025-11-14 20:19:47"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.11936875},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018740084},{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:202","sql":"SELECT count(*) FROM `system_coupons` WHERE `system_coupons`.`status` = 1 AND deleted_at IS NULL AND `system_coupons`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.020148833},{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:221","sql":"SELECT * FROM `system_coupons` WHERE `system_coupons`.`status` = 1 AND `system_coupons`.`deleted_at` IS NULL ORDER BY `system_coupons`.`id` DESC LIMIT 100","rows_affected":-1,"cost_seconds":0.020558375},{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:236","sql":"SELECT count(*) FROM `user_coupons` WHERE `user_coupons`.`coupon_id` = 3","rows_affected":1,"cost_seconds":0.019304334},{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:236","sql":"SELECT count(*) FROM `user_coupons` WHERE `user_coupons`.`coupon_id` = 2","rows_affected":1,"cost_seconds":0.020485875},{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:236","sql":"SELECT count(*) FROM `user_coupons` WHERE `user_coupons`.`coupon_id` = 1","rows_affected":1,"cost_seconds":0.019494167}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.119369208}} +{"level":"info","time":"2025-12-22 15:18:52","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/users?current=1&size=10&page=1&page_size=20","http_code":200,"business_code":0,"success":true,"cost_seconds":0.176643375,"trace_id":"2003002282098036736","trace_info":{"trace_id":"2003002282098036736","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/users?current=1&size=10&page=1&page_size=20","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002282098036736"],"Vary":["Origin"]},"body":{"page":1,"page_size":20,"total":6,"list":[{"id":813,"nickname":"善良的巴乔","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAABmAADpPQ5dAAAAAXRSTlMAQObYZgAAAD9JREFUeJxiAIH//xHkSBdAEofwRrjAKEAFsMQCTzQjXgCRSMDsES4AoWAALj6iBRABNCqAnHlGBRgAAQAA//8HSv0f1XCh/AAAAABJRU5ErkJggg==","invite_code":"AZCHW75Z","inviter_id":0,"created_at":"2025-11-16 13:50:58","douyin_id":"","channel_name":"","channel_code":"","coupons_count":1,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":12,"nickname":"张三","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"S93L5LDY","inviter_id":0,"created_at":"2025-11-14 21:21:44","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":6,"today_consume":11910,"seven_day_consume":11910},{"id":9,"nickname":"贝利求而不得","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"ELBZ6BM8","inviter_id":6,"created_at":"2025-11-14 11:48:55","douyin_id":"","channel_name":"","channel_code":"","coupons_count":2,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":8,"nickname":"欢呼的贝利","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"VUQQ9MYF","inviter_id":0,"created_at":"2025-11-14 11:18:07","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":7,"nickname":"大罗舞力四射","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"23JEKRTQ","inviter_id":0,"created_at":"2025-11-14 11:06:38","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":6,"nickname":"标致的欧文","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"A8ZWET6K","inviter_id":0,"created_at":"2025-11-14 11:06:05","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.176643208},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.068079083},{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:91","sql":"SELECT count(*) FROM `users` LEFT JOIN `channels` ON `channels`.`id` = `users`.`channel_id` WHERE `users`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.010143208},{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:102","sql":"SELECT `users`.*,`channels`.`name` AS `channel_name`,`channels`.`code` AS `channel_code` FROM `users` LEFT JOIN `channels` ON `channels`.`id` = `users`.`channel_id` WHERE `users`.`deleted_at` IS NULL ORDER BY `users`.`id` DESC LIMIT 20","rows_affected":-1,"cost_seconds":0.019729083},{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:126","sql":"SELECT `user_coupons`.`user_id`,COUNT(`user_coupons`.`id`) AS `count` FROM `user_coupons` WHERE `user_coupons`.`user_id` IN (813,12,9,8,7,6) AND `user_coupons`.`status` = 1 GROUP BY `user_coupons`.`user_id`","rows_affected":-1,"cost_seconds":0.019557625},{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:145","sql":"SELECT `user_item_cards`.`user_id`,COUNT(`user_item_cards`.`id`) AS `count` FROM `user_item_cards` WHERE `user_item_cards`.`user_id` IN (813,12,9,8,7,6) AND `user_item_cards`.`status` = 1 GROUP BY `user_item_cards`.`user_id`","rows_affected":-1,"cost_seconds":0.020883459},{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:172","sql":"SELECT `orders`.`user_id`,SUM(`orders`.`actual_amount`) AS `amount` FROM `orders` WHERE `orders`.`user_id` IN (813,12,9,8,7,6) AND `orders`.`status` = 2 AND `orders`.`created_at` >= '2025-12-22 00:00:00' GROUP BY `orders`.`user_id`","rows_affected":-1,"cost_seconds":0.019135083},{"time":"2025-12-22 15:18:52","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:185","sql":"SELECT `orders`.`user_id`,SUM(`orders`.`actual_amount`) AS `amount` FROM `orders` WHERE `orders`.`user_id` IN (813,12,9,8,7,6) AND `orders`.`status` = 2 AND `orders`.`created_at` >= '2025-12-16 00:00:00' GROUP BY `orders`.`user_id`","rows_affected":-1,"cost_seconds":0.01794875}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.176643375}} +{"level":"info","time":"2025-12-22 15:18:55","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/products?status=1&page=1&page_size=50","http_code":200,"business_code":0,"success":true,"cost_seconds":0.060145459,"trace_id":"2003002292088868864","trace_info":{"trace_id":"2003002292088868864","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/products?status=1&page=1&page_size=50","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002292088868864"],"Vary":["Origin"]},"body":{"page":1,"page_size":50,"total":271,"list":[{"id":271,"name":"LAMY凌美宝珠笔礼盒暗夜绿野奢礼盒","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765974142175986900.jpg\"]","price":87990,"stock":9999,"sales":0,"status":1},{"id":270,"name":"LAMY凌美钢笔礼盒 宋代限定版墨水笔青苍玉兰","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765974064319974300.jpg\"]","price":32990,"stock":9999,"sales":0,"status":1},{"id":269,"name":"LAMY凌美 钢笔(尼克+朱迪)礼盒疯狂动物城联名墨水笔套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973959992705500.jpg\"]","price":100900,"stock":9999,"sales":0,"status":1},{"id":268,"name":"LAMY凌美钢笔礼盒 狩猎系列磨砂黑白色墨水笔套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973806819055400.jpg\"]","price":43900,"stock":9999,"sales":0,"status":1},{"id":267,"name":"LAMY凌美钢笔礼盒 狩猎系列库洛米联名限定套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973718891607700.png\"]","price":50500,"stock":99999,"sales":0,"status":1},{"id":266,"name":"MINISO名创优品U型枕飞机云朵护颈枕记忆棉","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958525893026200.jpg\"]","price":7700,"stock":9999,"sales":0,"status":1},{"id":265,"name":"MINISO名创优品假面骑士×WASA龙联名系列盲盒单盒随机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958389919343600.jpg\"]","price":10490,"stock":9999,"sales":0,"status":1},{"id":264,"name":"MINISO名创优品橘猫工业铁甲小宝第二弹模型玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958320499495500.jpg\"]","price":10800,"stock":9999,"sales":0,"status":1},{"id":263,"name":"MINISO名创优品橘猫工业铁甲小宝第一弹模型玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958282291523200.jpg\"]","price":10800,"stock":9999,"sales":0,"status":1},{"id":262,"name":"MINISO名创Nanci囡茜氛围灯积木摆件中国风小夜灯","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958213519986200.png\"]","price":10900,"stock":9999,"sales":0,"status":1},{"id":261,"name":"名创优品迪士尼316不锈钢保温饭盒便当盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958147297026300.jpg\"]","price":8900,"stock":9999,"sales":0,"status":1},{"id":260,"name":"MINISO名创优品学院系列焦糖格纹双肩包包学院风","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958088190736900.jpg\"]","price":11500,"stock":9999,"sales":0,"status":1},{"id":259,"name":"MINISO/名创优品迪士尼朱迪的警官生活系列痛包","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957990772169000.jpg\"]","price":13090,"stock":9999,"sales":0,"status":1},{"id":258,"name":"MINISO名创优品雪夜派对拼装模型圣诞节礼物","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957900503621400.jpg\"]","price":13900,"stock":9999,"sales":0,"status":1},{"id":257,"name":"MINISO名创优品【深睡记忆枕】透气枕芯枕头护颈椎释压","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957819453538400.png\"]","price":14590,"stock":9999,"sales":0,"status":1},{"id":256,"name":"MINISO名创优品香薰氛围感蓝牙音乐香氛机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957751042251600.jpg\"]","price":24500,"stock":9999,"sales":0,"status":1},{"id":255,"name":"MINISO名创优品铠魂帝皇侠15周年典藏模玩机甲超可动人偶成品模型","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957636770820900.jpg\"]","price":27500,"stock":9999,"sales":0,"status":1},{"id":254,"name":"MINISO名创优品冰淇淋香薰膏室内卫生间清新芬芳花果香4盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957419425272100.jpg\"]","price":4390,"stock":9999,"sales":0,"status":1},{"id":253,"name":"MINISO名创优品硅胶热水袋","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957295282004700.jpg\"]","price":5790,"stock":9999,"sales":0,"status":1},{"id":252,"name":"MINISO名创优品Chiikawa系列毛茸茸季斗篷休闲午休毯随机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957103956331200.jpg\"]","price":16390,"stock":9999,"sales":0,"status":1},{"id":251,"name":"MINISO名创优品罗小黑轩影摇香系列迷你挂画冰箱贴五款盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957004993236600.jpg\"]","price":13990,"stock":9999,"sales":0,"status":1},{"id":250,"name":"MINISO名创优品chiikawa系列睡衣毛绒公仔","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956849972873300.jpg\"]","price":10900,"stock":9999,"sales":0,"status":1},{"id":249,"name":"MINISO名创优品蜡笔小新拼图","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956726589144000.png\"]","price":8890,"stock":9999,"sales":0,"status":1},{"id":248,"name":"MINISO名创优品三丽鸥保温杯公仔冰霸杯","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956569488102100.jpg\"]","price":8890,"stock":9999,"sales":0,"status":1},{"id":247,"name":"MINISO名创优品蜡笔小新超变装毛绒眼罩","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956491344273300.jpg\"]","price":1990,"stock":9999,"sales":0,"status":1},{"id":246,"name":"MINISO名创优品迪士尼疯狂动物城搪胶毛绒公仔盲盒摆件","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956361082178100.jpg\"]","price":8690,"stock":9999,"sales":0,"status":1},{"id":245,"name":"MINISO名创优品玉桂狗酷洛米捏捏小夜灯卧室睡眠灯床头灯LED灯随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956188247609800.jpg\"]","price":6590,"stock":9999,"sales":0,"status":1},{"id":244,"name":"MINISO名创优品Chiikawa奇幻马戏团毛绒挂件随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956006896154800.jpg\"]","price":5590,"stock":9999,"sales":0,"status":1},{"id":243,"name":"MINISO名创优品WAKUKU哇库库毛毛潮趣第二弹系列单盒盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955886258552300.jpg\"]","price":7590,"stock":9999,"sales":0,"status":1},{"id":242,"name":"MINISO名创优品Chiikawa樱花季吉伊乌萨奇小八零钱包挂件耳机包随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955794487832400.jpg\"]","price":4390,"stock":9999,"sales":0,"status":1},{"id":241,"name":"MINISO名创优品草莓熊休闲毯加厚保暖午睡毯子","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955717135623100.jpg\"]","price":9900,"stock":9999,"sales":0,"status":1},{"id":240,"name":"MINISO名创优品MyMelody秘森茶话会盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955658005155100.jpg\"]","price":3900,"stock":9999,"sales":0,"status":1},{"id":239,"name":"MINISO名创优品【指环爱神系列】2盒随机2款手办盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955515578588700.png\"]","price":6400,"stock":9999,"sales":0,"status":1},{"id":238,"name":"MINISO名创优品 小熊维尼拍立得系列(单盒单款)","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955384656076300.jpg\"]","price":3690,"stock":9999,"sales":0,"status":1},{"id":237,"name":"MINISO名创优品 小熊维尼拍立得系列(端盒8款)盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955301119356700.jpg\"]","price":30000,"stock":999,"sales":0,"status":1},{"id":236,"name":"MINISO名创优品缤纷街景积木第6季五件套拼装积木玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954965838583100.jpg\"]","price":18500,"stock":9999,"sales":0,"status":1},{"id":235,"name":"MINISO名创优品羊咩咩小羊玩偶毛绒公仔","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954804800434800.jpg\"]","price":2190,"stock":9999,"sales":0,"status":1},{"id":234,"name":"MINISO名创优品大师香薰室内家用150ml随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954438035852600.jpg\"]","price":4500,"stock":9999,"sales":0,"status":1},{"id":233,"name":"MINISO名创优品天然植物化妆棉800片白色","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954130792789000.jpg\"]","price":1390,"stock":9999,"sales":0,"status":1},{"id":232,"name":"MINISO名创优品玉桂狗u型枕","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765953971587736200.png\"]","price":3300,"stock":99999,"sales":0,"status":1},{"id":231,"name":"ZIPPO 春华秋实套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765878978681838200.jpg\"]","price":327500,"stock":9999,"sales":0,"status":1},{"id":230,"name":"ZIPPO 觉醒系列打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765877510957720900.jpg\"]","price":145500,"stock":9999,"sales":0,"status":1},{"id":229,"name":"ZIPPO 星际异兽套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765876033609870000.jpg\"]","price":135000,"stock":9999,"sales":0,"status":1},{"id":228,"name":"ZIPPO 幸运灵虫 套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875966491566400.jpg\"]","price":263900,"stock":9999,"sales":0,"status":1},{"id":227,"name":"ZIPPO Jack Daniel联名款——橡木之味打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875891023016800.jpg\"]","price":113800,"stock":9999,"sales":0,"status":1},{"id":226,"name":"ZIPPO八音盒·怦然心动打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875820076074600.jpg\"]","price":119900,"stock":9999,"sales":0,"status":1},{"id":225,"name":"ZIPPO蔷薇水晶礼盒套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875719520558200.jpg\"]","price":185000,"stock":9999,"sales":0,"status":1},{"id":224,"name":"ZIPPO幸运石系列随机款打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875631784700700.jpg\"]","price":58800,"stock":9999,"sales":0,"status":1},{"id":223,"name":"ZIPPO暗花幺點-PVD浸染打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875319301817500.avif\"]","price":33700,"stock":9999,"sales":0,"status":1},{"id":222,"name":"ZIPPO 雪花系列随机款打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875220636810700.jpg\"]","price":38200,"stock":9999,"sales":0,"status":1}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.060145292},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:18:55","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018169708},{"time":"2025-12-22 15:18:55","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/product/product.go:233","sql":"SELECT count(*) FROM `products` WHERE `products`.`status` = 1 AND deleted_at IS NULL AND `products`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.018911958},{"time":"2025-12-22 15:18:55","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/product/product.go:237","sql":"SELECT * FROM `products` WHERE `products`.`status` = 1 AND deleted_at IS NULL AND `products`.`deleted_at` IS NULL ORDER BY `products`.`id` DESC LIMIT 50","rows_affected":50,"cost_seconds":0.022381625}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.060145459}} +{"level":"info","time":"2025-12-22 15:19:00","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/users/813/coupons/add","http_code":200,"business_code":0,"success":true,"cost_seconds":0.138150209,"trace_id":"2003002313928609792","trace_info":{"trace_id":"2003002313928609792","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/users/813/coupons/add","header":{"Content-Type":"application/json"},"body":"{\"coupon_id\":2}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002313928609792"],"Vary":["Origin"]},"body":{"success":true},"http_code":200,"http_code_msg":"OK","cost_seconds":0.138149834},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.017940333},{"time":"2025-12-22 15:19:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/coupon_add.go:15","sql":"SELECT * FROM `system_coupons` WHERE `system_coupons`.`id` = 2 AND deleted_at IS NULL AND `system_coupons`.`deleted_at` IS NULL ORDER BY `system_coupons`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018845042},{"time":"2025-12-22 15:19:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/coupon_add.go:31","sql":"SELECT count(*) FROM `user_coupons` WHERE `user_coupons`.`coupon_id` = 2","rows_affected":1,"cost_seconds":0.019557708},{"time":"2025-12-22 15:19:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/coupon_add.go:54","sql":"INSERT INTO `user_coupons` (`user_id`,`coupon_id`,`valid_start`,`valid_end`,`status`,`balance_amount`) VALUES (813,2,'2025-12-14 00:07:09.383','2026-01-13 00:07:09.383',1,0)","rows_affected":1,"cost_seconds":0.030726834},{"time":"2025-12-22 15:19:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/coupon_add.go:59","sql":"UPDATE `user_coupons` SET `balance_amount`=3000,`updated_at`='2025-12-22 15:19:00.388' WHERE `user_coupons`.`id` = 14","rows_affected":1,"cost_seconds":0.0317125}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.138150209}} +{"level":"info","time":"2025-12-22 15:19:00","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/users?current=1&size=10&page=1&page_size=20","http_code":200,"business_code":0,"success":true,"cost_seconds":0.130994625,"trace_id":"2003002314914271232","trace_info":{"trace_id":"2003002314914271232","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/users?current=1&size=10&page=1&page_size=20","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002314914271232"],"Vary":["Origin"]},"body":{"page":1,"page_size":20,"total":6,"list":[{"id":813,"nickname":"善良的巴乔","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAABmAADpPQ5dAAAAAXRSTlMAQObYZgAAAD9JREFUeJxiAIH//xHkSBdAEofwRrjAKEAFsMQCTzQjXgCRSMDsES4AoWAALj6iBRABNCqAnHlGBRgAAQAA//8HSv0f1XCh/AAAAABJRU5ErkJggg==","invite_code":"AZCHW75Z","inviter_id":0,"created_at":"2025-11-16 13:50:58","douyin_id":"","channel_name":"","channel_code":"","coupons_count":2,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":12,"nickname":"张三","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"S93L5LDY","inviter_id":0,"created_at":"2025-11-14 21:21:44","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":6,"today_consume":11910,"seven_day_consume":11910},{"id":9,"nickname":"贝利求而不得","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"ELBZ6BM8","inviter_id":6,"created_at":"2025-11-14 11:48:55","douyin_id":"","channel_name":"","channel_code":"","coupons_count":2,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":8,"nickname":"欢呼的贝利","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"VUQQ9MYF","inviter_id":0,"created_at":"2025-11-14 11:18:07","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":7,"nickname":"大罗舞力四射","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"23JEKRTQ","inviter_id":0,"created_at":"2025-11-14 11:06:38","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":6,"nickname":"标致的欧文","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"A8ZWET6K","inviter_id":0,"created_at":"2025-11-14 11:06:05","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.130994459},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018204917},{"time":"2025-12-22 15:19:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:91","sql":"SELECT count(*) FROM `users` LEFT JOIN `channels` ON `channels`.`id` = `users`.`channel_id` WHERE `users`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.009475542},{"time":"2025-12-22 15:19:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:102","sql":"SELECT `users`.*,`channels`.`name` AS `channel_name`,`channels`.`code` AS `channel_code` FROM `users` LEFT JOIN `channels` ON `channels`.`id` = `users`.`channel_id` WHERE `users`.`deleted_at` IS NULL ORDER BY `users`.`id` DESC LIMIT 20","rows_affected":-1,"cost_seconds":0.02129975},{"time":"2025-12-22 15:19:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:126","sql":"SELECT `user_coupons`.`user_id`,COUNT(`user_coupons`.`id`) AS `count` FROM `user_coupons` WHERE `user_coupons`.`user_id` IN (813,12,9,8,7,6) AND `user_coupons`.`status` = 1 GROUP BY `user_coupons`.`user_id`","rows_affected":-1,"cost_seconds":0.024214417},{"time":"2025-12-22 15:19:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:145","sql":"SELECT `user_item_cards`.`user_id`,COUNT(`user_item_cards`.`id`) AS `count` FROM `user_item_cards` WHERE `user_item_cards`.`user_id` IN (813,12,9,8,7,6) AND `user_item_cards`.`status` = 1 GROUP BY `user_item_cards`.`user_id`","rows_affected":-1,"cost_seconds":0.018832083},{"time":"2025-12-22 15:19:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:172","sql":"SELECT `orders`.`user_id`,SUM(`orders`.`actual_amount`) AS `amount` FROM `orders` WHERE `orders`.`user_id` IN (813,12,9,8,7,6) AND `orders`.`status` = 2 AND `orders`.`created_at` >= '2025-12-22 00:00:00' GROUP BY `orders`.`user_id`","rows_affected":-1,"cost_seconds":0.019012667},{"time":"2025-12-22 15:19:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:185","sql":"SELECT `orders`.`user_id`,SUM(`orders`.`actual_amount`) AS `amount` FROM `orders` WHERE `orders`.`user_id` IN (813,12,9,8,7,6) AND `orders`.`status` = 2 AND `orders`.`created_at` >= '2025-12-16 00:00:00' GROUP BY `orders`.`user_id`","rows_affected":-1,"cost_seconds":0.019145833}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.130994625}} +{"level":"info","time":"2025-12-22 15:19:03","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/users/813/coupons/add","http_code":400,"business_code":20109,"success":false,"cost_seconds":0.064035833,"trace_id":"2003002328851943424","trace_info":{"trace_id":"2003002328851943424","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/users/813/coupons/add","header":{"Content-Type":"application/json"},"body":"{\"coupon_id\":3}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002328851943424"],"Vary":["Origin"]},"body":null,"business_code":20109,"business_code_msg":"发券失败:模板配额已满","http_code":400,"http_code_msg":"Bad Request","cost_seconds":0.064035583},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:03","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.02086875},{"time":"2025-12-22 15:19:03","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/coupon_add.go:15","sql":"SELECT * FROM `system_coupons` WHERE `system_coupons`.`id` = 3 AND deleted_at IS NULL AND `system_coupons`.`deleted_at` IS NULL ORDER BY `system_coupons`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.023764375},{"time":"2025-12-22 15:19:03","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/coupon_add.go:31","sql":"SELECT count(*) FROM `user_coupons` WHERE `user_coupons`.`coupon_id` = 3","rows_affected":1,"cost_seconds":0.018993041}],"redis":null,"mongos":null,"success":false,"cost_seconds":0.064035833}} +{"level":"info","time":"2025-12-22 15:19:09","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/products?status=1&page=1&page_size=50","http_code":200,"business_code":0,"success":true,"cost_seconds":0.05876575,"trace_id":"2003002351551516672","trace_info":{"trace_id":"2003002351551516672","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/products?status=1&page=1&page_size=50","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002351551516672"],"Vary":["Origin"]},"body":{"page":1,"page_size":50,"total":271,"list":[{"id":271,"name":"LAMY凌美宝珠笔礼盒暗夜绿野奢礼盒","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765974142175986900.jpg\"]","price":87990,"stock":9999,"sales":0,"status":1},{"id":270,"name":"LAMY凌美钢笔礼盒 宋代限定版墨水笔青苍玉兰","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765974064319974300.jpg\"]","price":32990,"stock":9999,"sales":0,"status":1},{"id":269,"name":"LAMY凌美 钢笔(尼克+朱迪)礼盒疯狂动物城联名墨水笔套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973959992705500.jpg\"]","price":100900,"stock":9999,"sales":0,"status":1},{"id":268,"name":"LAMY凌美钢笔礼盒 狩猎系列磨砂黑白色墨水笔套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973806819055400.jpg\"]","price":43900,"stock":9999,"sales":0,"status":1},{"id":267,"name":"LAMY凌美钢笔礼盒 狩猎系列库洛米联名限定套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973718891607700.png\"]","price":50500,"stock":99999,"sales":0,"status":1},{"id":266,"name":"MINISO名创优品U型枕飞机云朵护颈枕记忆棉","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958525893026200.jpg\"]","price":7700,"stock":9999,"sales":0,"status":1},{"id":265,"name":"MINISO名创优品假面骑士×WASA龙联名系列盲盒单盒随机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958389919343600.jpg\"]","price":10490,"stock":9999,"sales":0,"status":1},{"id":264,"name":"MINISO名创优品橘猫工业铁甲小宝第二弹模型玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958320499495500.jpg\"]","price":10800,"stock":9999,"sales":0,"status":1},{"id":263,"name":"MINISO名创优品橘猫工业铁甲小宝第一弹模型玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958282291523200.jpg\"]","price":10800,"stock":9999,"sales":0,"status":1},{"id":262,"name":"MINISO名创Nanci囡茜氛围灯积木摆件中国风小夜灯","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958213519986200.png\"]","price":10900,"stock":9999,"sales":0,"status":1},{"id":261,"name":"名创优品迪士尼316不锈钢保温饭盒便当盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958147297026300.jpg\"]","price":8900,"stock":9999,"sales":0,"status":1},{"id":260,"name":"MINISO名创优品学院系列焦糖格纹双肩包包学院风","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958088190736900.jpg\"]","price":11500,"stock":9999,"sales":0,"status":1},{"id":259,"name":"MINISO/名创优品迪士尼朱迪的警官生活系列痛包","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957990772169000.jpg\"]","price":13090,"stock":9999,"sales":0,"status":1},{"id":258,"name":"MINISO名创优品雪夜派对拼装模型圣诞节礼物","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957900503621400.jpg\"]","price":13900,"stock":9999,"sales":0,"status":1},{"id":257,"name":"MINISO名创优品【深睡记忆枕】透气枕芯枕头护颈椎释压","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957819453538400.png\"]","price":14590,"stock":9999,"sales":0,"status":1},{"id":256,"name":"MINISO名创优品香薰氛围感蓝牙音乐香氛机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957751042251600.jpg\"]","price":24500,"stock":9999,"sales":0,"status":1},{"id":255,"name":"MINISO名创优品铠魂帝皇侠15周年典藏模玩机甲超可动人偶成品模型","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957636770820900.jpg\"]","price":27500,"stock":9999,"sales":0,"status":1},{"id":254,"name":"MINISO名创优品冰淇淋香薰膏室内卫生间清新芬芳花果香4盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957419425272100.jpg\"]","price":4390,"stock":9999,"sales":0,"status":1},{"id":253,"name":"MINISO名创优品硅胶热水袋","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957295282004700.jpg\"]","price":5790,"stock":9999,"sales":0,"status":1},{"id":252,"name":"MINISO名创优品Chiikawa系列毛茸茸季斗篷休闲午休毯随机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957103956331200.jpg\"]","price":16390,"stock":9999,"sales":0,"status":1},{"id":251,"name":"MINISO名创优品罗小黑轩影摇香系列迷你挂画冰箱贴五款盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957004993236600.jpg\"]","price":13990,"stock":9999,"sales":0,"status":1},{"id":250,"name":"MINISO名创优品chiikawa系列睡衣毛绒公仔","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956849972873300.jpg\"]","price":10900,"stock":9999,"sales":0,"status":1},{"id":249,"name":"MINISO名创优品蜡笔小新拼图","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956726589144000.png\"]","price":8890,"stock":9999,"sales":0,"status":1},{"id":248,"name":"MINISO名创优品三丽鸥保温杯公仔冰霸杯","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956569488102100.jpg\"]","price":8890,"stock":9999,"sales":0,"status":1},{"id":247,"name":"MINISO名创优品蜡笔小新超变装毛绒眼罩","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956491344273300.jpg\"]","price":1990,"stock":9999,"sales":0,"status":1},{"id":246,"name":"MINISO名创优品迪士尼疯狂动物城搪胶毛绒公仔盲盒摆件","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956361082178100.jpg\"]","price":8690,"stock":9999,"sales":0,"status":1},{"id":245,"name":"MINISO名创优品玉桂狗酷洛米捏捏小夜灯卧室睡眠灯床头灯LED灯随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956188247609800.jpg\"]","price":6590,"stock":9999,"sales":0,"status":1},{"id":244,"name":"MINISO名创优品Chiikawa奇幻马戏团毛绒挂件随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956006896154800.jpg\"]","price":5590,"stock":9999,"sales":0,"status":1},{"id":243,"name":"MINISO名创优品WAKUKU哇库库毛毛潮趣第二弹系列单盒盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955886258552300.jpg\"]","price":7590,"stock":9999,"sales":0,"status":1},{"id":242,"name":"MINISO名创优品Chiikawa樱花季吉伊乌萨奇小八零钱包挂件耳机包随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955794487832400.jpg\"]","price":4390,"stock":9999,"sales":0,"status":1},{"id":241,"name":"MINISO名创优品草莓熊休闲毯加厚保暖午睡毯子","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955717135623100.jpg\"]","price":9900,"stock":9999,"sales":0,"status":1},{"id":240,"name":"MINISO名创优品MyMelody秘森茶话会盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955658005155100.jpg\"]","price":3900,"stock":9999,"sales":0,"status":1},{"id":239,"name":"MINISO名创优品【指环爱神系列】2盒随机2款手办盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955515578588700.png\"]","price":6400,"stock":9999,"sales":0,"status":1},{"id":238,"name":"MINISO名创优品 小熊维尼拍立得系列(单盒单款)","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955384656076300.jpg\"]","price":3690,"stock":9999,"sales":0,"status":1},{"id":237,"name":"MINISO名创优品 小熊维尼拍立得系列(端盒8款)盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955301119356700.jpg\"]","price":30000,"stock":999,"sales":0,"status":1},{"id":236,"name":"MINISO名创优品缤纷街景积木第6季五件套拼装积木玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954965838583100.jpg\"]","price":18500,"stock":9999,"sales":0,"status":1},{"id":235,"name":"MINISO名创优品羊咩咩小羊玩偶毛绒公仔","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954804800434800.jpg\"]","price":2190,"stock":9999,"sales":0,"status":1},{"id":234,"name":"MINISO名创优品大师香薰室内家用150ml随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954438035852600.jpg\"]","price":4500,"stock":9999,"sales":0,"status":1},{"id":233,"name":"MINISO名创优品天然植物化妆棉800片白色","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954130792789000.jpg\"]","price":1390,"stock":9999,"sales":0,"status":1},{"id":232,"name":"MINISO名创优品玉桂狗u型枕","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765953971587736200.png\"]","price":3300,"stock":99999,"sales":0,"status":1},{"id":231,"name":"ZIPPO 春华秋实套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765878978681838200.jpg\"]","price":327500,"stock":9999,"sales":0,"status":1},{"id":230,"name":"ZIPPO 觉醒系列打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765877510957720900.jpg\"]","price":145500,"stock":9999,"sales":0,"status":1},{"id":229,"name":"ZIPPO 星际异兽套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765876033609870000.jpg\"]","price":135000,"stock":9999,"sales":0,"status":1},{"id":228,"name":"ZIPPO 幸运灵虫 套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875966491566400.jpg\"]","price":263900,"stock":9999,"sales":0,"status":1},{"id":227,"name":"ZIPPO Jack Daniel联名款——橡木之味打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875891023016800.jpg\"]","price":113800,"stock":9999,"sales":0,"status":1},{"id":226,"name":"ZIPPO八音盒·怦然心动打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875820076074600.jpg\"]","price":119900,"stock":9999,"sales":0,"status":1},{"id":225,"name":"ZIPPO蔷薇水晶礼盒套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875719520558200.jpg\"]","price":185000,"stock":9999,"sales":0,"status":1},{"id":224,"name":"ZIPPO幸运石系列随机款打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875631784700700.jpg\"]","price":58800,"stock":9999,"sales":0,"status":1},{"id":223,"name":"ZIPPO暗花幺點-PVD浸染打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875319301817500.avif\"]","price":33700,"stock":9999,"sales":0,"status":1},{"id":222,"name":"ZIPPO 雪花系列随机款打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875220636810700.jpg\"]","price":38200,"stock":9999,"sales":0,"status":1}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.058765584},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018969917},{"time":"2025-12-22 15:19:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/product/product.go:233","sql":"SELECT count(*) FROM `products` WHERE `products`.`status` = 1 AND deleted_at IS NULL AND `products`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.018918084},{"time":"2025-12-22 15:19:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/product/product.go:237","sql":"SELECT * FROM `products` WHERE `products`.`status` = 1 AND deleted_at IS NULL AND `products`.`deleted_at` IS NULL ORDER BY `products`.`id` DESC LIMIT 50","rows_affected":50,"cost_seconds":0.020412583}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.05876575}} +{"level":"info","time":"2025-12-22 15:19:16","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/users/813/item_cards","http_code":200,"business_code":0,"success":true,"cost_seconds":0.274525,"trace_id":"2003002382425788416","trace_info":{"trace_id":"2003002382425788416","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/users/813/item_cards","header":{"Content-Type":"application/json"},"body":"{\"card_id\":1,\"quantity\":5}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002382425788416"],"Vary":["Origin"]},"body":{"message":"操作成功"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.274524667},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:16","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.021193542},{"time":"2025-12-22 15:19:16","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/item_card_add.go:35","sql":"SELECT * FROM `system_item_cards` WHERE `system_item_cards`.`id` = 1 AND deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL ORDER BY `system_item_cards`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.020958417},{"time":"2025-12-22 15:19:16","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/item_card_add.go:47","sql":"SELECT count(*) FROM `user_item_cards` WHERE `user_item_cards`.`user_id` = 813 AND `user_item_cards`.`card_id` = 1 AND `user_item_cards`.`status` = 1","rows_affected":1,"cost_seconds":0.020575917},{"time":"2025-12-22 15:19:16","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/item_card_add.go:76","sql":"INSERT INTO `user_item_cards` (`user_id`,`card_id`,`valid_start`,`valid_end`,`status`,`remark`) VALUES (813,1,'2025-11-26 00:00:00','2025-12-25 00:00:00',1,'')","rows_affected":1,"cost_seconds":0.032856167},{"time":"2025-12-22 15:19:16","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/item_card_add.go:76","sql":"INSERT INTO `user_item_cards` (`user_id`,`card_id`,`valid_start`,`valid_end`,`status`,`remark`) VALUES (813,1,'2025-11-26 00:00:00','2025-12-25 00:00:00',1,'')","rows_affected":1,"cost_seconds":0.032290667},{"time":"2025-12-22 15:19:16","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/item_card_add.go:76","sql":"INSERT INTO `user_item_cards` (`user_id`,`card_id`,`valid_start`,`valid_end`,`status`,`remark`) VALUES (813,1,'2025-11-26 00:00:00','2025-12-25 00:00:00',1,'')","rows_affected":1,"cost_seconds":0.031740291},{"time":"2025-12-22 15:19:16","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/item_card_add.go:76","sql":"INSERT INTO `user_item_cards` (`user_id`,`card_id`,`valid_start`,`valid_end`,`status`,`remark`) VALUES (813,1,'2025-11-26 00:00:00','2025-12-25 00:00:00',1,'')","rows_affected":1,"cost_seconds":0.033063209},{"time":"2025-12-22 15:19:16","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/item_card_add.go:76","sql":"INSERT INTO `user_item_cards` (`user_id`,`card_id`,`valid_start`,`valid_end`,`status`,`remark`) VALUES (813,1,'2025-11-26 00:00:00','2025-12-25 00:00:00',1,'')","rows_affected":1,"cost_seconds":0.030292709}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.274525}} +{"level":"info","time":"2025-12-22 15:19:17","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/users?current=1&size=10&page=1&page_size=20","http_code":200,"business_code":0,"success":true,"cost_seconds":0.127396125,"trace_id":"2003002384028012544","trace_info":{"trace_id":"2003002384028012544","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/users?current=1&size=10&page=1&page_size=20","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002384028012544"],"Vary":["Origin"]},"body":{"page":1,"page_size":20,"total":6,"list":[{"id":813,"nickname":"善良的巴乔","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAABmAADpPQ5dAAAAAXRSTlMAQObYZgAAAD9JREFUeJxiAIH//xHkSBdAEofwRrjAKEAFsMQCTzQjXgCRSMDsES4AoWAALj6iBRABNCqAnHlGBRgAAQAA//8HSv0f1XCh/AAAAABJRU5ErkJggg==","invite_code":"AZCHW75Z","inviter_id":0,"created_at":"2025-11-16 13:50:58","douyin_id":"","channel_name":"","channel_code":"","coupons_count":2,"item_cards_count":5,"today_consume":0,"seven_day_consume":0},{"id":12,"nickname":"张三","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"S93L5LDY","inviter_id":0,"created_at":"2025-11-14 21:21:44","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":6,"today_consume":11910,"seven_day_consume":11910},{"id":9,"nickname":"贝利求而不得","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"ELBZ6BM8","inviter_id":6,"created_at":"2025-11-14 11:48:55","douyin_id":"","channel_name":"","channel_code":"","coupons_count":2,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":8,"nickname":"欢呼的贝利","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"VUQQ9MYF","inviter_id":0,"created_at":"2025-11-14 11:18:07","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":7,"nickname":"大罗舞力四射","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"23JEKRTQ","inviter_id":0,"created_at":"2025-11-14 11:06:38","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":6,"nickname":"标致的欧文","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"A8ZWET6K","inviter_id":0,"created_at":"2025-11-14 11:06:05","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.127395625},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.019163},{"time":"2025-12-22 15:19:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:91","sql":"SELECT count(*) FROM `users` LEFT JOIN `channels` ON `channels`.`id` = `users`.`channel_id` WHERE `users`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.010377916},{"time":"2025-12-22 15:19:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:102","sql":"SELECT `users`.*,`channels`.`name` AS `channel_name`,`channels`.`code` AS `channel_code` FROM `users` LEFT JOIN `channels` ON `channels`.`id` = `users`.`channel_id` WHERE `users`.`deleted_at` IS NULL ORDER BY `users`.`id` DESC LIMIT 20","rows_affected":-1,"cost_seconds":0.019845083},{"time":"2025-12-22 15:19:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:126","sql":"SELECT `user_coupons`.`user_id`,COUNT(`user_coupons`.`id`) AS `count` FROM `user_coupons` WHERE `user_coupons`.`user_id` IN (813,12,9,8,7,6) AND `user_coupons`.`status` = 1 GROUP BY `user_coupons`.`user_id`","rows_affected":-1,"cost_seconds":0.019907791},{"time":"2025-12-22 15:19:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:145","sql":"SELECT `user_item_cards`.`user_id`,COUNT(`user_item_cards`.`id`) AS `count` FROM `user_item_cards` WHERE `user_item_cards`.`user_id` IN (813,12,9,8,7,6) AND `user_item_cards`.`status` = 1 GROUP BY `user_item_cards`.`user_id`","rows_affected":-1,"cost_seconds":0.018978792},{"time":"2025-12-22 15:19:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:172","sql":"SELECT `orders`.`user_id`,SUM(`orders`.`actual_amount`) AS `amount` FROM `orders` WHERE `orders`.`user_id` IN (813,12,9,8,7,6) AND `orders`.`status` = 2 AND `orders`.`created_at` >= '2025-12-22 00:00:00' GROUP BY `orders`.`user_id`","rows_affected":-1,"cost_seconds":0.018974625},{"time":"2025-12-22 15:19:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:185","sql":"SELECT `orders`.`user_id`,SUM(`orders`.`actual_amount`) AS `amount` FROM `orders` WHERE `orders`.`user_id` IN (813,12,9,8,7,6) AND `orders`.`status` = 2 AND `orders`.`created_at` >= '2025-12-16 00:00:00' GROUP BY `orders`.`user_id`","rows_affected":-1,"cost_seconds":0.01886375}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.127396125}} +{"level":"info","time":"2025-12-22 15:19:19","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/products?status=1&page=1&page_size=50","http_code":200,"business_code":0,"success":true,"cost_seconds":0.060376875,"trace_id":"2003002394060787712","trace_info":{"trace_id":"2003002394060787712","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/products?status=1&page=1&page_size=50","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002394060787712"],"Vary":["Origin"]},"body":{"page":1,"page_size":50,"total":271,"list":[{"id":271,"name":"LAMY凌美宝珠笔礼盒暗夜绿野奢礼盒","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765974142175986900.jpg\"]","price":87990,"stock":9999,"sales":0,"status":1},{"id":270,"name":"LAMY凌美钢笔礼盒 宋代限定版墨水笔青苍玉兰","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765974064319974300.jpg\"]","price":32990,"stock":9999,"sales":0,"status":1},{"id":269,"name":"LAMY凌美 钢笔(尼克+朱迪)礼盒疯狂动物城联名墨水笔套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973959992705500.jpg\"]","price":100900,"stock":9999,"sales":0,"status":1},{"id":268,"name":"LAMY凌美钢笔礼盒 狩猎系列磨砂黑白色墨水笔套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973806819055400.jpg\"]","price":43900,"stock":9999,"sales":0,"status":1},{"id":267,"name":"LAMY凌美钢笔礼盒 狩猎系列库洛米联名限定套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973718891607700.png\"]","price":50500,"stock":99999,"sales":0,"status":1},{"id":266,"name":"MINISO名创优品U型枕飞机云朵护颈枕记忆棉","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958525893026200.jpg\"]","price":7700,"stock":9999,"sales":0,"status":1},{"id":265,"name":"MINISO名创优品假面骑士×WASA龙联名系列盲盒单盒随机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958389919343600.jpg\"]","price":10490,"stock":9999,"sales":0,"status":1},{"id":264,"name":"MINISO名创优品橘猫工业铁甲小宝第二弹模型玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958320499495500.jpg\"]","price":10800,"stock":9999,"sales":0,"status":1},{"id":263,"name":"MINISO名创优品橘猫工业铁甲小宝第一弹模型玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958282291523200.jpg\"]","price":10800,"stock":9999,"sales":0,"status":1},{"id":262,"name":"MINISO名创Nanci囡茜氛围灯积木摆件中国风小夜灯","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958213519986200.png\"]","price":10900,"stock":9999,"sales":0,"status":1},{"id":261,"name":"名创优品迪士尼316不锈钢保温饭盒便当盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958147297026300.jpg\"]","price":8900,"stock":9999,"sales":0,"status":1},{"id":260,"name":"MINISO名创优品学院系列焦糖格纹双肩包包学院风","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958088190736900.jpg\"]","price":11500,"stock":9999,"sales":0,"status":1},{"id":259,"name":"MINISO/名创优品迪士尼朱迪的警官生活系列痛包","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957990772169000.jpg\"]","price":13090,"stock":9999,"sales":0,"status":1},{"id":258,"name":"MINISO名创优品雪夜派对拼装模型圣诞节礼物","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957900503621400.jpg\"]","price":13900,"stock":9999,"sales":0,"status":1},{"id":257,"name":"MINISO名创优品【深睡记忆枕】透气枕芯枕头护颈椎释压","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957819453538400.png\"]","price":14590,"stock":9999,"sales":0,"status":1},{"id":256,"name":"MINISO名创优品香薰氛围感蓝牙音乐香氛机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957751042251600.jpg\"]","price":24500,"stock":9999,"sales":0,"status":1},{"id":255,"name":"MINISO名创优品铠魂帝皇侠15周年典藏模玩机甲超可动人偶成品模型","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957636770820900.jpg\"]","price":27500,"stock":9999,"sales":0,"status":1},{"id":254,"name":"MINISO名创优品冰淇淋香薰膏室内卫生间清新芬芳花果香4盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957419425272100.jpg\"]","price":4390,"stock":9999,"sales":0,"status":1},{"id":253,"name":"MINISO名创优品硅胶热水袋","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957295282004700.jpg\"]","price":5790,"stock":9999,"sales":0,"status":1},{"id":252,"name":"MINISO名创优品Chiikawa系列毛茸茸季斗篷休闲午休毯随机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957103956331200.jpg\"]","price":16390,"stock":9999,"sales":0,"status":1},{"id":251,"name":"MINISO名创优品罗小黑轩影摇香系列迷你挂画冰箱贴五款盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957004993236600.jpg\"]","price":13990,"stock":9999,"sales":0,"status":1},{"id":250,"name":"MINISO名创优品chiikawa系列睡衣毛绒公仔","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956849972873300.jpg\"]","price":10900,"stock":9999,"sales":0,"status":1},{"id":249,"name":"MINISO名创优品蜡笔小新拼图","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956726589144000.png\"]","price":8890,"stock":9999,"sales":0,"status":1},{"id":248,"name":"MINISO名创优品三丽鸥保温杯公仔冰霸杯","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956569488102100.jpg\"]","price":8890,"stock":9999,"sales":0,"status":1},{"id":247,"name":"MINISO名创优品蜡笔小新超变装毛绒眼罩","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956491344273300.jpg\"]","price":1990,"stock":9999,"sales":0,"status":1},{"id":246,"name":"MINISO名创优品迪士尼疯狂动物城搪胶毛绒公仔盲盒摆件","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956361082178100.jpg\"]","price":8690,"stock":9999,"sales":0,"status":1},{"id":245,"name":"MINISO名创优品玉桂狗酷洛米捏捏小夜灯卧室睡眠灯床头灯LED灯随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956188247609800.jpg\"]","price":6590,"stock":9999,"sales":0,"status":1},{"id":244,"name":"MINISO名创优品Chiikawa奇幻马戏团毛绒挂件随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956006896154800.jpg\"]","price":5590,"stock":9999,"sales":0,"status":1},{"id":243,"name":"MINISO名创优品WAKUKU哇库库毛毛潮趣第二弹系列单盒盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955886258552300.jpg\"]","price":7590,"stock":9999,"sales":0,"status":1},{"id":242,"name":"MINISO名创优品Chiikawa樱花季吉伊乌萨奇小八零钱包挂件耳机包随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955794487832400.jpg\"]","price":4390,"stock":9999,"sales":0,"status":1},{"id":241,"name":"MINISO名创优品草莓熊休闲毯加厚保暖午睡毯子","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955717135623100.jpg\"]","price":9900,"stock":9999,"sales":0,"status":1},{"id":240,"name":"MINISO名创优品MyMelody秘森茶话会盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955658005155100.jpg\"]","price":3900,"stock":9999,"sales":0,"status":1},{"id":239,"name":"MINISO名创优品【指环爱神系列】2盒随机2款手办盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955515578588700.png\"]","price":6400,"stock":9999,"sales":0,"status":1},{"id":238,"name":"MINISO名创优品 小熊维尼拍立得系列(单盒单款)","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955384656076300.jpg\"]","price":3690,"stock":9999,"sales":0,"status":1},{"id":237,"name":"MINISO名创优品 小熊维尼拍立得系列(端盒8款)盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955301119356700.jpg\"]","price":30000,"stock":999,"sales":0,"status":1},{"id":236,"name":"MINISO名创优品缤纷街景积木第6季五件套拼装积木玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954965838583100.jpg\"]","price":18500,"stock":9999,"sales":0,"status":1},{"id":235,"name":"MINISO名创优品羊咩咩小羊玩偶毛绒公仔","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954804800434800.jpg\"]","price":2190,"stock":9999,"sales":0,"status":1},{"id":234,"name":"MINISO名创优品大师香薰室内家用150ml随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954438035852600.jpg\"]","price":4500,"stock":9999,"sales":0,"status":1},{"id":233,"name":"MINISO名创优品天然植物化妆棉800片白色","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954130792789000.jpg\"]","price":1390,"stock":9999,"sales":0,"status":1},{"id":232,"name":"MINISO名创优品玉桂狗u型枕","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765953971587736200.png\"]","price":3300,"stock":99999,"sales":0,"status":1},{"id":231,"name":"ZIPPO 春华秋实套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765878978681838200.jpg\"]","price":327500,"stock":9999,"sales":0,"status":1},{"id":230,"name":"ZIPPO 觉醒系列打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765877510957720900.jpg\"]","price":145500,"stock":9999,"sales":0,"status":1},{"id":229,"name":"ZIPPO 星际异兽套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765876033609870000.jpg\"]","price":135000,"stock":9999,"sales":0,"status":1},{"id":228,"name":"ZIPPO 幸运灵虫 套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875966491566400.jpg\"]","price":263900,"stock":9999,"sales":0,"status":1},{"id":227,"name":"ZIPPO Jack Daniel联名款——橡木之味打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875891023016800.jpg\"]","price":113800,"stock":9999,"sales":0,"status":1},{"id":226,"name":"ZIPPO八音盒·怦然心动打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875820076074600.jpg\"]","price":119900,"stock":9999,"sales":0,"status":1},{"id":225,"name":"ZIPPO蔷薇水晶礼盒套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875719520558200.jpg\"]","price":185000,"stock":9999,"sales":0,"status":1},{"id":224,"name":"ZIPPO幸运石系列随机款打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875631784700700.jpg\"]","price":58800,"stock":9999,"sales":0,"status":1},{"id":223,"name":"ZIPPO暗花幺點-PVD浸染打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875319301817500.avif\"]","price":33700,"stock":9999,"sales":0,"status":1},{"id":222,"name":"ZIPPO 雪花系列随机款打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875220636810700.jpg\"]","price":38200,"stock":9999,"sales":0,"status":1}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.060376625},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:19","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.017686042},{"time":"2025-12-22 15:19:19","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/product/product.go:233","sql":"SELECT count(*) FROM `products` WHERE `products`.`status` = 1 AND deleted_at IS NULL AND `products`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.018827292},{"time":"2025-12-22 15:19:19","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/product/product.go:237","sql":"SELECT * FROM `products` WHERE `products`.`status` = 1 AND deleted_at IS NULL AND `products`.`deleted_at` IS NULL ORDER BY `products`.`id` DESC LIMIT 50","rows_affected":50,"cost_seconds":0.022805291}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.060376875}} +{"level":"info","time":"2025-12-22 15:19:21","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/users/12/coupons/add","http_code":400,"business_code":20109,"success":false,"cost_seconds":0.059733333,"trace_id":"2003002403711881216","trace_info":{"trace_id":"2003002403711881216","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/users/12/coupons/add","header":{"Content-Type":"application/json"},"body":"{\"coupon_id\":3}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002403711881216"],"Vary":["Origin"]},"body":null,"business_code":20109,"business_code_msg":"发券失败:模板配额已满","http_code":400,"http_code_msg":"Bad Request","cost_seconds":0.059732833},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:21","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.017528125},{"time":"2025-12-22 15:19:21","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/coupon_add.go:15","sql":"SELECT * FROM `system_coupons` WHERE `system_coupons`.`id` = 3 AND deleted_at IS NULL AND `system_coupons`.`deleted_at` IS NULL ORDER BY `system_coupons`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.021858125},{"time":"2025-12-22 15:19:21","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/coupon_add.go:31","sql":"SELECT count(*) FROM `user_coupons` WHERE `user_coupons`.`coupon_id` = 3","rows_affected":1,"cost_seconds":0.019328458}],"redis":null,"mongos":null,"success":false,"cost_seconds":0.059733333}} +{"level":"info","time":"2025-12-22 15:19:32","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_coupons?page=1&page_size=10&name=","http_code":200,"business_code":0,"success":true,"cost_seconds":0.10748375,"trace_id":"2003002447852736512","trace_info":{"trace_id":"2003002447852736512","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_coupons?page=1&page_size=10&name=","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002447852736512"],"Vary":["Origin"]},"body":{"page":1,"page_size":10,"total":3,"list":[{"id":3,"name":"1000元优惠券","status":1,"coupon_type":1,"discount_type":1,"discount_value":100000,"min_amount":100,"max_discount":0,"valid_days":30,"total_quantity":3,"used_quantity":3,"created_at":"2025-12-14 17:31:24"},{"id":2,"name":"30元优惠券","status":1,"coupon_type":1,"discount_type":1,"discount_value":3000,"min_amount":100,"max_discount":0,"valid_days":30,"total_quantity":1000,"used_quantity":5,"created_at":"2025-12-08 01:40:47"},{"id":1,"name":"优惠券1 ","status":1,"coupon_type":1,"discount_type":1,"discount_value":10000,"min_amount":100,"max_discount":0,"valid_days":30,"total_quantity":1100,"used_quantity":5,"created_at":"2025-11-14 20:19:47"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.107483291},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.019121125},{"time":"2025-12-22 15:19:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:202","sql":"SELECT count(*) FROM `system_coupons` WHERE deleted_at IS NULL AND `system_coupons`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.009683708},{"time":"2025-12-22 15:19:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:221","sql":"SELECT * FROM `system_coupons` WHERE `system_coupons`.`deleted_at` IS NULL ORDER BY `system_coupons`.`id` DESC LIMIT 10","rows_affected":-1,"cost_seconds":0.019734166},{"time":"2025-12-22 15:19:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:236","sql":"SELECT count(*) FROM `user_coupons` WHERE `user_coupons`.`coupon_id` = 3","rows_affected":1,"cost_seconds":0.019944917},{"time":"2025-12-22 15:19:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:236","sql":"SELECT count(*) FROM `user_coupons` WHERE `user_coupons`.`coupon_id` = 2","rows_affected":1,"cost_seconds":0.019831041},{"time":"2025-12-22 15:19:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:236","sql":"SELECT count(*) FROM `user_coupons` WHERE `user_coupons`.`coupon_id` = 1","rows_affected":1,"cost_seconds":0.018018084}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.10748375}} +{"level":"info","time":"2025-12-22 15:19:32","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_item_cards?page=1&page_size=10&name=","http_code":200,"business_code":0,"success":true,"cost_seconds":0.052808375,"trace_id":"2003002450742611968","trace_info":{"trace_id":"2003002450742611968","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_item_cards?page=1&page_size=10&name=","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002450742611968"],"Vary":["Origin"]},"body":{"page":1,"page_size":10,"total":1,"list":[{"id":1,"created_at":"2025-11-14T18:46:15.338+08:00","updated_at":"2025-12-10T22:33:28.156+08:00","name":"双倍快乐水","status":1,"card_type":1,"scope_type":1,"activity_category_id":0,"activity_id":0,"issue_id":0,"price":1900,"valid_start":"2025-11-26T00:00:00+08:00","valid_end":"2025-12-25T00:00:00+08:00","effect_type":1,"reward_multiplier_x1000":0,"boost_rate_x1000":10,"stacking_strategy":1,"max_effect_value_x1000":0,"remark":"备注个锤子啊"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.052807875},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.01858925},{"time":"2025-12-22 15:19:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:326","sql":"SELECT count(*) FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.00965375},{"time":"2025-12-22 15:19:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:340","sql":"SELECT * FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL ORDER BY `system_item_cards`.`id` DESC LIMIT 10","rows_affected":1,"cost_seconds":0.0238645}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.052808375}} +{"level":"info","time":"2025-12-22 15:19:41","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"PUT","path":"/api/admin/system_coupons/3","http_code":200,"business_code":0,"success":true,"cost_seconds":0.075655333,"trace_id":"2003002488499736576","trace_info":{"trace_id":"2003002488499736576","request":{"ttl":"un-limit","method":"PUT","decoded_url":"/api/admin/system_coupons/3","header":{"Content-Type":"application/json"},"body":"{\"name\":\"1000元优惠券\",\"coupon_type\":1,\"discount_type\":1,\"discount_value\":100000,\"min_amount\":100,\"max_discount\":0,\"valid_days\":30,\"total_quantity\":0,\"status\":1,\"remark\":\"\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002488499736576"],"Vary":["Origin"]},"body":{"message":"操作成功"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.075654875},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:41","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.020639417},{"time":"2025-12-22 15:19:41","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:126","sql":"UPDATE `system_coupons` SET `discount_type`=1,`discount_value`=100000,`min_spend`=100,`name`='1000元优惠券',`scope_type`=1,`status`=1,`total_quantity`=0,`valid_end`='2026-01-21 15:19:41.923',`valid_start`='2025-12-22 15:19:41.923',`updated_at`='2025-12-22 15:19:41.934' WHERE `system_coupons`.`id` = 3 AND `system_coupons`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.043898416}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.075655333}} +{"level":"info","time":"2025-12-22 15:19:42","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_coupons?page=1&page_size=10&name=","http_code":200,"business_code":0,"success":true,"cost_seconds":0.113680875,"trace_id":"2003002489279877120","trace_info":{"trace_id":"2003002489279877120","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_coupons?page=1&page_size=10&name=","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002489279877120"],"Vary":["Origin"]},"body":{"page":1,"page_size":10,"total":3,"list":[{"id":3,"name":"1000元优惠券","status":1,"coupon_type":1,"discount_type":1,"discount_value":100000,"min_amount":100,"max_discount":0,"valid_days":30,"total_quantity":0,"used_quantity":3,"created_at":"2025-12-14 17:31:24"},{"id":2,"name":"30元优惠券","status":1,"coupon_type":1,"discount_type":1,"discount_value":3000,"min_amount":100,"max_discount":0,"valid_days":30,"total_quantity":1000,"used_quantity":5,"created_at":"2025-12-08 01:40:47"},{"id":1,"name":"优惠券1 ","status":1,"coupon_type":1,"discount_type":1,"discount_value":10000,"min_amount":100,"max_discount":0,"valid_days":30,"total_quantity":1100,"used_quantity":5,"created_at":"2025-11-14 20:19:47"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.1136805},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:42","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.020541125},{"time":"2025-12-22 15:19:42","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:202","sql":"SELECT count(*) FROM `system_coupons` WHERE deleted_at IS NULL AND `system_coupons`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.010293792},{"time":"2025-12-22 15:19:42","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:221","sql":"SELECT * FROM `system_coupons` WHERE `system_coupons`.`deleted_at` IS NULL ORDER BY `system_coupons`.`id` DESC LIMIT 10","rows_affected":-1,"cost_seconds":0.019490958},{"time":"2025-12-22 15:19:42","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:236","sql":"SELECT count(*) FROM `user_coupons` WHERE `user_coupons`.`coupon_id` = 3","rows_affected":1,"cost_seconds":0.019987667},{"time":"2025-12-22 15:19:42","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:236","sql":"SELECT count(*) FROM `user_coupons` WHERE `user_coupons`.`coupon_id` = 2","rows_affected":1,"cost_seconds":0.02166675},{"time":"2025-12-22 15:19:42","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:236","sql":"SELECT count(*) FROM `user_coupons` WHERE `user_coupons`.`coupon_id` = 1","rows_affected":1,"cost_seconds":0.020260459}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.113680875}} +{"level":"info","time":"2025-12-22 15:19:50","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/products?status=1&page=1&page_size=50","http_code":200,"business_code":0,"success":true,"cost_seconds":0.063555541,"trace_id":"2003002525749350400","trace_info":{"trace_id":"2003002525749350400","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/products?status=1&page=1&page_size=50","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002525749350400"],"Vary":["Origin"]},"body":{"page":1,"page_size":50,"total":271,"list":[{"id":271,"name":"LAMY凌美宝珠笔礼盒暗夜绿野奢礼盒","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765974142175986900.jpg\"]","price":87990,"stock":9999,"sales":0,"status":1},{"id":270,"name":"LAMY凌美钢笔礼盒 宋代限定版墨水笔青苍玉兰","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765974064319974300.jpg\"]","price":32990,"stock":9999,"sales":0,"status":1},{"id":269,"name":"LAMY凌美 钢笔(尼克+朱迪)礼盒疯狂动物城联名墨水笔套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973959992705500.jpg\"]","price":100900,"stock":9999,"sales":0,"status":1},{"id":268,"name":"LAMY凌美钢笔礼盒 狩猎系列磨砂黑白色墨水笔套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973806819055400.jpg\"]","price":43900,"stock":9999,"sales":0,"status":1},{"id":267,"name":"LAMY凌美钢笔礼盒 狩猎系列库洛米联名限定套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973718891607700.png\"]","price":50500,"stock":99999,"sales":0,"status":1},{"id":266,"name":"MINISO名创优品U型枕飞机云朵护颈枕记忆棉","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958525893026200.jpg\"]","price":7700,"stock":9999,"sales":0,"status":1},{"id":265,"name":"MINISO名创优品假面骑士×WASA龙联名系列盲盒单盒随机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958389919343600.jpg\"]","price":10490,"stock":9999,"sales":0,"status":1},{"id":264,"name":"MINISO名创优品橘猫工业铁甲小宝第二弹模型玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958320499495500.jpg\"]","price":10800,"stock":9999,"sales":0,"status":1},{"id":263,"name":"MINISO名创优品橘猫工业铁甲小宝第一弹模型玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958282291523200.jpg\"]","price":10800,"stock":9999,"sales":0,"status":1},{"id":262,"name":"MINISO名创Nanci囡茜氛围灯积木摆件中国风小夜灯","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958213519986200.png\"]","price":10900,"stock":9999,"sales":0,"status":1},{"id":261,"name":"名创优品迪士尼316不锈钢保温饭盒便当盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958147297026300.jpg\"]","price":8900,"stock":9999,"sales":0,"status":1},{"id":260,"name":"MINISO名创优品学院系列焦糖格纹双肩包包学院风","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958088190736900.jpg\"]","price":11500,"stock":9999,"sales":0,"status":1},{"id":259,"name":"MINISO/名创优品迪士尼朱迪的警官生活系列痛包","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957990772169000.jpg\"]","price":13090,"stock":9999,"sales":0,"status":1},{"id":258,"name":"MINISO名创优品雪夜派对拼装模型圣诞节礼物","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957900503621400.jpg\"]","price":13900,"stock":9999,"sales":0,"status":1},{"id":257,"name":"MINISO名创优品【深睡记忆枕】透气枕芯枕头护颈椎释压","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957819453538400.png\"]","price":14590,"stock":9999,"sales":0,"status":1},{"id":256,"name":"MINISO名创优品香薰氛围感蓝牙音乐香氛机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957751042251600.jpg\"]","price":24500,"stock":9999,"sales":0,"status":1},{"id":255,"name":"MINISO名创优品铠魂帝皇侠15周年典藏模玩机甲超可动人偶成品模型","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957636770820900.jpg\"]","price":27500,"stock":9999,"sales":0,"status":1},{"id":254,"name":"MINISO名创优品冰淇淋香薰膏室内卫生间清新芬芳花果香4盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957419425272100.jpg\"]","price":4390,"stock":9999,"sales":0,"status":1},{"id":253,"name":"MINISO名创优品硅胶热水袋","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957295282004700.jpg\"]","price":5790,"stock":9999,"sales":0,"status":1},{"id":252,"name":"MINISO名创优品Chiikawa系列毛茸茸季斗篷休闲午休毯随机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957103956331200.jpg\"]","price":16390,"stock":9999,"sales":0,"status":1},{"id":251,"name":"MINISO名创优品罗小黑轩影摇香系列迷你挂画冰箱贴五款盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957004993236600.jpg\"]","price":13990,"stock":9999,"sales":0,"status":1},{"id":250,"name":"MINISO名创优品chiikawa系列睡衣毛绒公仔","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956849972873300.jpg\"]","price":10900,"stock":9999,"sales":0,"status":1},{"id":249,"name":"MINISO名创优品蜡笔小新拼图","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956726589144000.png\"]","price":8890,"stock":9999,"sales":0,"status":1},{"id":248,"name":"MINISO名创优品三丽鸥保温杯公仔冰霸杯","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956569488102100.jpg\"]","price":8890,"stock":9999,"sales":0,"status":1},{"id":247,"name":"MINISO名创优品蜡笔小新超变装毛绒眼罩","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956491344273300.jpg\"]","price":1990,"stock":9999,"sales":0,"status":1},{"id":246,"name":"MINISO名创优品迪士尼疯狂动物城搪胶毛绒公仔盲盒摆件","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956361082178100.jpg\"]","price":8690,"stock":9999,"sales":0,"status":1},{"id":245,"name":"MINISO名创优品玉桂狗酷洛米捏捏小夜灯卧室睡眠灯床头灯LED灯随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956188247609800.jpg\"]","price":6590,"stock":9999,"sales":0,"status":1},{"id":244,"name":"MINISO名创优品Chiikawa奇幻马戏团毛绒挂件随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956006896154800.jpg\"]","price":5590,"stock":9999,"sales":0,"status":1},{"id":243,"name":"MINISO名创优品WAKUKU哇库库毛毛潮趣第二弹系列单盒盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955886258552300.jpg\"]","price":7590,"stock":9999,"sales":0,"status":1},{"id":242,"name":"MINISO名创优品Chiikawa樱花季吉伊乌萨奇小八零钱包挂件耳机包随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955794487832400.jpg\"]","price":4390,"stock":9999,"sales":0,"status":1},{"id":241,"name":"MINISO名创优品草莓熊休闲毯加厚保暖午睡毯子","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955717135623100.jpg\"]","price":9900,"stock":9999,"sales":0,"status":1},{"id":240,"name":"MINISO名创优品MyMelody秘森茶话会盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955658005155100.jpg\"]","price":3900,"stock":9999,"sales":0,"status":1},{"id":239,"name":"MINISO名创优品【指环爱神系列】2盒随机2款手办盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955515578588700.png\"]","price":6400,"stock":9999,"sales":0,"status":1},{"id":238,"name":"MINISO名创优品 小熊维尼拍立得系列(单盒单款)","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955384656076300.jpg\"]","price":3690,"stock":9999,"sales":0,"status":1},{"id":237,"name":"MINISO名创优品 小熊维尼拍立得系列(端盒8款)盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955301119356700.jpg\"]","price":30000,"stock":999,"sales":0,"status":1},{"id":236,"name":"MINISO名创优品缤纷街景积木第6季五件套拼装积木玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954965838583100.jpg\"]","price":18500,"stock":9999,"sales":0,"status":1},{"id":235,"name":"MINISO名创优品羊咩咩小羊玩偶毛绒公仔","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954804800434800.jpg\"]","price":2190,"stock":9999,"sales":0,"status":1},{"id":234,"name":"MINISO名创优品大师香薰室内家用150ml随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954438035852600.jpg\"]","price":4500,"stock":9999,"sales":0,"status":1},{"id":233,"name":"MINISO名创优品天然植物化妆棉800片白色","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954130792789000.jpg\"]","price":1390,"stock":9999,"sales":0,"status":1},{"id":232,"name":"MINISO名创优品玉桂狗u型枕","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765953971587736200.png\"]","price":3300,"stock":99999,"sales":0,"status":1},{"id":231,"name":"ZIPPO 春华秋实套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765878978681838200.jpg\"]","price":327500,"stock":9999,"sales":0,"status":1},{"id":230,"name":"ZIPPO 觉醒系列打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765877510957720900.jpg\"]","price":145500,"stock":9999,"sales":0,"status":1},{"id":229,"name":"ZIPPO 星际异兽套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765876033609870000.jpg\"]","price":135000,"stock":9999,"sales":0,"status":1},{"id":228,"name":"ZIPPO 幸运灵虫 套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875966491566400.jpg\"]","price":263900,"stock":9999,"sales":0,"status":1},{"id":227,"name":"ZIPPO Jack Daniel联名款——橡木之味打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875891023016800.jpg\"]","price":113800,"stock":9999,"sales":0,"status":1},{"id":226,"name":"ZIPPO八音盒·怦然心动打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875820076074600.jpg\"]","price":119900,"stock":9999,"sales":0,"status":1},{"id":225,"name":"ZIPPO蔷薇水晶礼盒套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875719520558200.jpg\"]","price":185000,"stock":9999,"sales":0,"status":1},{"id":224,"name":"ZIPPO幸运石系列随机款打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875631784700700.jpg\"]","price":58800,"stock":9999,"sales":0,"status":1},{"id":223,"name":"ZIPPO暗花幺點-PVD浸染打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875319301817500.avif\"]","price":33700,"stock":9999,"sales":0,"status":1},{"id":222,"name":"ZIPPO 雪花系列随机款打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875220636810700.jpg\"]","price":38200,"stock":9999,"sales":0,"status":1}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.06355525},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:50","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.019934416},{"time":"2025-12-22 15:19:50","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/product/product.go:233","sql":"SELECT count(*) FROM `products` WHERE `products`.`status` = 1 AND deleted_at IS NULL AND `products`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.0195725},{"time":"2025-12-22 15:19:50","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/product/product.go:237","sql":"SELECT * FROM `products` WHERE `products`.`status` = 1 AND deleted_at IS NULL AND `products`.`deleted_at` IS NULL ORDER BY `products`.`id` DESC LIMIT 50","rows_affected":50,"cost_seconds":0.023329292}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.063555541}} +{"level":"info","time":"2025-12-22 15:19:53","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/users/813/coupons/add","http_code":200,"business_code":0,"success":true,"cost_seconds":0.126548709,"trace_id":"2003002537099137024","trace_info":{"trace_id":"2003002537099137024","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/users/813/coupons/add","header":{"Content-Type":"application/json"},"body":"{\"coupon_id\":3}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002537099137024"],"Vary":["Origin"]},"body":{"success":true},"http_code":200,"http_code_msg":"OK","cost_seconds":0.126548334},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:53","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018961042},{"time":"2025-12-22 15:19:53","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/coupon_add.go:15","sql":"SELECT * FROM `system_coupons` WHERE `system_coupons`.`id` = 3 AND deleted_at IS NULL AND `system_coupons`.`deleted_at` IS NULL ORDER BY `system_coupons`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.025258208},{"time":"2025-12-22 15:19:53","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/coupon_add.go:54","sql":"INSERT INTO `user_coupons` (`user_id`,`coupon_id`,`valid_start`,`valid_end`,`status`,`balance_amount`) VALUES (813,3,'2025-12-22 15:19:41.923','2026-01-21 15:19:41.923',1,0)","rows_affected":1,"cost_seconds":0.031847167},{"time":"2025-12-22 15:19:53","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/coupon_add.go:59","sql":"UPDATE `user_coupons` SET `balance_amount`=100000,`updated_at`='2025-12-22 15:19:53.586' WHERE `user_coupons`.`id` = 15","rows_affected":1,"cost_seconds":0.029708667}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.126548709}} +{"level":"info","time":"2025-12-22 15:19:53","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/users?current=1&size=10&page=1&page_size=20","http_code":200,"business_code":0,"success":true,"cost_seconds":0.139930959,"trace_id":"2003002538072215552","trace_info":{"trace_id":"2003002538072215552","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/users?current=1&size=10&page=1&page_size=20","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002538072215552"],"Vary":["Origin"]},"body":{"page":1,"page_size":20,"total":6,"list":[{"id":813,"nickname":"善良的巴乔","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAABmAADpPQ5dAAAAAXRSTlMAQObYZgAAAD9JREFUeJxiAIH//xHkSBdAEofwRrjAKEAFsMQCTzQjXgCRSMDsES4AoWAALj6iBRABNCqAnHlGBRgAAQAA//8HSv0f1XCh/AAAAABJRU5ErkJggg==","invite_code":"AZCHW75Z","inviter_id":0,"created_at":"2025-11-16 13:50:58","douyin_id":"","channel_name":"","channel_code":"","coupons_count":3,"item_cards_count":5,"today_consume":0,"seven_day_consume":0},{"id":12,"nickname":"张三","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"S93L5LDY","inviter_id":0,"created_at":"2025-11-14 21:21:44","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":6,"today_consume":11910,"seven_day_consume":11910},{"id":9,"nickname":"贝利求而不得","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"ELBZ6BM8","inviter_id":6,"created_at":"2025-11-14 11:48:55","douyin_id":"","channel_name":"","channel_code":"","coupons_count":2,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":8,"nickname":"欢呼的贝利","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"VUQQ9MYF","inviter_id":0,"created_at":"2025-11-14 11:18:07","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":7,"nickname":"大罗舞力四射","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"23JEKRTQ","inviter_id":0,"created_at":"2025-11-14 11:06:38","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":6,"nickname":"标致的欧文","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"A8ZWET6K","inviter_id":0,"created_at":"2025-11-14 11:06:05","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.13993075},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:53","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.019087083},{"time":"2025-12-22 15:19:53","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:91","sql":"SELECT count(*) FROM `users` LEFT JOIN `channels` ON `channels`.`id` = `users`.`channel_id` WHERE `users`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.010307375},{"time":"2025-12-22 15:19:53","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:102","sql":"SELECT `users`.*,`channels`.`name` AS `channel_name`,`channels`.`code` AS `channel_code` FROM `users` LEFT JOIN `channels` ON `channels`.`id` = `users`.`channel_id` WHERE `users`.`deleted_at` IS NULL ORDER BY `users`.`id` DESC LIMIT 20","rows_affected":-1,"cost_seconds":0.0258275},{"time":"2025-12-22 15:19:53","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:126","sql":"SELECT `user_coupons`.`user_id`,COUNT(`user_coupons`.`id`) AS `count` FROM `user_coupons` WHERE `user_coupons`.`user_id` IN (813,12,9,8,7,6) AND `user_coupons`.`status` = 1 GROUP BY `user_coupons`.`user_id`","rows_affected":-1,"cost_seconds":0.020721458},{"time":"2025-12-22 15:19:53","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:145","sql":"SELECT `user_item_cards`.`user_id`,COUNT(`user_item_cards`.`id`) AS `count` FROM `user_item_cards` WHERE `user_item_cards`.`user_id` IN (813,12,9,8,7,6) AND `user_item_cards`.`status` = 1 GROUP BY `user_item_cards`.`user_id`","rows_affected":-1,"cost_seconds":0.022305625},{"time":"2025-12-22 15:19:53","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:172","sql":"SELECT `orders`.`user_id`,SUM(`orders`.`actual_amount`) AS `amount` FROM `orders` WHERE `orders`.`user_id` IN (813,12,9,8,7,6) AND `orders`.`status` = 2 AND `orders`.`created_at` >= '2025-12-22 00:00:00' GROUP BY `orders`.`user_id`","rows_affected":-1,"cost_seconds":0.020528625},{"time":"2025-12-22 15:19:53","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:185","sql":"SELECT `orders`.`user_id`,SUM(`orders`.`actual_amount`) AS `amount` FROM `orders` WHERE `orders`.`user_id` IN (813,12,9,8,7,6) AND `orders`.`status` = 2 AND `orders`.`created_at` >= '2025-12-16 00:00:00' GROUP BY `orders`.`user_id`","rows_affected":-1,"cost_seconds":0.020381667}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.139930959}} +{"level":"info","time":"2025-12-22 15:19:55","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/products?status=1&page=1&page_size=50","http_code":200,"business_code":0,"success":true,"cost_seconds":0.063920834,"trace_id":"2003002544376254464","trace_info":{"trace_id":"2003002544376254464","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/products?status=1&page=1&page_size=50","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002544376254464"],"Vary":["Origin"]},"body":{"page":1,"page_size":50,"total":271,"list":[{"id":271,"name":"LAMY凌美宝珠笔礼盒暗夜绿野奢礼盒","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765974142175986900.jpg\"]","price":87990,"stock":9999,"sales":0,"status":1},{"id":270,"name":"LAMY凌美钢笔礼盒 宋代限定版墨水笔青苍玉兰","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765974064319974300.jpg\"]","price":32990,"stock":9999,"sales":0,"status":1},{"id":269,"name":"LAMY凌美 钢笔(尼克+朱迪)礼盒疯狂动物城联名墨水笔套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973959992705500.jpg\"]","price":100900,"stock":9999,"sales":0,"status":1},{"id":268,"name":"LAMY凌美钢笔礼盒 狩猎系列磨砂黑白色墨水笔套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973806819055400.jpg\"]","price":43900,"stock":9999,"sales":0,"status":1},{"id":267,"name":"LAMY凌美钢笔礼盒 狩猎系列库洛米联名限定套装","category_id":10,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765973718891607700.png\"]","price":50500,"stock":99999,"sales":0,"status":1},{"id":266,"name":"MINISO名创优品U型枕飞机云朵护颈枕记忆棉","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958525893026200.jpg\"]","price":7700,"stock":9999,"sales":0,"status":1},{"id":265,"name":"MINISO名创优品假面骑士×WASA龙联名系列盲盒单盒随机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958389919343600.jpg\"]","price":10490,"stock":9999,"sales":0,"status":1},{"id":264,"name":"MINISO名创优品橘猫工业铁甲小宝第二弹模型玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958320499495500.jpg\"]","price":10800,"stock":9999,"sales":0,"status":1},{"id":263,"name":"MINISO名创优品橘猫工业铁甲小宝第一弹模型玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958282291523200.jpg\"]","price":10800,"stock":9999,"sales":0,"status":1},{"id":262,"name":"MINISO名创Nanci囡茜氛围灯积木摆件中国风小夜灯","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958213519986200.png\"]","price":10900,"stock":9999,"sales":0,"status":1},{"id":261,"name":"名创优品迪士尼316不锈钢保温饭盒便当盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958147297026300.jpg\"]","price":8900,"stock":9999,"sales":0,"status":1},{"id":260,"name":"MINISO名创优品学院系列焦糖格纹双肩包包学院风","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958088190736900.jpg\"]","price":11500,"stock":9999,"sales":0,"status":1},{"id":259,"name":"MINISO/名创优品迪士尼朱迪的警官生活系列痛包","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957990772169000.jpg\"]","price":13090,"stock":9999,"sales":0,"status":1},{"id":258,"name":"MINISO名创优品雪夜派对拼装模型圣诞节礼物","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957900503621400.jpg\"]","price":13900,"stock":9999,"sales":0,"status":1},{"id":257,"name":"MINISO名创优品【深睡记忆枕】透气枕芯枕头护颈椎释压","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957819453538400.png\"]","price":14590,"stock":9999,"sales":0,"status":1},{"id":256,"name":"MINISO名创优品香薰氛围感蓝牙音乐香氛机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957751042251600.jpg\"]","price":24500,"stock":9999,"sales":0,"status":1},{"id":255,"name":"MINISO名创优品铠魂帝皇侠15周年典藏模玩机甲超可动人偶成品模型","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957636770820900.jpg\"]","price":27500,"stock":9999,"sales":0,"status":1},{"id":254,"name":"MINISO名创优品冰淇淋香薰膏室内卫生间清新芬芳花果香4盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957419425272100.jpg\"]","price":4390,"stock":9999,"sales":0,"status":1},{"id":253,"name":"MINISO名创优品硅胶热水袋","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957295282004700.jpg\"]","price":5790,"stock":9999,"sales":0,"status":1},{"id":252,"name":"MINISO名创优品Chiikawa系列毛茸茸季斗篷休闲午休毯随机","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957103956331200.jpg\"]","price":16390,"stock":9999,"sales":0,"status":1},{"id":251,"name":"MINISO名创优品罗小黑轩影摇香系列迷你挂画冰箱贴五款盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765957004993236600.jpg\"]","price":13990,"stock":9999,"sales":0,"status":1},{"id":250,"name":"MINISO名创优品chiikawa系列睡衣毛绒公仔","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956849972873300.jpg\"]","price":10900,"stock":9999,"sales":0,"status":1},{"id":249,"name":"MINISO名创优品蜡笔小新拼图","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956726589144000.png\"]","price":8890,"stock":9999,"sales":0,"status":1},{"id":248,"name":"MINISO名创优品三丽鸥保温杯公仔冰霸杯","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956569488102100.jpg\"]","price":8890,"stock":9999,"sales":0,"status":1},{"id":247,"name":"MINISO名创优品蜡笔小新超变装毛绒眼罩","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956491344273300.jpg\"]","price":1990,"stock":9999,"sales":0,"status":1},{"id":246,"name":"MINISO名创优品迪士尼疯狂动物城搪胶毛绒公仔盲盒摆件","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956361082178100.jpg\"]","price":8690,"stock":9999,"sales":0,"status":1},{"id":245,"name":"MINISO名创优品玉桂狗酷洛米捏捏小夜灯卧室睡眠灯床头灯LED灯随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956188247609800.jpg\"]","price":6590,"stock":9999,"sales":0,"status":1},{"id":244,"name":"MINISO名创优品Chiikawa奇幻马戏团毛绒挂件随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765956006896154800.jpg\"]","price":5590,"stock":9999,"sales":0,"status":1},{"id":243,"name":"MINISO名创优品WAKUKU哇库库毛毛潮趣第二弹系列单盒盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955886258552300.jpg\"]","price":7590,"stock":9999,"sales":0,"status":1},{"id":242,"name":"MINISO名创优品Chiikawa樱花季吉伊乌萨奇小八零钱包挂件耳机包随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955794487832400.jpg\"]","price":4390,"stock":9999,"sales":0,"status":1},{"id":241,"name":"MINISO名创优品草莓熊休闲毯加厚保暖午睡毯子","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955717135623100.jpg\"]","price":9900,"stock":9999,"sales":0,"status":1},{"id":240,"name":"MINISO名创优品MyMelody秘森茶话会盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955658005155100.jpg\"]","price":3900,"stock":9999,"sales":0,"status":1},{"id":239,"name":"MINISO名创优品【指环爱神系列】2盒随机2款手办盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955515578588700.png\"]","price":6400,"stock":9999,"sales":0,"status":1},{"id":238,"name":"MINISO名创优品 小熊维尼拍立得系列(单盒单款)","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955384656076300.jpg\"]","price":3690,"stock":9999,"sales":0,"status":1},{"id":237,"name":"MINISO名创优品 小熊维尼拍立得系列(端盒8款)盲盒","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765955301119356700.jpg\"]","price":30000,"stock":999,"sales":0,"status":1},{"id":236,"name":"MINISO名创优品缤纷街景积木第6季五件套拼装积木玩具","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954965838583100.jpg\"]","price":18500,"stock":9999,"sales":0,"status":1},{"id":235,"name":"MINISO名创优品羊咩咩小羊玩偶毛绒公仔","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954804800434800.jpg\"]","price":2190,"stock":9999,"sales":0,"status":1},{"id":234,"name":"MINISO名创优品大师香薰室内家用150ml随机款","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954438035852600.jpg\"]","price":4500,"stock":9999,"sales":0,"status":1},{"id":233,"name":"MINISO名创优品天然植物化妆棉800片白色","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765954130792789000.jpg\"]","price":1390,"stock":9999,"sales":0,"status":1},{"id":232,"name":"MINISO名创优品玉桂狗u型枕","category_id":9,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765953971587736200.png\"]","price":3300,"stock":99999,"sales":0,"status":1},{"id":231,"name":"ZIPPO 春华秋实套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765878978681838200.jpg\"]","price":327500,"stock":9999,"sales":0,"status":1},{"id":230,"name":"ZIPPO 觉醒系列打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765877510957720900.jpg\"]","price":145500,"stock":9999,"sales":0,"status":1},{"id":229,"name":"ZIPPO 星际异兽套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765876033609870000.jpg\"]","price":135000,"stock":9999,"sales":0,"status":1},{"id":228,"name":"ZIPPO 幸运灵虫 套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875966491566400.jpg\"]","price":263900,"stock":9999,"sales":0,"status":1},{"id":227,"name":"ZIPPO Jack Daniel联名款——橡木之味打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875891023016800.jpg\"]","price":113800,"stock":9999,"sales":0,"status":1},{"id":226,"name":"ZIPPO八音盒·怦然心动打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875820076074600.jpg\"]","price":119900,"stock":9999,"sales":0,"status":1},{"id":225,"name":"ZIPPO蔷薇水晶礼盒套装打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875719520558200.jpg\"]","price":185000,"stock":9999,"sales":0,"status":1},{"id":224,"name":"ZIPPO幸运石系列随机款打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875631784700700.jpg\"]","price":58800,"stock":9999,"sales":0,"status":1},{"id":223,"name":"ZIPPO暗花幺點-PVD浸染打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875319301817500.avif\"]","price":33700,"stock":9999,"sales":0,"status":1},{"id":222,"name":"ZIPPO 雪花系列随机款打火机","category_id":8,"images_json":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/16/1765875220636810700.jpg\"]","price":38200,"stock":9999,"sales":0,"status":1}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.063920459},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:55","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.0212795},{"time":"2025-12-22 15:19:55","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/product/product.go:233","sql":"SELECT count(*) FROM `products` WHERE `products`.`status` = 1 AND deleted_at IS NULL AND `products`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.018720208},{"time":"2025-12-22 15:19:55","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/product/product.go:237","sql":"SELECT * FROM `products` WHERE `products`.`status` = 1 AND deleted_at IS NULL AND `products`.`deleted_at` IS NULL ORDER BY `products`.`id` DESC LIMIT 50","rows_affected":50,"cost_seconds":0.022960541}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.063920834}} +{"level":"info","time":"2025-12-22 15:19:57","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/users/12/coupons/add","http_code":200,"business_code":0,"success":true,"cost_seconds":0.13142925,"trace_id":"2003002554044125184","trace_info":{"trace_id":"2003002554044125184","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/users/12/coupons/add","header":{"Content-Type":"application/json"},"body":"{\"coupon_id\":3}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002554044125184"],"Vary":["Origin"]},"body":{"success":true},"http_code":200,"http_code_msg":"OK","cost_seconds":0.131428833},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:57","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.021891167},{"time":"2025-12-22 15:19:57","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/coupon_add.go:15","sql":"SELECT * FROM `system_coupons` WHERE `system_coupons`.`id` = 3 AND deleted_at IS NULL AND `system_coupons`.`deleted_at` IS NULL ORDER BY `system_coupons`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.023846125},{"time":"2025-12-22 15:19:57","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/coupon_add.go:54","sql":"INSERT INTO `user_coupons` (`user_id`,`coupon_id`,`valid_start`,`valid_end`,`status`,`balance_amount`) VALUES (12,3,'2025-12-22 15:19:41.923','2026-01-21 15:19:41.923',1,0)","rows_affected":1,"cost_seconds":0.033078041},{"time":"2025-12-22 15:19:57","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/service/user/coupon_add.go:59","sql":"UPDATE `user_coupons` SET `balance_amount`=100000,`updated_at`='2025-12-22 15:19:57.629' WHERE `user_coupons`.`id` = 16","rows_affected":1,"cost_seconds":0.031853042}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.13142925}} +{"level":"info","time":"2025-12-22 15:19:57","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/users?current=1&size=10&page=1&page_size=20","http_code":200,"business_code":0,"success":true,"cost_seconds":0.128880875,"trace_id":"2003002555050758144","trace_info":{"trace_id":"2003002555050758144","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/users?current=1&size=10&page=1&page_size=20","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003002555050758144"],"Vary":["Origin"]},"body":{"page":1,"page_size":20,"total":6,"list":[{"id":813,"nickname":"善良的巴乔","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAABmAADpPQ5dAAAAAXRSTlMAQObYZgAAAD9JREFUeJxiAIH//xHkSBdAEofwRrjAKEAFsMQCTzQjXgCRSMDsES4AoWAALj6iBRABNCqAnHlGBRgAAQAA//8HSv0f1XCh/AAAAABJRU5ErkJggg==","invite_code":"AZCHW75Z","inviter_id":0,"created_at":"2025-11-16 13:50:58","douyin_id":"","channel_name":"","channel_code":"","coupons_count":3,"item_cards_count":5,"today_consume":0,"seven_day_consume":0},{"id":12,"nickname":"张三","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"S93L5LDY","inviter_id":0,"created_at":"2025-11-14 21:21:44","douyin_id":"","channel_name":"","channel_code":"","coupons_count":1,"item_cards_count":6,"today_consume":11910,"seven_day_consume":11910},{"id":9,"nickname":"贝利求而不得","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"ELBZ6BM8","inviter_id":6,"created_at":"2025-11-14 11:48:55","douyin_id":"","channel_name":"","channel_code":"","coupons_count":2,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":8,"nickname":"欢呼的贝利","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"VUQQ9MYF","inviter_id":0,"created_at":"2025-11-14 11:18:07","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":7,"nickname":"大罗舞力四射","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"23JEKRTQ","inviter_id":0,"created_at":"2025-11-14 11:06:38","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":6,"nickname":"标致的欧文","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"A8ZWET6K","inviter_id":0,"created_at":"2025-11-14 11:06:05","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.128880459},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:19:57","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.01855225},{"time":"2025-12-22 15:19:57","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:91","sql":"SELECT count(*) FROM `users` LEFT JOIN `channels` ON `channels`.`id` = `users`.`channel_id` WHERE `users`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.00993275},{"time":"2025-12-22 15:19:57","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:102","sql":"SELECT `users`.*,`channels`.`name` AS `channel_name`,`channels`.`code` AS `channel_code` FROM `users` LEFT JOIN `channels` ON `channels`.`id` = `users`.`channel_id` WHERE `users`.`deleted_at` IS NULL ORDER BY `users`.`id` DESC LIMIT 20","rows_affected":-1,"cost_seconds":0.0198775},{"time":"2025-12-22 15:19:57","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:126","sql":"SELECT `user_coupons`.`user_id`,COUNT(`user_coupons`.`id`) AS `count` FROM `user_coupons` WHERE `user_coupons`.`user_id` IN (813,12,9,8,7,6) AND `user_coupons`.`status` = 1 GROUP BY `user_coupons`.`user_id`","rows_affected":-1,"cost_seconds":0.020930333},{"time":"2025-12-22 15:19:57","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:145","sql":"SELECT `user_item_cards`.`user_id`,COUNT(`user_item_cards`.`id`) AS `count` FROM `user_item_cards` WHERE `user_item_cards`.`user_id` IN (813,12,9,8,7,6) AND `user_item_cards`.`status` = 1 GROUP BY `user_item_cards`.`user_id`","rows_affected":-1,"cost_seconds":0.019307625},{"time":"2025-12-22 15:19:57","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:172","sql":"SELECT `orders`.`user_id`,SUM(`orders`.`actual_amount`) AS `amount` FROM `orders` WHERE `orders`.`user_id` IN (813,12,9,8,7,6) AND `orders`.`status` = 2 AND `orders`.`created_at` >= '2025-12-22 00:00:00' GROUP BY `orders`.`user_id`","rows_affected":-1,"cost_seconds":0.019557292},{"time":"2025-12-22 15:19:57","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:185","sql":"SELECT `orders`.`user_id`,SUM(`orders`.`actual_amount`) AS `amount` FROM `orders` WHERE `orders`.`user_id` IN (813,12,9,8,7,6) AND `orders`.`status` = 2 AND `orders`.`created_at` >= '2025-12-16 00:00:00' GROUP BY `orders`.`user_id`","rows_affected":-1,"cost_seconds":0.018697959}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.128880875}} +{"level":"info","time":"2025-12-22 15:51:55","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-22 15:51:59","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_item_cards?page=1&page_size=100","http_code":200,"business_code":0,"success":true,"cost_seconds":0.098179375,"trace_id":"2003010613617364992","trace_info":{"trace_id":"2003010613617364992","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_item_cards?page=1&page_size=100","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003010613617364992"],"Vary":["Origin"]},"body":{"page":1,"page_size":100,"total":1,"list":[{"id":1,"created_at":"2025-11-14T18:46:15.338+08:00","updated_at":"2025-12-10T22:33:28.156+08:00","name":"双倍快乐水","status":1,"card_type":1,"scope_type":1,"activity_category_id":0,"activity_id":0,"issue_id":0,"price":1900,"valid_start":"2025-11-26T00:00:00+08:00","valid_end":"2025-12-25T00:00:00+08:00","effect_type":1,"reward_multiplier_x1000":0,"boost_rate_x1000":10,"stacking_strategy":1,"max_effect_value_x1000":0,"remark":"备注个锤子啊"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.098179083},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.06574275},{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:326","sql":"SELECT count(*) FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.011999125},{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:340","sql":"SELECT * FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL ORDER BY `system_item_cards`.`id` DESC LIMIT 100","rows_affected":1,"cost_seconds":0.019292875}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.098179375}} +{"level":"info","time":"2025-12-22 15:51:59","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_titles?page=1&page_size=100","http_code":200,"business_code":0,"success":true,"cost_seconds":0.112609917,"trace_id":"2003010613617364992","trace_info":{"trace_id":"2003010613617364992","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_titles?page=1&page_size=100","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003010613617364992"],"Vary":["Origin"]},"body":{"page":1,"page_size":100,"total":2,"list":[{"id":8,"name":"概率加成","description":"抽奖期间 会对奖品有一定概率增幅","status":1,"obtain_rules_json":"{}","scopes_json":"{}","created_at":"2025-11-16T11:08:04.442+08:00","updated_at":"2025-11-16T11:08:04.442+08:00","deleted_at":null},{"id":7,"name":"奖品双倍","description":"在游戏活动抽奖的时候 会有一定几率触发双倍;效果等同双倍卡","status":1,"obtain_rules_json":"{}","scopes_json":"{}","created_at":"2025-11-16T10:49:39.519+08:00","updated_at":"2025-11-16T10:49:39.519+08:00","deleted_at":null}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.112609375},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.077621042},{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/titles_admin.go:42","sql":"SELECT count(*) FROM `system_titles` WHERE deleted_at IS NULL AND `system_titles`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.011404125},{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/titles_admin.go:48","sql":"SELECT * FROM `system_titles` WHERE deleted_at IS NULL AND `system_titles`.`deleted_at` IS NULL ORDER BY `system_titles`.`id` DESC LIMIT 100","rows_affected":2,"cost_seconds":0.0220355}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.112609917}} +{"level":"info","time":"2025-12-22 15:51:59","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_coupons?status=1&page=1&page_size=100","http_code":200,"business_code":0,"success":true,"cost_seconds":0.129602334,"trace_id":"2003010613617364992","trace_info":{"trace_id":"2003010613617364992","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_coupons?status=1&page=1&page_size=100","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003010613617364992"],"Vary":["Origin"]},"body":{"page":1,"page_size":100,"total":3,"list":[{"id":3,"name":"1000元优惠券","status":1,"coupon_type":1,"discount_type":1,"discount_value":100000,"min_amount":100,"max_discount":0,"valid_days":30,"total_quantity":0,"used_quantity":5,"created_at":"2025-12-14 17:31:24"},{"id":2,"name":"30元优惠券","status":1,"coupon_type":1,"discount_type":1,"discount_value":3000,"min_amount":100,"max_discount":0,"valid_days":30,"total_quantity":1000,"used_quantity":5,"created_at":"2025-12-08 01:40:47"},{"id":1,"name":"优惠券1 ","status":1,"coupon_type":1,"discount_type":1,"discount_value":10000,"min_amount":100,"max_discount":0,"valid_days":30,"total_quantity":1100,"used_quantity":5,"created_at":"2025-11-14 20:19:47"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.129602084},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.0208345},{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:202","sql":"SELECT count(*) FROM `system_coupons` WHERE `system_coupons`.`status` = 1 AND deleted_at IS NULL AND `system_coupons`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.020852542},{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:221","sql":"SELECT * FROM `system_coupons` WHERE `system_coupons`.`status` = 1 AND `system_coupons`.`deleted_at` IS NULL ORDER BY `system_coupons`.`id` DESC LIMIT 100","rows_affected":-1,"cost_seconds":0.021984209},{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:236","sql":"SELECT count(*) FROM `user_coupons` WHERE `user_coupons`.`coupon_id` = 3","rows_affected":1,"cost_seconds":0.02398575},{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:236","sql":"SELECT count(*) FROM `user_coupons` WHERE `user_coupons`.`coupon_id` = 2","rows_affected":1,"cost_seconds":0.019745333},{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/system_coupons.go:236","sql":"SELECT count(*) FROM `user_coupons` WHERE `user_coupons`.`coupon_id` = 1","rows_affected":1,"cost_seconds":0.020642}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.129602334}} +{"level":"info","time":"2025-12-22 15:51:59","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/users?current=1&size=10&page=1&page_size=20","http_code":200,"business_code":0,"success":true,"cost_seconds":0.1935785,"trace_id":"2003010613617364992","trace_info":{"trace_id":"2003010613617364992","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/users?current=1&size=10&page=1&page_size=20","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003010613617364992"],"Vary":["Origin"]},"body":{"page":1,"page_size":20,"total":6,"list":[{"id":813,"nickname":"善良的巴乔","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAABmAADpPQ5dAAAAAXRSTlMAQObYZgAAAD9JREFUeJxiAIH//xHkSBdAEofwRrjAKEAFsMQCTzQjXgCRSMDsES4AoWAALj6iBRABNCqAnHlGBRgAAQAA//8HSv0f1XCh/AAAAABJRU5ErkJggg==","invite_code":"AZCHW75Z","inviter_id":0,"created_at":"2025-11-16 13:50:58","douyin_id":"","channel_name":"","channel_code":"","coupons_count":3,"item_cards_count":5,"today_consume":0,"seven_day_consume":0},{"id":12,"nickname":"张三","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"S93L5LDY","inviter_id":0,"created_at":"2025-11-14 21:21:44","douyin_id":"","channel_name":"","channel_code":"","coupons_count":1,"item_cards_count":4,"today_consume":13890,"seven_day_consume":13890},{"id":9,"nickname":"贝利求而不得","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"ELBZ6BM8","inviter_id":6,"created_at":"2025-11-14 11:48:55","douyin_id":"","channel_name":"","channel_code":"","coupons_count":2,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":8,"nickname":"欢呼的贝利","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"VUQQ9MYF","inviter_id":0,"created_at":"2025-11-14 11:18:07","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":7,"nickname":"大罗舞力四射","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"23JEKRTQ","inviter_id":0,"created_at":"2025-11-14 11:06:38","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0},{"id":6,"nickname":"标致的欧文","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","invite_code":"A8ZWET6K","inviter_id":0,"created_at":"2025-11-14 11:06:05","douyin_id":"","channel_name":"","channel_code":"","coupons_count":0,"item_cards_count":0,"today_consume":0,"seven_day_consume":0}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.193578375},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.069315791},{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:91","sql":"SELECT count(*) FROM `users` LEFT JOIN `channels` ON `channels`.`id` = `users`.`channel_id` WHERE `users`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.009834},{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:102","sql":"SELECT `users`.*,`channels`.`name` AS `channel_name`,`channels`.`code` AS `channel_code` FROM `users` LEFT JOIN `channels` ON `channels`.`id` = `users`.`channel_id` WHERE `users`.`deleted_at` IS NULL ORDER BY `users`.`id` DESC LIMIT 20","rows_affected":-1,"cost_seconds":0.026002},{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:126","sql":"SELECT `user_coupons`.`user_id`,COUNT(`user_coupons`.`id`) AS `count` FROM `user_coupons` WHERE `user_coupons`.`user_id` IN (813,12,9,8,7,6) AND `user_coupons`.`status` = 1 GROUP BY `user_coupons`.`user_id`","rows_affected":-1,"cost_seconds":0.020956208},{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:145","sql":"SELECT `user_item_cards`.`user_id`,COUNT(`user_item_cards`.`id`) AS `count` FROM `user_item_cards` WHERE `user_item_cards`.`user_id` IN (813,12,9,8,7,6) AND `user_item_cards`.`status` = 1 GROUP BY `user_item_cards`.`user_id`","rows_affected":-1,"cost_seconds":0.020189333},{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:172","sql":"SELECT `orders`.`user_id`,SUM(`orders`.`actual_amount`) AS `amount` FROM `orders` WHERE `orders`.`user_id` IN (813,12,9,8,7,6) AND `orders`.`status` = 2 AND `orders`.`created_at` >= '2025-12-22 00:00:00' GROUP BY `orders`.`user_id`","rows_affected":-1,"cost_seconds":0.024657166},{"time":"2025-12-22 15:51:59","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/users_admin.go:185","sql":"SELECT `orders`.`user_id`,SUM(`orders`.`actual_amount`) AS `amount` FROM `orders` WHERE `orders`.`user_id` IN (813,12,9,8,7,6) AND `orders`.`status` = 2 AND `orders`.`created_at` >= '2025-12-16 00:00:00' GROUP BY `orders`.`user_id`","rows_affected":-1,"cost_seconds":0.019735459}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.1935785}} +{"level":"info","time":"2025-12-22 15:52:02","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_item_cards?page=1&page_size=10&name=","http_code":200,"business_code":0,"success":true,"cost_seconds":0.049987708,"trace_id":"2003010626120585216","trace_info":{"trace_id":"2003010626120585216","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_item_cards?page=1&page_size=10&name=","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003010626120585216"],"Vary":["Origin"]},"body":{"page":1,"page_size":10,"total":1,"list":[{"id":1,"created_at":"2025-11-14T18:46:15.338+08:00","updated_at":"2025-12-10T22:33:28.156+08:00","name":"双倍快乐水","status":1,"card_type":1,"scope_type":1,"activity_category_id":0,"activity_id":0,"issue_id":0,"price":1900,"valid_start":"2025-11-26T00:00:00+08:00","valid_end":"2025-12-25T00:00:00+08:00","effect_type":1,"reward_multiplier_x1000":0,"boost_rate_x1000":10,"stacking_strategy":1,"max_effect_value_x1000":0,"remark":"备注个锤子啊"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.049986083},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:52:02","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.019302958},{"time":"2025-12-22 15:52:02","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:326","sql":"SELECT count(*) FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.009999792},{"time":"2025-12-22 15:52:02","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:340","sql":"SELECT * FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL ORDER BY `system_item_cards`.`id` DESC LIMIT 10","rows_affected":1,"cost_seconds":0.020283167}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.049987708}} +{"level":"info","time":"2025-12-22 15:52:17","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"PUT","path":"/api/admin/system_item_cards/1","http_code":200,"business_code":0,"success":true,"cost_seconds":0.063296792,"trace_id":"2003010690863861760","trace_info":{"trace_id":"2003010690863861760","request":{"ttl":"un-limit","method":"PUT","decoded_url":"/api/admin/system_item_cards/1","header":{"Content-Type":"application/json"},"body":"{\"name\":\"双倍快乐水\",\"card_type\":1,\"scope_type\":1,\"effect_type\":1,\"price\":1900,\"reward_multiplier_x1000\":0,\"boost_rate_x1000\":10,\"stacking_strategy\":1,\"max_effect_value_x1000\":2000,\"status\":1,\"remark\":\"备注个锤子啊\",\"valid_start_unix\":1764086400,\"valid_end_unix\":1766592000}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003010690863861760"],"Vary":["Origin"]},"body":{"message":"操作成功"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.063296542},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:52:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.020268084},{"time":"2025-12-22 15:52:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:211","sql":"UPDATE `system_item_cards` SET `boost_rate_x1000`=10,`card_type`=1,`effect_type`=1,`max_effect_value_x1000`=2000,`name`='双倍快乐水',`price`=1900,`remark`='备注个锤子啊',`reward_multiplier_x1000`=0,`scope_type`=1,`stacking_strategy`=1,`status`=1,`valid_end`='2025-12-25 00:00:00',`valid_start`='2025-11-26 00:00:00',`updated_at`='2025-12-22 15:52:17.53' WHERE `system_item_cards`.`id` = 1 AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.031310792}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.063296792}} +{"level":"info","time":"2025-12-22 15:52:17","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_item_cards?page=1&page_size=10&name=","http_code":200,"business_code":0,"success":true,"cost_seconds":0.094999958,"trace_id":"2003010691631419392","trace_info":{"trace_id":"2003010691631419392","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_item_cards?page=1&page_size=10&name=","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003010691631419392"],"Vary":["Origin"]},"body":{"page":1,"page_size":10,"total":1,"list":[{"id":1,"created_at":"2025-11-14T18:46:15.338+08:00","updated_at":"2025-12-22T15:52:17.53+08:00","name":"双倍快乐水","status":1,"card_type":1,"scope_type":1,"activity_category_id":0,"activity_id":0,"issue_id":0,"price":1900,"valid_start":"2025-11-26T00:00:00+08:00","valid_end":"2025-12-25T00:00:00+08:00","effect_type":1,"reward_multiplier_x1000":0,"boost_rate_x1000":10,"stacking_strategy":1,"max_effect_value_x1000":2000,"remark":"备注个锤子啊"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.094999583},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:52:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.020185292},{"time":"2025-12-22 15:52:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:326","sql":"SELECT count(*) FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.032474583},{"time":"2025-12-22 15:52:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:340","sql":"SELECT * FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL ORDER BY `system_item_cards`.`id` DESC LIMIT 10","rows_affected":1,"cost_seconds":0.041926}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.094999958}} +{"level":"info","time":"2025-12-22 15:54:17","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_item_cards?page=1&page_size=10&name=","http_code":200,"business_code":0,"success":true,"cost_seconds":0.101637791,"trace_id":"2003011192871718912","trace_info":{"trace_id":"2003011192871718912","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_item_cards?page=1&page_size=10&name=","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003011192871718912"],"Vary":["Origin"]},"body":{"page":1,"page_size":10,"total":1,"list":[{"id":1,"created_at":"2025-11-14T18:46:15.338+08:00","updated_at":"2025-12-22T15:52:17.53+08:00","name":"双倍快乐水","status":1,"card_type":1,"scope_type":1,"activity_category_id":0,"activity_id":0,"issue_id":0,"price":1900,"valid_start":"2025-11-26T00:00:00+08:00","valid_end":"2025-12-25T00:00:00+08:00","effect_type":1,"reward_multiplier_x1000":0,"boost_rate_x1000":10,"stacking_strategy":1,"max_effect_value_x1000":2000,"remark":"备注个锤子啊"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.101637416},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:54:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.0677675},{"time":"2025-12-22 15:54:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:326","sql":"SELECT count(*) FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.009633125},{"time":"2025-12-22 15:54:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:340","sql":"SELECT * FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL ORDER BY `system_item_cards`.`id` DESC LIMIT 10","rows_affected":1,"cost_seconds":0.023849792}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.101637791}} +{"level":"info","time":"2025-12-22 15:54:17","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_item_cards?page=1&page_size=10&name=","http_code":200,"business_code":0,"success":true,"cost_seconds":0.052886917,"trace_id":"2003011195031785472","trace_info":{"trace_id":"2003011195031785472","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_item_cards?page=1&page_size=10&name=","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003011195031785472"],"Vary":["Origin"]},"body":{"page":1,"page_size":10,"total":1,"list":[{"id":1,"created_at":"2025-11-14T18:46:15.338+08:00","updated_at":"2025-12-22T15:52:17.53+08:00","name":"双倍快乐水","status":1,"card_type":1,"scope_type":1,"activity_category_id":0,"activity_id":0,"issue_id":0,"price":1900,"valid_start":"2025-11-26T00:00:00+08:00","valid_end":"2025-12-25T00:00:00+08:00","effect_type":1,"reward_multiplier_x1000":0,"boost_rate_x1000":10,"stacking_strategy":1,"max_effect_value_x1000":2000,"remark":"备注个锤子啊"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.0528865},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:54:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.020438958},{"time":"2025-12-22 15:54:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:326","sql":"SELECT count(*) FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.01023925},{"time":"2025-12-22 15:54:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:340","sql":"SELECT * FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL ORDER BY `system_item_cards`.`id` DESC LIMIT 10","rows_affected":1,"cost_seconds":0.021851042}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.052886917}} +{"level":"info","time":"2025-12-22 15:55:35","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"PUT","path":"/api/admin/system_item_cards/1","http_code":200,"business_code":0,"success":true,"cost_seconds":0.068160375,"trace_id":"2003011522615316480","trace_info":{"trace_id":"2003011522615316480","request":{"ttl":"un-limit","method":"PUT","decoded_url":"/api/admin/system_item_cards/1","header":{"Content-Type":"application/json"},"body":"{\"name\":\"双倍快乐水\",\"card_type\":1,\"scope_type\":1,\"effect_type\":2,\"price\":1900,\"reward_multiplier_x1000\":2000,\"boost_rate_x1000\":10,\"stacking_strategy\":1,\"max_effect_value_x1000\":2000,\"status\":1,\"remark\":\"备注个锤子啊\",\"valid_start_unix\":1764086400,\"valid_end_unix\":1766592000}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003011522615316480"],"Vary":["Origin"]},"body":{"message":"操作成功"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.068159667},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:55:35","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018137708},{"time":"2025-12-22 15:55:35","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:211","sql":"UPDATE `system_item_cards` SET `boost_rate_x1000`=10,`card_type`=1,`effect_type`=2,`max_effect_value_x1000`=2000,`name`='双倍快乐水',`price`=1900,`remark`='备注个锤子啊',`reward_multiplier_x1000`=2000,`scope_type`=1,`stacking_strategy`=1,`status`=1,`valid_end`='2025-12-25 00:00:00',`valid_start`='2025-11-26 00:00:00',`updated_at`='2025-12-22 15:55:35.831' WHERE `system_item_cards`.`id` = 1 AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.040064708}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.068160375}} +{"level":"info","time":"2025-12-22 15:55:36","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_item_cards?page=1&page_size=10&name=","http_code":200,"business_code":0,"success":true,"cost_seconds":0.049044125,"trace_id":"2003011523412234240","trace_info":{"trace_id":"2003011523412234240","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_item_cards?page=1&page_size=10&name=","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003011523412234240"],"Vary":["Origin"]},"body":{"page":1,"page_size":10,"total":1,"list":[{"id":1,"created_at":"2025-11-14T18:46:15.338+08:00","updated_at":"2025-12-22T15:55:35.831+08:00","name":"双倍快乐水","status":1,"card_type":1,"scope_type":1,"activity_category_id":0,"activity_id":0,"issue_id":0,"price":1900,"valid_start":"2025-11-26T00:00:00+08:00","valid_end":"2025-12-25T00:00:00+08:00","effect_type":2,"reward_multiplier_x1000":2000,"boost_rate_x1000":10,"stacking_strategy":1,"max_effect_value_x1000":2000,"remark":"备注个锤子啊"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.049043667},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:55:36","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.01797225},{"time":"2025-12-22 15:55:36","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:326","sql":"SELECT count(*) FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.010018042},{"time":"2025-12-22 15:55:36","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:340","sql":"SELECT * FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL ORDER BY `system_item_cards`.`id` DESC LIMIT 10","rows_affected":1,"cost_seconds":0.020386917}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.049044125}} +{"level":"info","time":"2025-12-22 15:56:42","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_item_cards?page=1&page_size=10&name=","http_code":200,"business_code":0,"success":true,"cost_seconds":0.062760208,"trace_id":"2003011801318428672","trace_info":{"trace_id":"2003011801318428672","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_item_cards?page=1&page_size=10&name=","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003011801318428672"],"Vary":["Origin"]},"body":{"page":1,"page_size":10,"total":1,"list":[{"id":1,"created_at":"2025-11-14T18:46:15.338+08:00","updated_at":"2025-12-22T15:55:35.831+08:00","name":"双倍快乐水","status":1,"card_type":1,"scope_type":1,"activity_category_id":0,"activity_id":0,"issue_id":0,"price":1900,"valid_start":"2025-11-26T00:00:00+08:00","valid_end":"2025-12-25T00:00:00+08:00","effect_type":2,"reward_multiplier_x1000":2000,"boost_rate_x1000":10,"stacking_strategy":1,"max_effect_value_x1000":2000,"remark":"备注个锤子啊"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.062759417},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:56:42","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.02594},{"time":"2025-12-22 15:56:42","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:326","sql":"SELECT count(*) FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.012292084},{"time":"2025-12-22 15:56:42","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:340","sql":"SELECT * FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL ORDER BY `system_item_cards`.`id` DESC LIMIT 10","rows_affected":1,"cost_seconds":0.023123375}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.062760208}} +{"level":"info","time":"2025-12-22 15:58:12","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"PUT","path":"/api/admin/system_item_cards/1","http_code":200,"business_code":0,"success":true,"cost_seconds":0.073104,"trace_id":"2003012179548180480","trace_info":{"trace_id":"2003012179548180480","request":{"ttl":"un-limit","method":"PUT","decoded_url":"/api/admin/system_item_cards/1","header":{"Content-Type":"application/json"},"body":"{\"name\":\"双倍快乐水\",\"card_type\":1,\"scope_type\":1,\"effect_type\":2,\"price\":1900,\"reward_multiplier_x1000\":2000,\"boost_rate_x1000\":10,\"stacking_strategy\":1,\"max_effect_value_x1000\":2000,\"status\":1,\"remark\":\"备注个锤子啊\",\"valid_start_unix\":1764086400,\"valid_end_unix\":1766592000}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003012179548180480"],"Vary":["Origin"]},"body":{"message":"操作成功"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.07310325},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:58:12","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.021137375},{"time":"2025-12-22 15:58:12","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:211","sql":"UPDATE `system_item_cards` SET `boost_rate_x1000`=10,`card_type`=1,`effect_type`=2,`max_effect_value_x1000`=2000,`name`='双倍快乐水',`price`=1900,`remark`='备注个锤子啊',`reward_multiplier_x1000`=2000,`scope_type`=1,`stacking_strategy`=1,`status`=1,`valid_end`='2025-12-25 00:00:00',`valid_start`='2025-11-26 00:00:00',`updated_at`='2025-12-22 15:58:12.46' WHERE `system_item_cards`.`id` = 1 AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.042073708}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.073104}} +{"level":"info","time":"2025-12-22 15:58:12","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_item_cards?page=1&page_size=10&name=","http_code":200,"business_code":0,"success":true,"cost_seconds":0.065194333,"trace_id":"2003012180340903936","trace_info":{"trace_id":"2003012180340903936","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_item_cards?page=1&page_size=10&name=","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003012180340903936"],"Vary":["Origin"]},"body":{"page":1,"page_size":10,"total":1,"list":[{"id":1,"created_at":"2025-11-14T18:46:15.338+08:00","updated_at":"2025-12-22T15:58:12.46+08:00","name":"双倍快乐水","status":1,"card_type":1,"scope_type":1,"activity_category_id":0,"activity_id":0,"issue_id":0,"price":1900,"valid_start":"2025-11-26T00:00:00+08:00","valid_end":"2025-12-25T00:00:00+08:00","effect_type":2,"reward_multiplier_x1000":2000,"boost_rate_x1000":10,"stacking_strategy":1,"max_effect_value_x1000":2000,"remark":"备注个锤子啊"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.065193791},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:58:12","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.023957166},{"time":"2025-12-22 15:58:12","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:326","sql":"SELECT count(*) FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.011713291},{"time":"2025-12-22 15:58:12","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:340","sql":"SELECT * FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL ORDER BY `system_item_cards`.`id` DESC LIMIT 10","rows_affected":1,"cost_seconds":0.028501708}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.065194333}} +{"level":"info","time":"2025-12-22 15:58:27","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"PUT","path":"/api/admin/system_item_cards/1","http_code":200,"business_code":0,"success":true,"cost_seconds":0.06025475,"trace_id":"2003012242429186048","trace_info":{"trace_id":"2003012242429186048","request":{"ttl":"un-limit","method":"PUT","decoded_url":"/api/admin/system_item_cards/1","header":{"Content-Type":"application/json"},"body":"{\"name\":\"双倍快乐水\",\"card_type\":1,\"scope_type\":1,\"effect_type\":1,\"price\":1900,\"reward_multiplier_x1000\":2000,\"boost_rate_x1000\":10,\"stacking_strategy\":1,\"max_effect_value_x1000\":2000,\"status\":1,\"remark\":\"备注个锤子啊\",\"valid_start_unix\":1764086400,\"valid_end_unix\":1766592000}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003012242429186048"],"Vary":["Origin"]},"body":{"message":"操作成功"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.060254458},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:58:27","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018391917},{"time":"2025-12-22 15:58:27","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:211","sql":"UPDATE `system_item_cards` SET `boost_rate_x1000`=10,`card_type`=1,`effect_type`=1,`max_effect_value_x1000`=2000,`name`='双倍快乐水',`price`=1900,`remark`='备注个锤子啊',`reward_multiplier_x1000`=2000,`scope_type`=1,`stacking_strategy`=1,`status`=1,`valid_end`='2025-12-25 00:00:00',`valid_start`='2025-11-26 00:00:00',`updated_at`='2025-12-22 15:58:27.449' WHERE `system_item_cards`.`id` = 1 AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.031980375}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.06025475}} +{"level":"info","time":"2025-12-22 15:58:27","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_item_cards?page=1&page_size=10&name=","http_code":200,"business_code":0,"success":true,"cost_seconds":0.046791042,"trace_id":"2003012243154800640","trace_info":{"trace_id":"2003012243154800640","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_item_cards?page=1&page_size=10&name=","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003012243154800640"],"Vary":["Origin"]},"body":{"page":1,"page_size":10,"total":1,"list":[{"id":1,"created_at":"2025-11-14T18:46:15.338+08:00","updated_at":"2025-12-22T15:58:27.449+08:00","name":"双倍快乐水","status":1,"card_type":1,"scope_type":1,"activity_category_id":0,"activity_id":0,"issue_id":0,"price":1900,"valid_start":"2025-11-26T00:00:00+08:00","valid_end":"2025-12-25T00:00:00+08:00","effect_type":1,"reward_multiplier_x1000":2000,"boost_rate_x1000":10,"stacking_strategy":1,"max_effect_value_x1000":2000,"remark":"备注个锤子啊"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.046784458},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:58:27","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018360958},{"time":"2025-12-22 15:58:27","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:326","sql":"SELECT count(*) FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.0092285},{"time":"2025-12-22 15:58:27","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:340","sql":"SELECT * FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL ORDER BY `system_item_cards`.`id` DESC LIMIT 10","rows_affected":1,"cost_seconds":0.018854166}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.046791042}} +{"level":"info","time":"2025-12-22 15:58:44","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"PUT","path":"/api/admin/system_item_cards/1","http_code":200,"business_code":0,"success":true,"cost_seconds":0.103618458,"trace_id":"2003012313774297088","trace_info":{"trace_id":"2003012313774297088","request":{"ttl":"un-limit","method":"PUT","decoded_url":"/api/admin/system_item_cards/1","header":{"Content-Type":"application/json"},"body":"{\"name\":\"双倍快乐水\",\"card_type\":1,\"scope_type\":1,\"effect_type\":2,\"price\":1900,\"reward_multiplier_x1000\":2000,\"boost_rate_x1000\":10,\"stacking_strategy\":1,\"max_effect_value_x1000\":2000,\"status\":1,\"remark\":\"备注个锤子啊\",\"valid_start_unix\":1764086400,\"valid_end_unix\":1766592000}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003012313774297088"],"Vary":["Origin"]},"body":{"message":"操作成功"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.103618},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:58:44","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018058},{"time":"2025-12-22 15:58:44","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:211","sql":"UPDATE `system_item_cards` SET `boost_rate_x1000`=10,`card_type`=1,`effect_type`=2,`max_effect_value_x1000`=2000,`name`='双倍快乐水',`price`=1900,`remark`='备注个锤子啊',`reward_multiplier_x1000`=2000,`scope_type`=1,`stacking_strategy`=1,`status`=1,`valid_end`='2025-12-25 00:00:00',`valid_start`='2025-11-26 00:00:00',`updated_at`='2025-12-22 15:58:44.505' WHERE `system_item_cards`.`id` = 1 AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.02911525}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.103618458}} +{"level":"info","time":"2025-12-22 15:58:44","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_item_cards?page=1&page_size=10&name=","http_code":200,"business_code":0,"success":true,"cost_seconds":0.049544542,"trace_id":"2003012314655100928","trace_info":{"trace_id":"2003012314655100928","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_item_cards?page=1&page_size=10&name=","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003012314655100928"],"Vary":["Origin"]},"body":{"page":1,"page_size":10,"total":1,"list":[{"id":1,"created_at":"2025-11-14T18:46:15.338+08:00","updated_at":"2025-12-22T15:58:44.505+08:00","name":"双倍快乐水","status":1,"card_type":1,"scope_type":1,"activity_category_id":0,"activity_id":0,"issue_id":0,"price":1900,"valid_start":"2025-11-26T00:00:00+08:00","valid_end":"2025-12-25T00:00:00+08:00","effect_type":2,"reward_multiplier_x1000":2000,"boost_rate_x1000":10,"stacking_strategy":1,"max_effect_value_x1000":2000,"remark":"备注个锤子啊"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.049544209},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 15:58:44","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018406416},{"time":"2025-12-22 15:58:44","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:326","sql":"SELECT count(*) FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.01033025},{"time":"2025-12-22 15:58:44","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:340","sql":"SELECT * FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL ORDER BY `system_item_cards`.`id` DESC LIMIT 10","rows_affected":1,"cost_seconds":0.020431625}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.049544542}} +{"level":"info","time":"2025-12-22 16:01:10","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_item_cards?page=1&page_size=10&name=","http_code":200,"business_code":0,"success":true,"cost_seconds":0.054267167,"trace_id":"2003012926906044416","trace_info":{"trace_id":"2003012926906044416","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_item_cards?page=1&page_size=10&name=","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003012926906044416"],"Vary":["Origin"]},"body":{"page":1,"page_size":10,"total":1,"list":[{"id":1,"created_at":"2025-11-14T18:46:15.338+08:00","updated_at":"2025-12-22T15:58:44.505+08:00","name":"双倍快乐水","status":1,"card_type":1,"scope_type":1,"activity_category_id":0,"activity_id":0,"issue_id":0,"price":1900,"valid_start":"2025-11-26T00:00:00+08:00","valid_end":"2025-12-25T00:00:00+08:00","effect_type":2,"reward_multiplier_x1000":2000,"boost_rate_x1000":10,"stacking_strategy":1,"max_effect_value_x1000":2000,"remark":"备注个锤子啊"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.054266875},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:01:10","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.020637958},{"time":"2025-12-22 16:01:10","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:326","sql":"SELECT count(*) FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.010100208},{"time":"2025-12-22 16:01:10","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:340","sql":"SELECT * FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL ORDER BY `system_item_cards`.`id` DESC LIMIT 10","rows_affected":1,"cost_seconds":0.023166917}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.054267167}} +{"level":"info","time":"2025-12-22 16:01:50","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-22 16:01:54","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_item_cards?page=1&page_size=10&name=","http_code":200,"business_code":0,"success":true,"cost_seconds":0.063680792,"trace_id":"2003013109492486144","trace_info":{"trace_id":"2003013109492486144","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_item_cards?page=1&page_size=10&name=","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003013109492486144"],"Vary":["Origin"]},"body":{"page":1,"page_size":10,"total":1,"list":[{"id":1,"created_at":"2025-11-14T18:46:15.338+08:00","updated_at":"2025-12-22T15:58:44.505+08:00","name":"双倍快乐水","status":1,"card_type":1,"scope_type":1,"activity_category_id":0,"activity_id":0,"issue_id":0,"price":1900,"valid_start":"2025-11-26T00:00:00+08:00","valid_end":"2025-12-25T00:00:00+08:00","effect_type":2,"reward_multiplier_x1000":2000,"boost_rate_x1000":10,"stacking_strategy":1,"max_effect_value_x1000":2000,"remark":"备注个锤子啊"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.0636805},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:01:54","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.026303875},{"time":"2025-12-22 16:01:54","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:326","sql":"SELECT count(*) FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.010704625},{"time":"2025-12-22 16:01:54","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:340","sql":"SELECT * FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL ORDER BY `system_item_cards`.`id` DESC LIMIT 10","rows_affected":1,"cost_seconds":0.025911583}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.063680792}} +{"level":"info","time":"2025-12-22 16:02:01","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"PUT","path":"/api/admin/system_item_cards/1","http_code":200,"business_code":0,"success":true,"cost_seconds":0.061728667,"trace_id":"2003013141486637056","trace_info":{"trace_id":"2003013141486637056","request":{"ttl":"un-limit","method":"PUT","decoded_url":"/api/admin/system_item_cards/1","header":{"Content-Type":"application/json"},"body":"{\"name\":\"双倍快乐水\",\"card_type\":1,\"scope_type\":1,\"effect_type\":1,\"price\":1900,\"reward_multiplier_x1000\":2000,\"boost_rate_x1000\":10,\"stacking_strategy\":1,\"max_effect_value_x1000\":2000,\"status\":1,\"remark\":\"备注个锤子啊\",\"valid_start_unix\":1764086400,\"valid_end_unix\":1766592000}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003013141486637056"],"Vary":["Origin"]},"body":{"message":"操作成功"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.061728042},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:02:01","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.0200985},{"time":"2025-12-22 16:02:01","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:211","sql":"UPDATE `system_item_cards` SET `boost_rate_x1000`=10,`card_type`=1,`effect_type`=1,`max_effect_value_x1000`=2000,`name`='双倍快乐水',`price`=1900,`remark`='备注个锤子啊',`reward_multiplier_x1000`=2000,`scope_type`=1,`stacking_strategy`=1,`status`=1,`valid_end`='2025-12-25 00:00:00',`valid_start`='2025-11-26 00:00:00',`updated_at`='2025-12-22 16:02:01.802' WHERE `system_item_cards`.`id` = 1 AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.032278084}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.061728667}} +{"level":"info","time":"2025-12-22 16:02:01","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/system_item_cards?page=1&page_size=10&name=","http_code":200,"business_code":0,"success":true,"cost_seconds":0.052151625,"trace_id":"2003013142216445952","trace_info":{"trace_id":"2003013142216445952","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/system_item_cards?page=1&page_size=10&name=","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003013142216445952"],"Vary":["Origin"]},"body":{"page":1,"page_size":10,"total":1,"list":[{"id":1,"created_at":"2025-11-14T18:46:15.338+08:00","updated_at":"2025-12-22T16:02:01.802+08:00","name":"双倍快乐水","status":1,"card_type":1,"scope_type":1,"activity_category_id":0,"activity_id":0,"issue_id":0,"price":1900,"valid_start":"2025-11-26T00:00:00+08:00","valid_end":"2025-12-25T00:00:00+08:00","effect_type":1,"reward_multiplier_x1000":2000,"boost_rate_x1000":10,"stacking_strategy":1,"max_effect_value_x1000":2000,"remark":"备注个锤子啊"}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.052151084},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:02:01","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.020010083},{"time":"2025-12-22 16:02:01","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:326","sql":"SELECT count(*) FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL","rows_affected":1,"cost_seconds":0.009821917},{"time":"2025-12-22 16:02:01","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/item_cards_admin.go:340","sql":"SELECT * FROM `system_item_cards` WHERE deleted_at IS NULL AND `system_item_cards`.`deleted_at` IS NULL ORDER BY `system_item_cards`.`id` DESC LIMIT 10","rows_affected":1,"cost_seconds":0.021624333}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.052151625}} +{"level":"info","time":"2025-12-22 16:05:02","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-22 16:05:06","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/pay/orders?current=1&size=10&isTrusted=true&_vts=1766390706439","http_code":200,"business_code":0,"success":true,"cost_seconds":0.268352292,"trace_id":"2003013916065533952","trace_info":{"trace_id":"2003013916065533952","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/pay/orders?current=1&size=10&isTrusted=true&_vts=1766390706439","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003013916065533952"],"Vary":["Origin"]},"body":{"page":1,"page_size":10,"total":18,"list":[{"actual_amount":495,"created_at":"2025-12-22T16:03:59.551+08:00","id":298,"is_consumed":0,"order_no":"O20251222160359","paid_at":"2025-12-22 16:04:05","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:20","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:55:59.237+08:00","id":297,"is_consumed":0,"order_no":"O20251222155559","paid_at":"2025-12-22 15:56:04","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:21","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:53:35.444+08:00","id":296,"is_consumed":0,"order_no":"O20251222155335","paid_at":"2025-12-22 15:53:45","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:22","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:52:40.539+08:00","id":295,"is_consumed":0,"order_no":"O20251222155240","paid_at":"2025-12-22 15:52:46","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:23","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:49:26.185+08:00","id":294,"is_consumed":0,"order_no":"O20251222154926","paid_at":"2025-12-22 15:49:34","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:24","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:48:26.242+08:00","id":293,"is_consumed":0,"order_no":"O20251222154826","paid_at":"2025-12-22 15:48:32","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:25","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:23:48.548+08:00","id":292,"is_consumed":0,"order_no":"O20251222152348","paid_at":"2025-12-22 15:23:54","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:20","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:20:14.237+08:00","id":291,"is_consumed":0,"order_no":"O20251222152014","paid_at":"2025-12-22 15:20:20","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:56:35.169+08:00","id":290,"is_consumed":0,"order_no":"O20251222145635","paid_at":"2025-12-22 14:56:41","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:43:30.723+08:00","id":289,"is_consumed":0,"order_no":"O20251222144330","paid_at":"2025-12-22 14:43:37","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":2,"total_amount":990,"user_id":12}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.268351875},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:06","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.020928},{"time":"2025-12-22 16:05:06","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:106","sql":"SELECT count(*) FROM `orders`","rows_affected":1,"cost_seconds":0.009016792},{"time":"2025-12-22 16:05:06","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:111","sql":"SELECT * FROM `orders` ORDER BY `orders`.`id` DESC LIMIT 10","rows_affected":10,"cost_seconds":0.019561417},{"time":"2025-12-22 16:05:06","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:117","sql":"SELECT * FROM `system_configs` WHERE `system_configs`.`config_key` = 'points_exchange_per_cent' AND `system_configs`.`deleted_at` IS NULL ORDER BY `system_configs`.`id` LIMIT 1","rows_affected":0,"cost_seconds":0.019496},{"time":"2025-12-22 16:05:06","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222160359'","rows_affected":0,"cost_seconds":0.018783542},{"time":"2025-12-22 16:05:06","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155559'","rows_affected":0,"cost_seconds":0.019592583},{"time":"2025-12-22 16:05:06","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155335'","rows_affected":0,"cost_seconds":0.020305},{"time":"2025-12-22 16:05:06","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155240'","rows_affected":0,"cost_seconds":0.019252958},{"time":"2025-12-22 16:05:06","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222154926'","rows_affected":0,"cost_seconds":0.021311125},{"time":"2025-12-22 16:05:06","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222154826'","rows_affected":0,"cost_seconds":0.018640375},{"time":"2025-12-22 16:05:06","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222152348'","rows_affected":0,"cost_seconds":0.019022333},{"time":"2025-12-22 16:05:06","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222152014'","rows_affected":0,"cost_seconds":0.019430708},{"time":"2025-12-22 16:05:06","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222145635'","rows_affected":0,"cost_seconds":0.019435041},{"time":"2025-12-22 16:05:06","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222144330'","rows_affected":0,"cost_seconds":0.019299292}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.268352292}} +{"level":"info","time":"2025-12-22 16:05:09","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/pay/orders?current=1&size=100","http_code":200,"business_code":0,"success":true,"cost_seconds":0.416950542,"trace_id":"2003013928858161152","trace_info":{"trace_id":"2003013928858161152","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/pay/orders?current=1&size=100","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003013928858161152"],"Vary":["Origin"]},"body":{"page":1,"page_size":100,"total":18,"list":[{"actual_amount":495,"created_at":"2025-12-22T16:03:59.551+08:00","id":298,"is_consumed":0,"order_no":"O20251222160359","paid_at":"2025-12-22 16:04:05","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:20","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:55:59.237+08:00","id":297,"is_consumed":0,"order_no":"O20251222155559","paid_at":"2025-12-22 15:56:04","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:21","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:53:35.444+08:00","id":296,"is_consumed":0,"order_no":"O20251222155335","paid_at":"2025-12-22 15:53:45","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:22","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:52:40.539+08:00","id":295,"is_consumed":0,"order_no":"O20251222155240","paid_at":"2025-12-22 15:52:46","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:23","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:49:26.185+08:00","id":294,"is_consumed":0,"order_no":"O20251222154926","paid_at":"2025-12-22 15:49:34","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:24","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:48:26.242+08:00","id":293,"is_consumed":0,"order_no":"O20251222154826","paid_at":"2025-12-22 15:48:32","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:25","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:23:48.548+08:00","id":292,"is_consumed":0,"order_no":"O20251222152348","paid_at":"2025-12-22 15:23:54","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:20","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:20:14.237+08:00","id":291,"is_consumed":0,"order_no":"O20251222152014","paid_at":"2025-12-22 15:20:20","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:56:35.169+08:00","id":290,"is_consumed":0,"order_no":"O20251222145635","paid_at":"2025-12-22 14:56:41","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:43:30.723+08:00","id":289,"is_consumed":0,"order_no":"O20251222144330","paid_at":"2025-12-22 14:43:37","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:41:40.953+08:00","id":288,"is_consumed":0,"order_no":"O20251222144140","paid_at":"2025-12-22 14:41:46","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:39:59.718+08:00","id":287,"is_consumed":0,"order_no":"O20251222143959","paid_at":"2025-12-22 14:40:05","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":3000,"created_at":"2025-12-22T14:37:30.498+08:00","id":286,"is_consumed":0,"order_no":"O20251222143730","paid_at":"2025-12-22 14:37:36","points_amount":0,"points_used":0,"remark":"lottery:activity:52|issue:57|count:1|slots:45:1","source_type":2,"status":2,"total_amount":3000,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:57:40.712+08:00","id":285,"is_consumed":0,"order_no":"O20251222135740","paid_at":"2025-12-22 13:57:47","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:43:07.064+08:00","id":284,"is_consumed":0,"order_no":"O20251222134307","paid_at":"2025-12-22 13:43:15","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:32:34.182+08:00","id":283,"is_consumed":0,"order_no":"O20251222133234","paid_at":"2025-12-22 13:32:40","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:30:35.46+08:00","id":282,"is_consumed":0,"order_no":"O20251222133035","paid_at":"2025-12-22 13:30:42","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:27:07.25+08:00","id":281,"is_consumed":0,"order_no":"O20251222132707","paid_at":"2025-12-22 13:27:13","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":2,"total_amount":990,"user_id":12}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.416949917},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.01804925},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:106","sql":"SELECT count(*) FROM `orders`","rows_affected":1,"cost_seconds":0.00851425},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:111","sql":"SELECT * FROM `orders` ORDER BY `orders`.`id` DESC LIMIT 100","rows_affected":18,"cost_seconds":0.017916042},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:117","sql":"SELECT * FROM `system_configs` WHERE `system_configs`.`config_key` = 'points_exchange_per_cent' AND `system_configs`.`deleted_at` IS NULL ORDER BY `system_configs`.`id` LIMIT 1","rows_affected":0,"cost_seconds":0.018470542},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222160359'","rows_affected":0,"cost_seconds":0.018912292},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155559'","rows_affected":0,"cost_seconds":0.02010975},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155335'","rows_affected":0,"cost_seconds":0.019465084},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155240'","rows_affected":0,"cost_seconds":0.019019375},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222154926'","rows_affected":0,"cost_seconds":0.01934375},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222154826'","rows_affected":0,"cost_seconds":0.019060333},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222152348'","rows_affected":0,"cost_seconds":0.019609833},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222152014'","rows_affected":0,"cost_seconds":0.019913708},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222145635'","rows_affected":0,"cost_seconds":0.019025709},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222144330'","rows_affected":0,"cost_seconds":0.018996292},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222144140'","rows_affected":0,"cost_seconds":0.021149291},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222143959'","rows_affected":0,"cost_seconds":0.019365833},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222143730'","rows_affected":0,"cost_seconds":0.018762583},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222135740'","rows_affected":0,"cost_seconds":0.019324708},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222134307'","rows_affected":0,"cost_seconds":0.021271625},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222133234'","rows_affected":0,"cost_seconds":0.0180275},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222133035'","rows_affected":0,"cost_seconds":0.020153834},{"time":"2025-12-22 16:05:09","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222132707'","rows_affected":0,"cost_seconds":0.019214292}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.416950542}} +{"level":"info","time":"2025-12-22 16:05:14","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":1.217169333,"trace_id":"2003013945429856256","trace_info":{"trace_id":"2003013945429856256","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222160359\",\"amount\":495,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003013945429856256"],"Vary":["Origin"]},"body":{"order_no":"O20251222160359","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":1.217168958},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:13","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018556459},{"time":"2025-12-22 16:05:13","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222160359' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.0182525},{"time":"2025-12-22 16:05:13","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222160359'","rows_affected":0,"cost_seconds":0.020164542},{"time":"2025-12-22 16:05:14","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (298,'O20251222160359','RO20251222160359-1766390713','wechat_jsapi','PROCESSING',495,'批量退款','2025-12-22 16:05:14.319','{\"refund_id\":\"50301005652025122237784981651\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.0386665},{"time":"2025-12-22 16:05:14","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:14.368' WHERE `orders`.`id` = 298","rows_affected":1,"cost_seconds":0.029580584},{"time":"2025-12-22 16:05:14","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',4,'payment_refund','50301005652025122237784981651','批量退款')","rows_affected":1,"cost_seconds":0.02904625}],"redis":null,"mongos":null,"success":true,"cost_seconds":1.217169333}} +{"level":"info","time":"2025-12-22 16:05:16","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":1.239803125,"trace_id":"2003013950936977408","trace_info":{"trace_id":"2003013950936977408","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222155559\",\"amount\":495,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003013950936977408"],"Vary":["Origin"]},"body":{"order_no":"O20251222155559","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":1.239802791},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:14","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.020604542},{"time":"2025-12-22 16:05:14","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222155559' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.022604542},{"time":"2025-12-22 16:05:14","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155559'","rows_affected":0,"cost_seconds":0.02054325},{"time":"2025-12-22 16:05:15","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (297,'O20251222155559','RO20251222155559-1766390714','wechat_jsapi','PROCESSING',495,'批量退款','2025-12-22 16:05:15.738','{\"refund_id\":\"50300605482025122206813969455\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.031486459},{"time":"2025-12-22 16:05:15","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:15.779' WHERE `orders`.`id` = 297","rows_affected":1,"cost_seconds":0.029499333},{"time":"2025-12-22 16:05:16","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',4,'payment_refund','50300605482025122206813969455','批量退款')","rows_affected":1,"cost_seconds":0.029033125}],"redis":null,"mongos":null,"success":true,"cost_seconds":1.239803125}} +{"level":"info","time":"2025-12-22 16:05:17","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":0.969523375,"trace_id":"2003013956511207424","trace_info":{"trace_id":"2003013956511207424","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222155335\",\"amount\":495,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003013956511207424"],"Vary":["Origin"]},"body":{"order_no":"O20251222155335","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.96952325},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:16","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.017228875},{"time":"2025-12-22 16:05:16","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222155335' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.019028083},{"time":"2025-12-22 16:05:16","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155335'","rows_affected":0,"cost_seconds":0.018693666},{"time":"2025-12-22 16:05:16","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (296,'O20251222155335','RO20251222155335-1766390716','wechat_jsapi','PROCESSING',495,'批量退款','2025-12-22 16:05:16.779','{\"refund_id\":\"50302705622025122255027520651\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.040591084},{"time":"2025-12-22 16:05:16","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:16.829' WHERE `orders`.`id` = 296","rows_affected":1,"cost_seconds":0.031683209},{"time":"2025-12-22 16:05:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',4,'payment_refund','50302705622025122255027520651','批量退款')","rows_affected":1,"cost_seconds":0.028102917}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.969523375}} +{"level":"info","time":"2025-12-22 16:05:18","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":1.178681333,"trace_id":"2003013960952975360","trace_info":{"trace_id":"2003013960952975360","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222155240\",\"amount\":495,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003013960952975360"],"Vary":["Origin"]},"body":{"order_no":"O20251222155240","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":1.178681125},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.017573625},{"time":"2025-12-22 16:05:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222155240' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.01887975},{"time":"2025-12-22 16:05:17","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155240'","rows_affected":0,"cost_seconds":0.01925725},{"time":"2025-12-22 16:05:18","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (295,'O20251222155240','RO20251222155240-1766390717','wechat_jsapi','PROCESSING',495,'批量退款','2025-12-22 16:05:18.065','{\"refund_id\":\"50303405712025122215152529913\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.036366167},{"time":"2025-12-22 16:05:18","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:18.11' WHERE `orders`.`id` = 295","rows_affected":1,"cost_seconds":0.028504417},{"time":"2025-12-22 16:05:18","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',4,'payment_refund','50303405712025122215152529913','批量退款')","rows_affected":1,"cost_seconds":0.029599459}],"redis":null,"mongos":null,"success":true,"cost_seconds":1.178681333}} +{"level":"info","time":"2025-12-22 16:05:19","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":0.938253,"trace_id":"2003013966283935744","trace_info":{"trace_id":"2003013966283935744","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222154926\",\"amount\":495,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003013966283935744"],"Vary":["Origin"]},"body":{"order_no":"O20251222154926","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.938252958},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:18","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.017371458},{"time":"2025-12-22 16:05:18","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222154926' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.019070083},{"time":"2025-12-22 16:05:18","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222154926'","rows_affected":0,"cost_seconds":0.018630875},{"time":"2025-12-22 16:05:19","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (294,'O20251222154926','RO20251222154926-1766390718','wechat_jsapi','PROCESSING',495,'批量退款','2025-12-22 16:05:19.085','{\"refund_id\":\"50300705442025122294178734168\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.040387042},{"time":"2025-12-22 16:05:19","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:19.135' WHERE `orders`.`id` = 294","rows_affected":1,"cost_seconds":0.030297208},{"time":"2025-12-22 16:05:19","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',4,'payment_refund','50300705442025122294178734168','批量退款')","rows_affected":1,"cost_seconds":0.030929792}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.938253}} +{"level":"info","time":"2025-12-22 16:05:20","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":1.199135375,"trace_id":"2003013970608263168","trace_info":{"trace_id":"2003013970608263168","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222154826\",\"amount\":495,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003013970608263168"],"Vary":["Origin"]},"body":{"order_no":"O20251222154826","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":1.199135292},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:19","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.017607},{"time":"2025-12-22 16:05:19","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222154826' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.019585625},{"time":"2025-12-22 16:05:19","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222154826'","rows_affected":0,"cost_seconds":0.019180667},{"time":"2025-12-22 16:05:20","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (293,'O20251222154826','RO20251222154826-1766390719','wechat_jsapi','PROCESSING',495,'批量退款','2025-12-22 16:05:20.392','{\"refund_id\":\"50301005572025122262260275897\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.033218083},{"time":"2025-12-22 16:05:20","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:20.435' WHERE `orders`.`id` = 293","rows_affected":1,"cost_seconds":0.029067667},{"time":"2025-12-22 16:05:20","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',4,'payment_refund','50301005572025122262260275897','批量退款')","rows_affected":1,"cost_seconds":0.028436625}],"redis":null,"mongos":null,"success":true,"cost_seconds":1.199135375}} +{"level":"info","time":"2025-12-22 16:05:21","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":0.965414625,"trace_id":"2003013976018915328","trace_info":{"trace_id":"2003013976018915328","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222152348\",\"amount\":495,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003013976018915328"],"Vary":["Origin"]},"body":{"order_no":"O20251222152348","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.965414333},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:20","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018696583},{"time":"2025-12-22 16:05:20","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222152348' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.020415542},{"time":"2025-12-22 16:05:20","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222152348'","rows_affected":0,"cost_seconds":0.019368417},{"time":"2025-12-22 16:05:21","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (292,'O20251222152348','RO20251222152348-1766390720','wechat_jsapi','PROCESSING',495,'批量退款','2025-12-22 16:05:21.438','{\"refund_id\":\"50302105502025122231950631594\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.0363265},{"time":"2025-12-22 16:05:21","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:21.483' WHERE `orders`.`id` = 292","rows_affected":1,"cost_seconds":0.029637958},{"time":"2025-12-22 16:05:21","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',4,'payment_refund','50302105502025122231950631594','批量退款')","rows_affected":1,"cost_seconds":0.029838542}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.965414625}} +{"level":"info","time":"2025-12-22 16:05:22","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":1.075241583,"trace_id":"2003013980464877568","trace_info":{"trace_id":"2003013980464877568","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222152014\",\"amount\":495,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003013980464877568"],"Vary":["Origin"]},"body":{"order_no":"O20251222152014","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":1.07524125},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:21","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.0182045},{"time":"2025-12-22 16:05:21","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222152014' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.019904375},{"time":"2025-12-22 16:05:21","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222152014'","rows_affected":0,"cost_seconds":0.021373708},{"time":"2025-12-22 16:05:22","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (291,'O20251222152014','RO20251222152014-1766390721','wechat_jsapi','PROCESSING',495,'批量退款','2025-12-22 16:05:22.617','{\"refund_id\":\"50301105572025122214399857334\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.032227},{"time":"2025-12-22 16:05:22","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:22.659' WHERE `orders`.`id` = 291","rows_affected":1,"cost_seconds":0.028502708},{"time":"2025-12-22 16:05:22","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',4,'payment_refund','50301105572025122214399857334','批量退款')","rows_affected":1,"cost_seconds":0.029419209}],"redis":null,"mongos":null,"success":true,"cost_seconds":1.075241583}} +{"level":"info","time":"2025-12-22 16:05:24","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":1.048581584,"trace_id":"2003013985334464512","trace_info":{"trace_id":"2003013985334464512","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222145635\",\"amount\":990,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003013985334464512"],"Vary":["Origin"]},"body":{"order_no":"O20251222145635","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":1.048581167},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:22","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018648},{"time":"2025-12-22 16:05:22","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222145635' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018748},{"time":"2025-12-22 16:05:23","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222145635'","rows_affected":0,"cost_seconds":0.018705041},{"time":"2025-12-22 16:05:23","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (290,'O20251222145635','RO20251222145635-1766390723','wechat_jsapi','PROCESSING',990,'批量退款','2025-12-22 16:05:23.8','{\"refund_id\":\"50300205482025122230963991566\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.03534875},{"time":"2025-12-22 16:05:23","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:23.845' WHERE `orders`.`id` = 290","rows_affected":1,"cost_seconds":0.029873459},{"time":"2025-12-22 16:05:24","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',9,'payment_refund','50300205482025122230963991566','批量退款')","rows_affected":1,"cost_seconds":0.030801583}],"redis":null,"mongos":null,"success":true,"cost_seconds":1.048581584}} +{"level":"info","time":"2025-12-22 16:05:25","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":0.901767041,"trace_id":"2003013990111776768","trace_info":{"trace_id":"2003013990111776768","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222144330\",\"amount\":990,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003013990111776768"],"Vary":["Origin"]},"body":{"order_no":"O20251222144330","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.901766833},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:24","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.017923084},{"time":"2025-12-22 16:05:24","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222144330' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.019509416},{"time":"2025-12-22 16:05:24","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222144330'","rows_affected":0,"cost_seconds":0.021349125},{"time":"2025-12-22 16:05:24","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (289,'O20251222144330','RO20251222144330-1766390724','wechat_jsapi','PROCESSING',990,'批量退款','2025-12-22 16:05:24.792','{\"refund_id\":\"50301405532025122271234901145\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.038500667},{"time":"2025-12-22 16:05:24","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:24.841' WHERE `orders`.`id` = 289","rows_affected":1,"cost_seconds":0.028587959},{"time":"2025-12-22 16:05:25","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',9,'payment_refund','50301405532025122271234901145','批量退款')","rows_affected":1,"cost_seconds":0.030183458}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.901767041}} +{"level":"info","time":"2025-12-22 16:05:25","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":0.908195667,"trace_id":"2003013994247360512","trace_info":{"trace_id":"2003013994247360512","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222144140\",\"amount\":990,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003013994247360512"],"Vary":["Origin"]},"body":{"order_no":"O20251222144140","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.908195584},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:25","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.016426542},{"time":"2025-12-22 16:05:25","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222144140' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.017589667},{"time":"2025-12-22 16:05:25","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222144140'","rows_affected":0,"cost_seconds":0.01842325},{"time":"2025-12-22 16:05:25","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (288,'O20251222144140','RO20251222144140-1766390725','wechat_jsapi','PROCESSING',990,'批量退款','2025-12-22 16:05:25.797','{\"refund_id\":\"50301405732025122224162778814\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.032554041},{"time":"2025-12-22 16:05:25","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:25.839' WHERE `orders`.`id` = 288","rows_affected":1,"cost_seconds":0.030725417},{"time":"2025-12-22 16:05:25","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',9,'payment_refund','50301405732025122224162778814','批量退款')","rows_affected":1,"cost_seconds":0.029273375}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.908195667}} +{"level":"info","time":"2025-12-22 16:05:26","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":0.8379355,"trace_id":"2003013998437470208","trace_info":{"trace_id":"2003013998437470208","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222143959\",\"amount\":990,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003013998437470208"],"Vary":["Origin"]},"body":{"order_no":"O20251222143959","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.837935417},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:26","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.017362292},{"time":"2025-12-22 16:05:26","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222143959' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018705875},{"time":"2025-12-22 16:05:26","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222143959'","rows_affected":0,"cost_seconds":0.018789792},{"time":"2025-12-22 16:05:26","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (287,'O20251222143959','RO20251222143959-1766390726','wechat_jsapi','PROCESSING',990,'批量退款','2025-12-22 16:05:26.726','{\"refund_id\":\"50302605502025122257633802990\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.030168542},{"time":"2025-12-22 16:05:26","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:26.766' WHERE `orders`.`id` = 287","rows_affected":1,"cost_seconds":0.02858225},{"time":"2025-12-22 16:05:26","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',9,'payment_refund','50302605502025122257633802990','批量退款')","rows_affected":1,"cost_seconds":0.028435875}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.8379355}} +{"level":"info","time":"2025-12-22 16:05:27","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":0.88104375,"trace_id":"2003014002308812800","trace_info":{"trace_id":"2003014002308812800","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222143730\",\"amount\":3000,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003014002308812800"],"Vary":["Origin"]},"body":{"order_no":"O20251222143730","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.881043291},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:27","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.020659125},{"time":"2025-12-22 16:05:27","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222143730' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018961083},{"time":"2025-12-22 16:05:27","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222143730'","rows_affected":0,"cost_seconds":0.0189555},{"time":"2025-12-22 16:05:27","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (286,'O20251222143730','RO20251222143730-1766390727','wechat_jsapi','PROCESSING',3000,'批量退款','2025-12-22 16:05:27.695','{\"refund_id\":\"50301605732025122254907538436\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.034230375},{"time":"2025-12-22 16:05:27","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:27.738' WHERE `orders`.`id` = 286","rows_affected":1,"cost_seconds":0.028447875},{"time":"2025-12-22 16:05:27","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',30,'payment_refund','50301605732025122254907538436','批量退款')","rows_affected":1,"cost_seconds":0.028189}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.88104375}} +{"level":"info","time":"2025-12-22 16:05:28","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":0.89476275,"trace_id":"2003014006410842112","trace_info":{"trace_id":"2003014006410842112","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222135740\",\"amount\":990,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003014006410842112"],"Vary":["Origin"]},"body":{"order_no":"O20251222135740","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.894762333},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:28","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018133208},{"time":"2025-12-22 16:05:28","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222135740' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.021185167},{"time":"2025-12-22 16:05:28","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222135740'","rows_affected":0,"cost_seconds":0.0197055},{"time":"2025-12-22 16:05:28","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (285,'O20251222135740','RO20251222135740-1766390728','wechat_jsapi','PROCESSING',990,'批量退款','2025-12-22 16:05:28.685','{\"refund_id\":\"50303605752025122258501866296\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.02914225},{"time":"2025-12-22 16:05:28","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:28.723' WHERE `orders`.`id` = 285","rows_affected":1,"cost_seconds":0.028389625},{"time":"2025-12-22 16:05:28","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',9,'payment_refund','50303605752025122258501866296','批量退款')","rows_affected":1,"cost_seconds":0.02929925}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.89476275}} +{"level":"info","time":"2025-12-22 16:05:29","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":1.000444583,"trace_id":"2003014010567397376","trace_info":{"trace_id":"2003014010567397376","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222134307\",\"amount\":990,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003014010567397376"],"Vary":["Origin"]},"body":{"order_no":"O20251222134307","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":1.000444083},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:28","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.017908958},{"time":"2025-12-22 16:05:29","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222134307' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.017826542},{"time":"2025-12-22 16:05:29","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222134307'","rows_affected":0,"cost_seconds":0.017847625},{"time":"2025-12-22 16:05:29","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (284,'O20251222134307','RO20251222134307-1766390729','wechat_jsapi','PROCESSING',990,'批量退款','2025-12-22 16:05:29.776','{\"refund_id\":\"50300405642025122258737274543\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.034414875},{"time":"2025-12-22 16:05:29","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:29.821' WHERE `orders`.`id` = 284","rows_affected":1,"cost_seconds":0.03013175},{"time":"2025-12-22 16:05:29","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',9,'payment_refund','50300405642025122258737274543','批量退款')","rows_affected":1,"cost_seconds":0.03250325}],"redis":null,"mongos":null,"success":true,"cost_seconds":1.000444583}} +{"level":"info","time":"2025-12-22 16:05:30","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":0.85050675,"trace_id":"2003014015147577344","trace_info":{"trace_id":"2003014015147577344","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222133234\",\"amount\":990,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003014015147577344"],"Vary":["Origin"]},"body":{"order_no":"O20251222133234","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.850506333},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:30","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.017206208},{"time":"2025-12-22 16:05:30","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222133234' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.020625083},{"time":"2025-12-22 16:05:30","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222133234'","rows_affected":0,"cost_seconds":0.018080542},{"time":"2025-12-22 16:05:30","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (283,'O20251222133234','RO20251222133234-1766390730','wechat_jsapi','PROCESSING',990,'批量退款','2025-12-22 16:05:30.711','{\"refund_id\":\"50300405442025122293133694701\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.037457959},{"time":"2025-12-22 16:05:30","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:30.758' WHERE `orders`.`id` = 283","rows_affected":1,"cost_seconds":0.02956375},{"time":"2025-12-22 16:05:30","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',9,'payment_refund','50300405442025122293133694701','批量退款')","rows_affected":1,"cost_seconds":0.030097334}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.85050675}} +{"level":"info","time":"2025-12-22 16:05:31","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":0.878247625,"trace_id":"2003014019115388928","trace_info":{"trace_id":"2003014019115388928","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222133035\",\"amount\":990,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003014019115388928"],"Vary":["Origin"]},"body":{"order_no":"O20251222133035","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.87824725},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:31","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.017999459},{"time":"2025-12-22 16:05:31","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222133035' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.0190485},{"time":"2025-12-22 16:05:31","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222133035'","rows_affected":0,"cost_seconds":0.020389375},{"time":"2025-12-22 16:05:31","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (282,'O20251222133035','RO20251222133035-1766390731','wechat_jsapi','PROCESSING',990,'批量退款','2025-12-22 16:05:31.691','{\"refund_id\":\"50303705472025122266093161364\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.034929541},{"time":"2025-12-22 16:05:31","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:31.736' WHERE `orders`.`id` = 282","rows_affected":1,"cost_seconds":0.028418125},{"time":"2025-12-22 16:05:31","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',9,'payment_refund','50303705472025122266093161364','批量退款')","rows_affected":1,"cost_seconds":0.029703584}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.878247625}} +{"level":"info","time":"2025-12-22 16:05:32","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":0.901767291,"trace_id":"2003014023230001152","trace_info":{"trace_id":"2003014023230001152","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222132707\",\"amount\":990,\"reason\":\"批量退款\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003014023230001152"],"Vary":["Origin"]},"body":{"order_no":"O20251222132707","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":0.901766958},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.019209084},{"time":"2025-12-22 16:05:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222132707' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.019358292},{"time":"2025-12-22 16:05:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222132707'","rows_affected":0,"cost_seconds":0.020134583},{"time":"2025-12-22 16:05:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (281,'O20251222132707','RO20251222132707-1766390732','wechat_jsapi','PROCESSING',990,'批量退款','2025-12-22 16:05:32.687','{\"refund_id\":\"50300305562025122221365465310\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.035923125},{"time":"2025-12-22 16:05:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:05:32.733' WHERE `orders`.`id` = 281","rows_affected":1,"cost_seconds":0.0292045},{"time":"2025-12-22 16:05:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',9,'payment_refund','50300305562025122221365465310','批量退款')","rows_affected":1,"cost_seconds":0.03089975}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.901767291}} +{"level":"info","time":"2025-12-22 16:05:33","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/pay/orders?current=1&size=100","http_code":200,"business_code":0,"success":true,"cost_seconds":0.427974791,"trace_id":"2003014027432693760","trace_info":{"trace_id":"2003014027432693760","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/pay/orders?current=1&size=100","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003014027432693760"],"Vary":["Origin"]},"body":{"page":1,"page_size":100,"total":18,"list":[{"actual_amount":495,"created_at":"2025-12-22T16:03:59.551+08:00","id":298,"is_consumed":0,"order_no":"O20251222160359","paid_at":"2025-12-22 16:04:05","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:20","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:55:59.237+08:00","id":297,"is_consumed":0,"order_no":"O20251222155559","paid_at":"2025-12-22 15:56:04","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:21","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:53:35.444+08:00","id":296,"is_consumed":0,"order_no":"O20251222155335","paid_at":"2025-12-22 15:53:45","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:22","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:52:40.539+08:00","id":295,"is_consumed":0,"order_no":"O20251222155240","paid_at":"2025-12-22 15:52:46","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:23","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:49:26.185+08:00","id":294,"is_consumed":0,"order_no":"O20251222154926","paid_at":"2025-12-22 15:49:34","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:24","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:48:26.242+08:00","id":293,"is_consumed":0,"order_no":"O20251222154826","paid_at":"2025-12-22 15:48:32","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:25","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:23:48.548+08:00","id":292,"is_consumed":0,"order_no":"O20251222152348","paid_at":"2025-12-22 15:23:54","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:20","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:20:14.237+08:00","id":291,"is_consumed":0,"order_no":"O20251222152014","paid_at":"2025-12-22 15:20:20","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:56:35.169+08:00","id":290,"is_consumed":0,"order_no":"O20251222145635","paid_at":"2025-12-22 14:56:41","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:43:30.723+08:00","id":289,"is_consumed":0,"order_no":"O20251222144330","paid_at":"2025-12-22 14:43:37","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:41:40.953+08:00","id":288,"is_consumed":0,"order_no":"O20251222144140","paid_at":"2025-12-22 14:41:46","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:39:59.718+08:00","id":287,"is_consumed":0,"order_no":"O20251222143959","paid_at":"2025-12-22 14:40:05","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":3000,"created_at":"2025-12-22T14:37:30.498+08:00","id":286,"is_consumed":0,"order_no":"O20251222143730","paid_at":"2025-12-22 14:37:36","points_amount":0,"points_used":0,"remark":"lottery:activity:52|issue:57|count:1|slots:45:1","source_type":2,"status":4,"total_amount":3000,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:57:40.712+08:00","id":285,"is_consumed":0,"order_no":"O20251222135740","paid_at":"2025-12-22 13:57:47","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:43:07.064+08:00","id":284,"is_consumed":0,"order_no":"O20251222134307","paid_at":"2025-12-22 13:43:15","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:32:34.182+08:00","id":283,"is_consumed":0,"order_no":"O20251222133234","paid_at":"2025-12-22 13:32:40","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:30:35.46+08:00","id":282,"is_consumed":0,"order_no":"O20251222133035","paid_at":"2025-12-22 13:30:42","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:27:07.25+08:00","id":281,"is_consumed":0,"order_no":"O20251222132707","paid_at":"2025-12-22 13:27:13","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.427974458},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.018257},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:106","sql":"SELECT count(*) FROM `orders`","rows_affected":1,"cost_seconds":0.010410542},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:111","sql":"SELECT * FROM `orders` ORDER BY `orders`.`id` DESC LIMIT 100","rows_affected":18,"cost_seconds":0.021771917},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:117","sql":"SELECT * FROM `system_configs` WHERE `system_configs`.`config_key` = 'points_exchange_per_cent' AND `system_configs`.`deleted_at` IS NULL ORDER BY `system_configs`.`id` LIMIT 1","rows_affected":0,"cost_seconds":0.018884458},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222160359'","rows_affected":0,"cost_seconds":0.019598541},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155559'","rows_affected":0,"cost_seconds":0.019296083},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155335'","rows_affected":0,"cost_seconds":0.020544917},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155240'","rows_affected":0,"cost_seconds":0.019441125},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222154926'","rows_affected":0,"cost_seconds":0.020131875},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222154826'","rows_affected":0,"cost_seconds":0.01923775},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222152348'","rows_affected":0,"cost_seconds":0.020050417},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222152014'","rows_affected":0,"cost_seconds":0.019306083},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222145635'","rows_affected":0,"cost_seconds":0.017972042},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222144330'","rows_affected":0,"cost_seconds":0.019021916},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222144140'","rows_affected":0,"cost_seconds":0.021185333},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222143959'","rows_affected":0,"cost_seconds":0.020534625},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222143730'","rows_affected":0,"cost_seconds":0.0202645},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222135740'","rows_affected":0,"cost_seconds":0.020293792},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222134307'","rows_affected":0,"cost_seconds":0.019684083},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222133234'","rows_affected":0,"cost_seconds":0.021226375},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222133035'","rows_affected":0,"cost_seconds":0.020088625},{"time":"2025-12-22 16:05:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222132707'","rows_affected":0,"cost_seconds":0.019144375}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.427974791}} +{"level":"info","time":"2025-12-22 16:08:33","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/pay/orders?current=1&size=100&isTrusted=true&_vts=1766390912853","http_code":200,"business_code":0,"success":true,"cost_seconds":0.442084792,"trace_id":"2003014781836988416","trace_info":{"trace_id":"2003014781836988416","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/pay/orders?current=1&size=100&isTrusted=true&_vts=1766390912853","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003014781836988416"],"Vary":["Origin"]},"body":{"page":1,"page_size":100,"total":19,"list":[{"actual_amount":495,"created_at":"2025-12-22T16:06:40.192+08:00","id":299,"is_consumed":0,"order_no":"O20251222160640","paid_at":"2025-12-22 16:06:46","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T16:03:59.551+08:00","id":298,"is_consumed":0,"order_no":"O20251222160359","paid_at":"2025-12-22 16:04:05","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:20","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:55:59.237+08:00","id":297,"is_consumed":0,"order_no":"O20251222155559","paid_at":"2025-12-22 15:56:04","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:21","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:53:35.444+08:00","id":296,"is_consumed":0,"order_no":"O20251222155335","paid_at":"2025-12-22 15:53:45","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:22","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:52:40.539+08:00","id":295,"is_consumed":0,"order_no":"O20251222155240","paid_at":"2025-12-22 15:52:46","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:23","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:49:26.185+08:00","id":294,"is_consumed":0,"order_no":"O20251222154926","paid_at":"2025-12-22 15:49:34","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:24","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:48:26.242+08:00","id":293,"is_consumed":0,"order_no":"O20251222154826","paid_at":"2025-12-22 15:48:32","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:25","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:23:48.548+08:00","id":292,"is_consumed":0,"order_no":"O20251222152348","paid_at":"2025-12-22 15:23:54","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:20","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:20:14.237+08:00","id":291,"is_consumed":0,"order_no":"O20251222152014","paid_at":"2025-12-22 15:20:20","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:56:35.169+08:00","id":290,"is_consumed":0,"order_no":"O20251222145635","paid_at":"2025-12-22 14:56:41","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:43:30.723+08:00","id":289,"is_consumed":0,"order_no":"O20251222144330","paid_at":"2025-12-22 14:43:37","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:41:40.953+08:00","id":288,"is_consumed":0,"order_no":"O20251222144140","paid_at":"2025-12-22 14:41:46","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:39:59.718+08:00","id":287,"is_consumed":0,"order_no":"O20251222143959","paid_at":"2025-12-22 14:40:05","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":3000,"created_at":"2025-12-22T14:37:30.498+08:00","id":286,"is_consumed":0,"order_no":"O20251222143730","paid_at":"2025-12-22 14:37:36","points_amount":0,"points_used":0,"remark":"lottery:activity:52|issue:57|count:1|slots:45:1","source_type":2,"status":4,"total_amount":3000,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:57:40.712+08:00","id":285,"is_consumed":0,"order_no":"O20251222135740","paid_at":"2025-12-22 13:57:47","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:43:07.064+08:00","id":284,"is_consumed":0,"order_no":"O20251222134307","paid_at":"2025-12-22 13:43:15","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:32:34.182+08:00","id":283,"is_consumed":0,"order_no":"O20251222133234","paid_at":"2025-12-22 13:32:40","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:30:35.46+08:00","id":282,"is_consumed":0,"order_no":"O20251222133035","paid_at":"2025-12-22 13:30:42","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:27:07.25+08:00","id":281,"is_consumed":0,"order_no":"O20251222132707","paid_at":"2025-12-22 13:27:13","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.442084625},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:08:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.019424333},{"time":"2025-12-22 16:08:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:106","sql":"SELECT count(*) FROM `orders`","rows_affected":1,"cost_seconds":0.013985417},{"time":"2025-12-22 16:08:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:111","sql":"SELECT * FROM `orders` ORDER BY `orders`.`id` DESC LIMIT 100","rows_affected":19,"cost_seconds":0.022821208},{"time":"2025-12-22 16:08:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:117","sql":"SELECT * FROM `system_configs` WHERE `system_configs`.`config_key` = 'points_exchange_per_cent' AND `system_configs`.`deleted_at` IS NULL ORDER BY `system_configs`.`id` LIMIT 1","rows_affected":0,"cost_seconds":0.020000625},{"time":"2025-12-22 16:08:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222160640'","rows_affected":0,"cost_seconds":0.020146541},{"time":"2025-12-22 16:08:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222160359'","rows_affected":0,"cost_seconds":0.019340125},{"time":"2025-12-22 16:08:32","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155559'","rows_affected":0,"cost_seconds":0.019385917},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155335'","rows_affected":0,"cost_seconds":0.018983917},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155240'","rows_affected":0,"cost_seconds":0.021757625},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222154926'","rows_affected":0,"cost_seconds":0.020196166},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222154826'","rows_affected":0,"cost_seconds":0.019490042},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222152348'","rows_affected":0,"cost_seconds":0.018351042},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222152014'","rows_affected":0,"cost_seconds":0.019048},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222145635'","rows_affected":0,"cost_seconds":0.018703625},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222144330'","rows_affected":0,"cost_seconds":0.018164792},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222144140'","rows_affected":0,"cost_seconds":0.01835925},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222143959'","rows_affected":0,"cost_seconds":0.018779208},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222143730'","rows_affected":0,"cost_seconds":0.019717542},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222135740'","rows_affected":0,"cost_seconds":0.018839916},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222134307'","rows_affected":0,"cost_seconds":0.018807208},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222133234'","rows_affected":0,"cost_seconds":0.018421667},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222133035'","rows_affected":0,"cost_seconds":0.019291333},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222132707'","rows_affected":0,"cost_seconds":0.018704042}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.442084792}} +{"level":"info","time":"2025-12-22 16:08:33","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/pay/orders?current=1&size=100&isTrusted=true&_vts=1766390913045","http_code":200,"business_code":0,"success":true,"cost_seconds":0.518373208,"trace_id":"2003014782612934656","trace_info":{"trace_id":"2003014782612934656","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/pay/orders?current=1&size=100&isTrusted=true&_vts=1766390913045","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003014782612934656"],"Vary":["Origin"]},"body":{"page":1,"page_size":100,"total":19,"list":[{"actual_amount":495,"created_at":"2025-12-22T16:06:40.192+08:00","id":299,"is_consumed":0,"order_no":"O20251222160640","paid_at":"2025-12-22 16:06:46","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495","source_type":3,"status":2,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T16:03:59.551+08:00","id":298,"is_consumed":0,"order_no":"O20251222160359","paid_at":"2025-12-22 16:04:05","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:20","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:55:59.237+08:00","id":297,"is_consumed":0,"order_no":"O20251222155559","paid_at":"2025-12-22 15:56:04","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:21","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:53:35.444+08:00","id":296,"is_consumed":0,"order_no":"O20251222155335","paid_at":"2025-12-22 15:53:45","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:22","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:52:40.539+08:00","id":295,"is_consumed":0,"order_no":"O20251222155240","paid_at":"2025-12-22 15:52:46","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:23","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:49:26.185+08:00","id":294,"is_consumed":0,"order_no":"O20251222154926","paid_at":"2025-12-22 15:49:34","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:24","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:48:26.242+08:00","id":293,"is_consumed":0,"order_no":"O20251222154826","paid_at":"2025-12-22 15:48:32","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:25","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:23:48.548+08:00","id":292,"is_consumed":0,"order_no":"O20251222152348","paid_at":"2025-12-22 15:23:54","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:20","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:20:14.237+08:00","id":291,"is_consumed":0,"order_no":"O20251222152014","paid_at":"2025-12-22 15:20:20","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:56:35.169+08:00","id":290,"is_consumed":0,"order_no":"O20251222145635","paid_at":"2025-12-22 14:56:41","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:43:30.723+08:00","id":289,"is_consumed":0,"order_no":"O20251222144330","paid_at":"2025-12-22 14:43:37","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:41:40.953+08:00","id":288,"is_consumed":0,"order_no":"O20251222144140","paid_at":"2025-12-22 14:41:46","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:39:59.718+08:00","id":287,"is_consumed":0,"order_no":"O20251222143959","paid_at":"2025-12-22 14:40:05","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":3000,"created_at":"2025-12-22T14:37:30.498+08:00","id":286,"is_consumed":0,"order_no":"O20251222143730","paid_at":"2025-12-22 14:37:36","points_amount":0,"points_used":0,"remark":"lottery:activity:52|issue:57|count:1|slots:45:1","source_type":2,"status":4,"total_amount":3000,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:57:40.712+08:00","id":285,"is_consumed":0,"order_no":"O20251222135740","paid_at":"2025-12-22 13:57:47","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:43:07.064+08:00","id":284,"is_consumed":0,"order_no":"O20251222134307","paid_at":"2025-12-22 13:43:15","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:32:34.182+08:00","id":283,"is_consumed":0,"order_no":"O20251222133234","paid_at":"2025-12-22 13:32:40","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:30:35.46+08:00","id":282,"is_consumed":0,"order_no":"O20251222133035","paid_at":"2025-12-22 13:30:42","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:27:07.25+08:00","id":281,"is_consumed":0,"order_no":"O20251222132707","paid_at":"2025-12-22 13:27:13","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.518372792},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.075378834},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:106","sql":"SELECT count(*) FROM `orders`","rows_affected":1,"cost_seconds":0.010225542},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:111","sql":"SELECT * FROM `orders` ORDER BY `orders`.`id` DESC LIMIT 100","rows_affected":19,"cost_seconds":0.02190025},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:117","sql":"SELECT * FROM `system_configs` WHERE `system_configs`.`config_key` = 'points_exchange_per_cent' AND `system_configs`.`deleted_at` IS NULL ORDER BY `system_configs`.`id` LIMIT 1","rows_affected":0,"cost_seconds":0.02107425},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222160640'","rows_affected":0,"cost_seconds":0.021115291},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222160359'","rows_affected":0,"cost_seconds":0.0200375},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155559'","rows_affected":0,"cost_seconds":0.021115916},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155335'","rows_affected":0,"cost_seconds":0.020756708},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155240'","rows_affected":0,"cost_seconds":0.020876959},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222154926'","rows_affected":0,"cost_seconds":0.019925875},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222154826'","rows_affected":0,"cost_seconds":0.021019292},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222152348'","rows_affected":0,"cost_seconds":0.01972125},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222152014'","rows_affected":0,"cost_seconds":0.020947166},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222145635'","rows_affected":0,"cost_seconds":0.020172292},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222144330'","rows_affected":0,"cost_seconds":0.019186},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222144140'","rows_affected":0,"cost_seconds":0.020003},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222143959'","rows_affected":0,"cost_seconds":0.019873917},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222143730'","rows_affected":0,"cost_seconds":0.020060667},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222135740'","rows_affected":0,"cost_seconds":0.021542042},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222134307'","rows_affected":0,"cost_seconds":0.020695917},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222133234'","rows_affected":0,"cost_seconds":0.020910292},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222133035'","rows_affected":0,"cost_seconds":0.020428333},{"time":"2025-12-22 16:08:33","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222132707'","rows_affected":0,"cost_seconds":0.020128791}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.518373208}} +{"level":"info","time":"2025-12-22 16:08:39","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"POST","path":"/api/admin/pay/refunds","http_code":200,"business_code":0,"success":true,"cost_seconds":1.153726458,"trace_id":"2003014805341868032","trace_info":{"trace_id":"2003014805341868032","request":{"ttl":"un-limit","method":"POST","decoded_url":"/api/admin/pay/refunds","header":{"Content-Type":"application/json"},"body":"{\"order_no\":\"O20251222160640\",\"amount\":495,\"reason\":\"\"}"},"response":{"header":{"Access-Control-Allow-Credentials":["true"],"Access-Control-Allow-Origin":["*"],"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003014805341868032"],"Vary":["Origin"]},"body":{"order_no":"O20251222160640","status":"success"},"http_code":200,"http_code_msg":"OK","cost_seconds":1.15372625},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:08:38","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.020510458},{"time":"2025-12-22 16:08:38","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:40","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222160640' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.021328625},{"time":"2025-12-22 16:08:38","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:46","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222160640'","rows_affected":0,"cost_seconds":0.022479625},{"time":"2025-12-22 16:08:39","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:87","sql":"INSERT INTO `payment_refunds` (`order_id`,`order_no`,`refund_no`,`channel`,`status`,`amount_refund`,`reason`,`success_time`,`raw`) VALUES (299,'O20251222160640','RO20251222160640-1766390918','wechat_jsapi','PROCESSING',495,'','2025-12-22 16:08:39.386','{\"refund_id\":\"50300905522025122205319297343\",\"status\":\"PROCESSING\"}')","rows_affected":1,"cost_seconds":0.025598833},{"time":"2025-12-22 16:08:39","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:93","sql":"UPDATE `orders` SET `status`=4,`updated_at`='2025-12-22 16:08:39.422' WHERE `orders`.`id` = 299","rows_affected":1,"cost_seconds":0.027991292},{"time":"2025-12-22 16:08:39","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_refund_admin.go:217","sql":"INSERT INTO `user_points_ledger` (`user_id`,`action`,`points`,`ref_table`,`ref_id`,`remark`) VALUES (12,'refund_amount',4,'payment_refund','50300905522025122205319297343','')","rows_affected":1,"cost_seconds":0.024025375}],"redis":null,"mongos":null,"success":true,"cost_seconds":1.153726458}} +{"level":"info","time":"2025-12-22 16:08:40","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/pay/orders?current=1&size=100","http_code":200,"business_code":0,"success":true,"cost_seconds":0.459962917,"trace_id":"2003014810567970816","trace_info":{"trace_id":"2003014810567970816","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/pay/orders?current=1&size=100","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003014810567970816"],"Vary":["Origin"]},"body":{"page":1,"page_size":100,"total":19,"list":[{"actual_amount":495,"created_at":"2025-12-22T16:06:40.192+08:00","id":299,"is_consumed":0,"order_no":"O20251222160640","paid_at":"2025-12-22 16:06:46","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T16:03:59.551+08:00","id":298,"is_consumed":0,"order_no":"O20251222160359","paid_at":"2025-12-22 16:04:05","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:20","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:55:59.237+08:00","id":297,"is_consumed":0,"order_no":"O20251222155559","paid_at":"2025-12-22 15:56:04","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:21","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:53:35.444+08:00","id":296,"is_consumed":0,"order_no":"O20251222155335","paid_at":"2025-12-22 15:53:45","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:22","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:52:40.539+08:00","id":295,"is_consumed":0,"order_no":"O20251222155240","paid_at":"2025-12-22 15:52:46","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:23","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:49:26.185+08:00","id":294,"is_consumed":0,"order_no":"O20251222154926","paid_at":"2025-12-22 15:49:34","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:24","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:48:26.242+08:00","id":293,"is_consumed":0,"order_no":"O20251222154826","paid_at":"2025-12-22 15:48:32","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:25","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:23:48.548+08:00","id":292,"is_consumed":0,"order_no":"O20251222152348","paid_at":"2025-12-22 15:23:54","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495|itemcard:20","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":495,"created_at":"2025-12-22T15:20:14.237+08:00","id":291,"is_consumed":0,"order_no":"O20251222152014","paid_at":"2025-12-22 15:20:20","points_amount":0,"points_used":0,"remark":"matching_game:issue:71|c:16:495","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:56:35.169+08:00","id":290,"is_consumed":0,"order_no":"O20251222145635","paid_at":"2025-12-22 14:56:41","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:43:30.723+08:00","id":289,"is_consumed":0,"order_no":"O20251222144330","paid_at":"2025-12-22 14:43:37","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:41:40.953+08:00","id":288,"is_consumed":0,"order_no":"O20251222144140","paid_at":"2025-12-22 14:41:46","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T14:39:59.718+08:00","id":287,"is_consumed":0,"order_no":"O20251222143959","paid_at":"2025-12-22 14:40:05","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":3000,"created_at":"2025-12-22T14:37:30.498+08:00","id":286,"is_consumed":0,"order_no":"O20251222143730","paid_at":"2025-12-22 14:37:36","points_amount":0,"points_used":0,"remark":"lottery:activity:52|issue:57|count:1|slots:45:1","source_type":2,"status":4,"total_amount":3000,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:57:40.712+08:00","id":285,"is_consumed":0,"order_no":"O20251222135740","paid_at":"2025-12-22 13:57:47","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:43:07.064+08:00","id":284,"is_consumed":0,"order_no":"O20251222134307","paid_at":"2025-12-22 13:43:15","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:32:34.182+08:00","id":283,"is_consumed":0,"order_no":"O20251222133234","paid_at":"2025-12-22 13:32:40","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:30:35.46+08:00","id":282,"is_consumed":0,"order_no":"O20251222133035","paid_at":"2025-12-22 13:30:42","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12},{"actual_amount":990,"created_at":"2025-12-22T13:27:07.25+08:00","id":281,"is_consumed":0,"order_no":"O20251222132707","paid_at":"2025-12-22 13:27:13","points_amount":0,"points_used":0,"remark":"matching_game:issue:71","source_type":3,"status":4,"total_amount":990,"user_id":12}]},"http_code":200,"http_code_msg":"OK","cost_seconds":0.459962458},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:08:39","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.019837458},{"time":"2025-12-22 16:08:39","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:106","sql":"SELECT count(*) FROM `orders`","rows_affected":1,"cost_seconds":0.009385709},{"time":"2025-12-22 16:08:39","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:111","sql":"SELECT * FROM `orders` ORDER BY `orders`.`id` DESC LIMIT 100","rows_affected":19,"cost_seconds":0.019621916},{"time":"2025-12-22 16:08:39","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:117","sql":"SELECT * FROM `system_configs` WHERE `system_configs`.`config_key` = 'points_exchange_per_cent' AND `system_configs`.`deleted_at` IS NULL ORDER BY `system_configs`.`id` LIMIT 1","rows_affected":0,"cost_seconds":0.020530375},{"time":"2025-12-22 16:08:39","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222160640'","rows_affected":0,"cost_seconds":0.018768458},{"time":"2025-12-22 16:08:39","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222160359'","rows_affected":0,"cost_seconds":0.021148333},{"time":"2025-12-22 16:08:39","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155559'","rows_affected":0,"cost_seconds":0.020048875},{"time":"2025-12-22 16:08:39","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155335'","rows_affected":0,"cost_seconds":0.021012375},{"time":"2025-12-22 16:08:39","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222155240'","rows_affected":0,"cost_seconds":0.021248},{"time":"2025-12-22 16:08:39","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222154926'","rows_affected":0,"cost_seconds":0.020831292},{"time":"2025-12-22 16:08:39","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222154826'","rows_affected":0,"cost_seconds":0.020190709},{"time":"2025-12-22 16:08:39","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222152348'","rows_affected":0,"cost_seconds":0.019974208},{"time":"2025-12-22 16:08:39","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222152014'","rows_affected":0,"cost_seconds":0.02055725},{"time":"2025-12-22 16:08:39","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222145635'","rows_affected":0,"cost_seconds":0.020775417},{"time":"2025-12-22 16:08:40","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222144330'","rows_affected":0,"cost_seconds":0.020455125},{"time":"2025-12-22 16:08:40","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222144140'","rows_affected":0,"cost_seconds":0.021409417},{"time":"2025-12-22 16:08:40","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222143959'","rows_affected":0,"cost_seconds":0.020175958},{"time":"2025-12-22 16:08:40","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222143730'","rows_affected":0,"cost_seconds":0.019825042},{"time":"2025-12-22 16:08:40","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222135740'","rows_affected":0,"cost_seconds":0.019981375},{"time":"2025-12-22 16:08:40","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222134307'","rows_affected":0,"cost_seconds":0.021318042},{"time":"2025-12-22 16:08:40","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222133234'","rows_affected":0,"cost_seconds":0.01966825},{"time":"2025-12-22 16:08:40","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222133035'","rows_affected":0,"cost_seconds":0.021115792},{"time":"2025-12-22 16:08:40","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:127","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222132707'","rows_affected":0,"cost_seconds":0.020062792}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.459962917}} +{"level":"info","time":"2025-12-22 16:11:01","caller":"logger/logger.go:309","msg":"trace-log","domain":"mini-chat[fat]","method":"GET","path":"/api/admin/pay/orders/O20251222160640","http_code":200,"business_code":0,"success":true,"cost_seconds":0.258204584,"trace_id":"2003015402266824704","trace_info":{"trace_id":"2003015402266824704","request":{"ttl":"un-limit","method":"GET","decoded_url":"/api/admin/pay/orders/O20251222160640","header":{"Content-Type":""},"body":""},"response":{"header":{"Content-Type":["application/json; charset=utf-8"],"Trace-Id":["2003015402266824704"],"Vary":["Origin"]},"body":{"order":{"id":299,"created_at":"2025-12-22T16:06:40.192+08:00","updated_at":"2025-12-22T16:08:39.422+08:00","user_id":12,"order_no":"O20251222160640","source_type":3,"total_amount":990,"discount_amount":495,"points_amount":0,"actual_amount":495,"status":4,"pay_preorder_id":123,"paid_at":"2025-12-22T16:06:46+08:00","cancelled_at":null,"user_address_id":0,"is_consumed":0,"points_ledger_id":0,"remark":"matching_game:issue:71|c:16:495"},"items":[{"id":119,"created_at":"2025-12-22T16:06:50.634+08:00","order_id":299,"product_id":265,"title":"MINISO名创优品假面骑士×WASA龙联名系列盲盒单盒随机","quantity":1,"price":0,"total_amount":0,"product_images":"[\"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/17/1765958389919343600.jpg\"]","status":1}],"shipments":[],"ledgers":[],"user":{"id":12,"created_at":"2025-11-14T21:21:44.096+08:00","updated_at":"2025-12-22T13:26:52.838+08:00","deleted_at":null,"nickname":"张三","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD/ZgAoWEEDAAAAAXRSTlMAQObYZgAAAEVJREFUeJzs1cENACAIQ1H2X7pGrdgdPj3V540QrRtJqgwWdpFzTnh4w3GDw6+xQ2DIm54YHDIDuSUDflb7p4HDCgAA//+mPbt9kpdsSwAAAABJRU5ErkJggg==","mobile":"17600666406","openid":"onjlN4wrsGe09SdRrpl4D_FWBh0I","unionid":"oHj_m5uMSHCLFAIyos_giOM00CS0","invite_code":"S93L5LDY","inviter_id":0,"status":1,"douyin_id":"","channel_id":0},"coupons":[{"user_coupon_id":16,"applied_amount":495}],"activity":{"activity_id":0,"activity_name":"","issue_id":0,"issue_number":"","is_winner":0,"level":0,"reward_id":0,"reward_name":"","product_id":0,"count":1,"product_price":0,"price_draw":990,"price_draw_points":990},"payment":{"status":4,"actual_amount":495,"paid_at":"2025-12-22 16:06:46","pay_preorder_id":123,"transaction_id":"4200002901202512222643150169","points_amount":0,"points_used":0,"coupon_applied_amount":495,"total_amount":990},"refunds":[{"refund_no":"RO20251222160640-1766390918","amount":495,"status":"PROCESSING","channel":"wechat_jsapi","reason":"","created_at":"2025-12-22 16:08:39"}],"refundable_amount":0,"computed_items":null,"reward_order":null,"reward_items":null,"reward_shipments":null,"draw_receipts":null},"http_code":200,"http_code_msg":"OK","cost_seconds":0.258204},"third_party_requests":null,"debugs":null,"sqls":[{"time":"2025-12-22 16:11:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/router/interceptor/admin_auth.go:43","sql":"SELECT * FROM `admin` WHERE `admin`.`id` = 2 ORDER BY `admin`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.070416792},{"time":"2025-12-22 16:11:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:251","sql":"SELECT * FROM `orders` WHERE `orders`.`order_no` = 'O20251222160640' ORDER BY `orders`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.01992325},{"time":"2025-12-22 16:11:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:256","sql":"SELECT * FROM `order_items` WHERE `order_items`.`order_id` = 299","rows_affected":1,"cost_seconds":0.020358167},{"time":"2025-12-22 16:11:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:257","sql":"SELECT * FROM `shipping_records` WHERE `shipping_records`.`order_id` = 299","rows_affected":0,"cost_seconds":0.022602958},{"time":"2025-12-22 16:11:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:258","sql":"SELECT * FROM `user_points_ledger` WHERE `user_points_ledger`.`ref_table` = 'orders' AND `user_points_ledger`.`ref_id` = 'O20251222160640'","rows_affected":0,"cost_seconds":0.020373709},{"time":"2025-12-22 16:11:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:259","sql":"SELECT * FROM `order_coupons` WHERE `order_coupons`.`order_id` = 299","rows_affected":1,"cost_seconds":0.021336584},{"time":"2025-12-22 16:11:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:280","sql":"SELECT * FROM `system_configs` WHERE `system_configs`.`config_key` = 'points_exchange_per_cent' AND `system_configs`.`deleted_at` IS NULL ORDER BY `system_configs`.`id` LIMIT 1","rows_affected":0,"cost_seconds":0.018527291},{"time":"2025-12-22 16:11:00","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:291","sql":"SELECT * FROM `users` WHERE `users`.`id` = 12 AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.020424292},{"time":"2025-12-22 16:11:01","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:503","sql":"SELECT * FROM `payment_transactions` WHERE `payment_transactions`.`order_no` = 'O20251222160640' ORDER BY `payment_transactions`.`id` DESC,`payment_transactions`.`id` LIMIT 1","rows_affected":1,"cost_seconds":0.023021208},{"time":"2025-12-22 16:11:01","stack":"/Users/win/aicode/bindbox/bindbox_game/internal/api/admin/pay_orders_admin.go:517","sql":"SELECT * FROM `payment_refunds` WHERE `payment_refunds`.`order_no` = 'O20251222160640' ORDER BY `payment_refunds`.`id` DESC","rows_affected":1,"cost_seconds":0.01916975}],"redis":null,"mongos":null,"success":true,"cost_seconds":0.258204584}} +{"level":"info","time":"2025-12-22 16:29:34","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"fatal","time":"2025-12-22 16:29:34","caller":"logger/logger.go:333","msg":"http server startup err","domain":"mini-chat[fat]","error":"listen tcp :9991: bind: address already in use"} +{"level":"info","time":"2025-12-22 16:34:23","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-22 16:39:01","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-22 16:53:58","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"fatal","time":"2025-12-22 16:53:58","caller":"logger/logger.go:333","msg":"http server startup err","domain":"mini-chat[fat]","error":"listen tcp :9991: bind: address already in use"} +{"level":"info","time":"2025-12-22 16:55:01","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-22 17:08:22","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-22 17:24:33","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-22 18:58:51","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-22 19:18:05","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"fatal","time":"2025-12-22 19:18:05","caller":"logger/logger.go:333","msg":"http server startup err","domain":"mini-chat[fat]","error":"listen tcp :9991: bind: address already in use"} +{"level":"info","time":"2025-12-23 10:22:04","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 10:23:31","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 10:24:02","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 10:24:36","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 10:27:06","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 11:15:16","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 11:24:42","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 11:33:05","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 12:15:22","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 12:28:48","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 12:33:05","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 12:53:14","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 13:08:37","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 13:19:36","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 13:20:55","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 13:23:05","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 13:34:50","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 13:57:39","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 13:57:39","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"} +{"level":"info","time":"2025-12-23 13:57:39","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4} +{"level":"info","time":"2025-12-23 13:57:39","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3} +{"level":"info","time":"2025-12-23 13:57:39","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2} +{"level":"info","time":"2025-12-23 13:57:39","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1} +{"level":"info","time":"2025-12-23 13:57:39","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0} +{"level":"info","time":"2025-12-23 14:10:52","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 14:10:52","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"} +{"level":"info","time":"2025-12-23 14:10:52","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4} +{"level":"info","time":"2025-12-23 14:10:52","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0} +{"level":"info","time":"2025-12-23 14:10:52","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1} +{"level":"info","time":"2025-12-23 14:10:52","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2} +{"level":"info","time":"2025-12-23 14:10:52","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3} +{"level":"info","time":"2025-12-23 14:14:42","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 14:14:42","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"} +{"level":"info","time":"2025-12-23 14:14:42","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0} +{"level":"info","time":"2025-12-23 14:14:42","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4} +{"level":"info","time":"2025-12-23 14:14:42","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1} +{"level":"info","time":"2025-12-23 14:14:42","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2} +{"level":"info","time":"2025-12-23 14:14:42","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3} +{"level":"info","time":"2025-12-23 15:00:02","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 15:00:02","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"} +{"level":"info","time":"2025-12-23 15:00:02","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3} +{"level":"info","time":"2025-12-23 15:00:02","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2} +{"level":"info","time":"2025-12-23 15:00:02","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4} +{"level":"info","time":"2025-12-23 15:00:02","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0} +{"level":"info","time":"2025-12-23 15:00:02","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1} +{"level":"info","time":"2025-12-23 17:33:27","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 17:33:27","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"} +{"level":"info","time":"2025-12-23 17:33:27","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4} +{"level":"info","time":"2025-12-23 17:33:27","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0} +{"level":"info","time":"2025-12-23 17:33:27","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3} +{"level":"info","time":"2025-12-23 17:33:27","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2} +{"level":"info","time":"2025-12-23 17:33:27","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1} +{"level":"info","time":"2025-12-23 17:52:02","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 17:52:02","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"} +{"level":"info","time":"2025-12-23 17:52:02","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4} +{"level":"info","time":"2025-12-23 17:52:02","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1} +{"level":"info","time":"2025-12-23 17:52:02","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3} +{"level":"info","time":"2025-12-23 17:52:02","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0} +{"level":"info","time":"2025-12-23 17:52:02","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2} +{"level":"info","time":"2025-12-23 20:10:38","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"} +{"level":"info","time":"2025-12-23 20:10:38","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"} +{"level":"info","time":"2025-12-23 20:10:39","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1} +{"level":"info","time":"2025-12-23 20:10:39","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2} +{"level":"info","time":"2025-12-23 20:10:39","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3} +{"level":"info","time":"2025-12-23 20:10:39","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0} +{"level":"info","time":"2025-12-23 20:10:39","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4} diff --git a/main.go b/main.go index 01c2862..e3dbcba 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "bindbox-game/configs" "bindbox-game/internal/pkg/env" "bindbox-game/internal/pkg/logger" + "bindbox-game/internal/pkg/redis" "bindbox-game/internal/pkg/shutdown" "bindbox-game/internal/pkg/timeutil" "bindbox-game/internal/repository/mysql" @@ -130,8 +131,13 @@ func main() { _ = customLogger.Sync() }() + // 初始化 Redis + if err := redis.Init(context.Background(), customLogger); err != nil { + customLogger.Warn("Redis init failed, some features may be disabled", zap.Error(err)) + } + // 初始化 HTTP 服务 - mux, err := router.NewHTTPMux(customLogger, dbRepo) + mux, cleanup, err := router.NewHTTPMux(customLogger, dbRepo) if err != nil { panic(err) } @@ -152,6 +158,11 @@ func main() { // 优雅关闭 shutdown.Close( func() { + // 清理资源 (Worker) + if cleanup != nil { + cleanup() + } + // 关闭 http server if err := server.Shutdown(context.TODO()); err != nil { customLogger.Error("server shutdown err", zap.Error(err)) diff --git a/migrations/20251222_add_order_coupon_and_card.sql b/migrations/20251222_add_order_coupon_and_card.sql new file mode 100644 index 0000000..9526a21 --- /dev/null +++ b/migrations/20251222_add_order_coupon_and_card.sql @@ -0,0 +1,6 @@ +-- 添加订单优惠券与道具卡字段 +-- 2025-12-22 + +ALTER TABLE `orders` +ADD COLUMN `coupon_id` BIGINT NULL COMMENT '使用的优惠券ID' AFTER `points_ledger_id`, +ADD COLUMN `item_card_id` BIGINT NULL COMMENT '使用的道具卡ID' AFTER `coupon_id`; diff --git a/migrations/20251223_add_user_invites_effective_columns.sql b/migrations/20251223_add_user_invites_effective_columns.sql new file mode 100644 index 0000000..035fc3b --- /dev/null +++ b/migrations/20251223_add_user_invites_effective_columns.sql @@ -0,0 +1,4 @@ +ALTER TABLE user_invites ADD COLUMN is_effective TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否为有效邀请'; +ALTER TABLE user_invites ADD COLUMN accumulated_amount BIGINT NOT NULL DEFAULT 0 COMMENT '被邀请人累计消费金额(分)'; +ALTER TABLE task_center_task_tiers ADD COLUMN extra_params JSON COMMENT '额外参数配置(如消费门槛)'; +ALTER TABLE task_center_user_progress ADD COLUMN effective_invite_count BIGINT NOT NULL DEFAULT 0 COMMENT '有效邀请人数'; diff --git a/migrations/20251223_fix_invite_count_comment.sql b/migrations/20251223_fix_invite_count_comment.sql new file mode 100644 index 0000000..b694022 --- /dev/null +++ b/migrations/20251223_fix_invite_count_comment.sql @@ -0,0 +1 @@ +ALTER TABLE task_center_user_progress MODIFY COLUMN invite_count BIGINT NOT NULL DEFAULT 0 COMMENT '累计邀请数(注册)'; diff --git a/说明文档.md b/说明文档.md new file mode 100644 index 0000000..3aa19ac --- /dev/null +++ b/说明文档.md @@ -0,0 +1,12 @@ +# 项目说明文档 + +## 一、项目规划 +(待补充) + +## 二、实施方案 +(待补充) + +## 三、进度记录 +- **2025-12-23** [修复] + - 修复微信虚拟发货接口 `items_desc` 字段因字节截断导致的 UTF-8 编码错误 (errcode=47007)。 + - 修复开奖通知接口 `phrase3` 字段内容过长导致的参数无效错误 (errcode=47003),调整为固定文案“恭喜中奖”,并将赏品名称移至 `thing4` 展示。