feat: minesweeper dynamic config and granular rewards
Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 48s
Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 48s
This commit is contained in:
parent
425e64daa5
commit
c9a83a232a
@ -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.9 .
|
||||
docker push zfc931912343/bindbox-game:v1.9
|
||||
docker build -t zfc931912343/bindbox-game:v1.10 .
|
||||
docker push zfc931912343/bindbox-game:v1.10
|
||||
|
||||
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
|
||||
docker pull zfc931912343/bindbox-game:v1.10 &&docker rm -f bindbox-game && docker run -d --name bindbox-game -p 9991:9991 zfc931912343/bindbox-game:v1.10
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
}
|
||||
})()
|
||||
</script>
|
||||
<script type="module" crossorigin src="/assets/index-D9UEFhei.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-BtMJajWI.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BZQg_MtJ.css">
|
||||
</head>
|
||||
|
||||
|
||||
@ -34,6 +34,6 @@ eg :
|
||||
|
||||
```shell
|
||||
# 根目录下执行
|
||||
go run cmd/gormgen/main.go -dsn "root:api2api..@tcp(sh-cynosdbmysql-grp-88th45wy.sql.tencentcdb.com:28555)/bindbox_game?charset=utf8mb4&parseTime=True&loc=Local" -tables "admin,log_operation,log_request,activities,activity_categories,activity_draw_logs,activity_issues,activity_reward_settings,system_coupons,user_coupons,user_inventory,user_inventory_transfers,user_points,user_points_ledger,users,user_addresses,menu_actions,menus,role_actions,role_menus,role_users,roles,order_items,orders,products,shipping_records,product_categories,user_invites,system_item_cards,user_item_cards,activity_draw_effects,banner,activity_draw_receipts,system_titles,system_title_effects,user_titles,user_title_effect_claims,payment_preorders,payment_transactions,payment_refunds,payment_notify_events,payment_bills,payment_bill_diff,ops_shipping_stats,system_configs,issue_position_claims,task_center_tasks,task_center_task_tiers,task_center_task_rewards,order_coupons,matching_card_types,channels"
|
||||
go run cmd/gormgen/main.go -dsn "root:api2api..@tcp(sh-cynosdbmysql-grp-88th45wy.sql.tencentcdb.com:28555)/bindbox_game?charset=utf8mb4&parseTime=True&loc=Local" -tables "admin,log_operation,log_request,activities,activity_categories,activity_draw_logs,activity_issues,activity_reward_settings,system_coupons,user_coupons,user_inventory,user_inventory_transfers,user_points,user_points_ledger,users,user_addresses,menu_actions,menus,role_actions,role_menus,role_users,roles,order_items,orders,products,shipping_records,product_categories,user_invites,system_item_cards,user_item_cards,activity_draw_effects,banner,activity_draw_receipts,system_titles,system_title_effects,user_titles,user_title_effect_claims,payment_preorders,payment_transactions,payment_refunds,payment_notify_events,payment_bills,payment_bill_diff,ops_shipping_stats,system_configs,issue_position_claims,task_center_tasks,task_center_task_tiers,task_center_task_rewards,order_coupons,matching_card_types,channels,user_game_tickets,game_ticket_logs"
|
||||
|
||||
```
|
||||
|
||||
380
internal/api/game/handler.go
Normal file
380
internal/api/game/handler.go
Normal file
@ -0,0 +1,380 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"bindbox-game/internal/code"
|
||||
"bindbox-game/internal/pkg/core"
|
||||
"bindbox-game/internal/pkg/logger"
|
||||
"bindbox-game/internal/pkg/validation"
|
||||
"bindbox-game/internal/repository/mysql"
|
||||
"bindbox-game/internal/repository/mysql/dao"
|
||||
"bindbox-game/internal/service/game"
|
||||
usersvc "bindbox-game/internal/service/user"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
logger logger.CustomLogger
|
||||
db mysql.Repo
|
||||
redis *redis.Client
|
||||
ticketSvc game.TicketService
|
||||
userSvc usersvc.Service
|
||||
readDB *dao.Query
|
||||
}
|
||||
|
||||
func New(l logger.CustomLogger, db mysql.Repo, rdb *redis.Client, userSvc usersvc.Service) *handler {
|
||||
return &handler{
|
||||
logger: l,
|
||||
db: db,
|
||||
redis: rdb,
|
||||
ticketSvc: game.NewTicketService(l, db),
|
||||
userSvc: userSvc,
|
||||
readDB: dao.Use(db.GetDbR()),
|
||||
}
|
||||
}
|
||||
|
||||
// ========== Admin API ==========
|
||||
|
||||
type grantTicketRequest struct {
|
||||
GameCode string `json:"game_code" binding:"required"`
|
||||
Amount int `json:"amount" binding:"required,min=1"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
// GrantUserTicket Admin为用户发放游戏资格
|
||||
// @Summary 发放游戏资格
|
||||
// @Tags 管理端.游戏
|
||||
// @Param user_id path int true "用户ID"
|
||||
// @Param RequestBody body grantTicketRequest true "请求参数"
|
||||
// @Success 200 {object} map[string]any
|
||||
// @Router /api/admin/users/{user_id}/game_tickets [post]
|
||||
func (h *handler) GrantUserTicket() core.HandlerFunc {
|
||||
return func(ctx core.Context) {
|
||||
userID, err := strconv.ParseInt(ctx.Param("user_id"), 10, 64)
|
||||
if err != nil || userID <= 0 {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "invalid user_id"))
|
||||
return
|
||||
}
|
||||
|
||||
req := new(grantTicketRequest)
|
||||
if err := ctx.ShouldBindJSON(req); err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
|
||||
return
|
||||
}
|
||||
|
||||
err = h.ticketSvc.GrantTicket(ctx.RequestContext(), userID, req.GameCode, req.Amount, "admin", 0, req.Remark)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Payload(map[string]any{"success": true})
|
||||
}
|
||||
}
|
||||
|
||||
// ListUserTickets Admin查询用户游戏资格日志
|
||||
// @Summary 查询用户游戏资格日志
|
||||
// @Tags 管理端.游戏
|
||||
// @Param user_id path int true "用户ID"
|
||||
// @Param page query int false "页码"
|
||||
// @Param page_size query int false "每页数量"
|
||||
// @Success 200 {object} map[string]any
|
||||
// @Router /api/admin/users/{user_id}/game_tickets [get]
|
||||
func (h *handler) ListUserTickets() core.HandlerFunc {
|
||||
return func(ctx core.Context) {
|
||||
userID, err := strconv.ParseInt(ctx.Param("user_id"), 10, 64)
|
||||
if err != nil || userID <= 0 {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "invalid user_id"))
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
}
|
||||
_ = ctx.ShouldBindQuery(&req)
|
||||
|
||||
if req.Page <= 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.PageSize <= 0 {
|
||||
req.PageSize = 20
|
||||
}
|
||||
|
||||
logs, total, err := h.ticketSvc.GetTicketLogs(ctx.RequestContext(), userID, req.Page, req.PageSize)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Payload(map[string]any{
|
||||
"list": logs,
|
||||
"total": total,
|
||||
"page": req.Page,
|
||||
"page_size": req.PageSize,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ========== App API ==========
|
||||
|
||||
// GetMyTickets App获取我的游戏资格
|
||||
// @Summary 获取我的游戏资格
|
||||
// @Tags APP端.游戏
|
||||
// @Param user_id path int true "用户ID"
|
||||
// @Success 200 {object} map[string]int
|
||||
// @Router /api/app/users/{user_id}/game_tickets [get]
|
||||
func (h *handler) GetMyTickets() core.HandlerFunc {
|
||||
return func(ctx core.Context) {
|
||||
userID := int64(ctx.SessionUserInfo().Id)
|
||||
|
||||
tickets, err := h.ticketSvc.GetUserTickets(ctx.RequestContext(), userID)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Payload(tickets)
|
||||
}
|
||||
}
|
||||
|
||||
type enterGameRequest struct {
|
||||
GameCode string `json:"game_code" binding:"required"`
|
||||
}
|
||||
|
||||
type enterGameResponse struct {
|
||||
TicketToken string `json:"ticket_token"`
|
||||
NakamaServer string `json:"nakama_server"`
|
||||
NakamaKey string `json:"nakama_key"`
|
||||
RemainingTimes int `json:"remaining_times"`
|
||||
}
|
||||
|
||||
// EnterGame App进入游戏(消耗资格)
|
||||
// @Summary 进入游戏
|
||||
// @Tags APP端.游戏
|
||||
// @Param RequestBody body enterGameRequest true "请求参数"
|
||||
// @Success 200 {object} enterGameResponse
|
||||
// @Router /api/app/games/enter [post]
|
||||
func (h *handler) EnterGame() core.HandlerFunc {
|
||||
return func(ctx core.Context) {
|
||||
userID := int64(ctx.SessionUserInfo().Id)
|
||||
|
||||
req := new(enterGameRequest)
|
||||
if err := ctx.ShouldBindJSON(req); err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
|
||||
return
|
||||
}
|
||||
|
||||
// 扣减资格
|
||||
if err := h.ticketSvc.UseTicket(ctx.RequestContext(), userID, req.GameCode); err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, 180001, "游戏次数不足"))
|
||||
return
|
||||
}
|
||||
|
||||
// 生成临时token并存入Redis
|
||||
ticketToken := generateTicketToken(userID)
|
||||
h.redis.Set(ctx.RequestContext(), "game:ticket:"+ticketToken, userID, 30*60*1000000000) // 30分钟
|
||||
|
||||
// 查询剩余次数
|
||||
ticket, _ := h.ticketSvc.GetUserTicketByGame(ctx.RequestContext(), userID, req.GameCode)
|
||||
remaining := 0
|
||||
if ticket != nil {
|
||||
remaining = int(ticket.Available)
|
||||
}
|
||||
|
||||
// TODO: 从配置读取Nakama服务器信息
|
||||
ctx.Payload(&enterGameResponse{
|
||||
TicketToken: ticketToken,
|
||||
NakamaServer: "ws://localhost:7350",
|
||||
NakamaKey: "defaultkey",
|
||||
RemainingTimes: remaining,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ========== Internal API (Nakama调用) ==========
|
||||
|
||||
type verifyRequest struct {
|
||||
UserID string `json:"user_id"`
|
||||
Ticket string `json:"ticket"`
|
||||
}
|
||||
|
||||
type verifyResponse struct {
|
||||
Valid bool `json:"valid"`
|
||||
UserID string `json:"user_id"`
|
||||
GameConfig map[string]any `json:"game_config,omitempty"`
|
||||
}
|
||||
|
||||
// VerifyTicket Internal验证游戏票据
|
||||
// @Summary 验证票据
|
||||
// @Tags Internal.游戏
|
||||
// @Param RequestBody body verifyRequest true "请求参数"
|
||||
// @Success 200 {object} verifyResponse
|
||||
// @Router /internal/game/verify [post]
|
||||
func (h *handler) VerifyTicket() core.HandlerFunc {
|
||||
return func(ctx core.Context) {
|
||||
req := new(verifyRequest)
|
||||
if err := ctx.ShouldBindJSON(req); err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
|
||||
return
|
||||
}
|
||||
|
||||
// 从Redis验证token
|
||||
storedUserID, err := h.redis.Get(ctx.RequestContext(), "game:ticket:"+req.Ticket).Result()
|
||||
if err != nil || storedUserID != req.UserID {
|
||||
ctx.Payload(&verifyResponse{Valid: false})
|
||||
return
|
||||
}
|
||||
|
||||
// 获取游戏配置
|
||||
gameConfig := make(map[string]any)
|
||||
configKey := "game_minesweeper_config"
|
||||
conf, err := h.readDB.SystemConfigs.WithContext(ctx.RequestContext()).Where(h.readDB.SystemConfigs.ConfigKey.Eq(configKey)).First()
|
||||
if err == nil && conf != nil {
|
||||
json.Unmarshal([]byte(conf.ConfigValue), &gameConfig)
|
||||
}
|
||||
|
||||
ctx.Payload(&verifyResponse{Valid: true, UserID: req.UserID, GameConfig: gameConfig})
|
||||
}
|
||||
}
|
||||
|
||||
type settleRequest struct {
|
||||
UserID string `json:"user_id"`
|
||||
Ticket string `json:"ticket"`
|
||||
MatchID string `json:"match_id"`
|
||||
Win bool `json:"win"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type settleResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Reward string `json:"reward,omitempty"`
|
||||
}
|
||||
|
||||
// SettleGame Internal游戏结算
|
||||
// @Summary 游戏结算
|
||||
// @Tags Internal.游戏
|
||||
// @Param RequestBody body settleRequest true "请求参数"
|
||||
// @Success 200 {object} settleResponse
|
||||
// @Router /internal/game/settle [post]
|
||||
func (h *handler) SettleGame() core.HandlerFunc {
|
||||
return func(ctx core.Context) {
|
||||
req := new(settleRequest)
|
||||
if err := ctx.ShouldBindJSON(req); err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
|
||||
return
|
||||
}
|
||||
|
||||
// 验证token
|
||||
storedUserID, err := h.redis.Get(ctx.RequestContext(), "game:ticket:"+req.Ticket).Result()
|
||||
if err != nil || storedUserID != req.UserID {
|
||||
ctx.Payload(&settleResponse{Success: false})
|
||||
return
|
||||
}
|
||||
|
||||
// 删除token防止重复使用
|
||||
h.redis.Del(ctx.RequestContext(), "game:ticket:"+req.Ticket)
|
||||
|
||||
// 奖品发放逻辑
|
||||
var rewardMsg string
|
||||
var msConfig struct {
|
||||
WinnerRewardPoints int64 `json:"winner_reward_points"`
|
||||
ParticipationRewardPoints int64 `json:"participation_reward_points"`
|
||||
WinnerRewardProductID int64 `json:"winner_reward_product_id"`
|
||||
ParticipationRewardProductID int64 `json:"participation_reward_product_id"`
|
||||
}
|
||||
|
||||
// 1. 读取配置
|
||||
configKey := "game_minesweeper_config"
|
||||
conf, err := h.readDB.SystemConfigs.WithContext(ctx.RequestContext()).Where(h.readDB.SystemConfigs.ConfigKey.Eq(configKey)).First()
|
||||
if err == nil && conf != nil {
|
||||
json.Unmarshal([]byte(conf.ConfigValue), &msConfig)
|
||||
}
|
||||
|
||||
uid, _ := strconv.ParseInt(req.UserID, 10, 64)
|
||||
|
||||
// 2. 确定奖励内容
|
||||
var targetProductID int64
|
||||
var targetPoints int64
|
||||
|
||||
if req.Win {
|
||||
targetProductID = msConfig.WinnerRewardProductID
|
||||
targetPoints = msConfig.WinnerRewardPoints
|
||||
if targetPoints == 0 && targetProductID == 0 {
|
||||
targetPoints = 100 // 兜底
|
||||
}
|
||||
} else {
|
||||
targetProductID = msConfig.ParticipationRewardProductID
|
||||
targetPoints = msConfig.ParticipationRewardPoints
|
||||
if targetPoints == 0 && targetProductID == 0 {
|
||||
targetPoints = 10 // 兜底
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 发放奖励
|
||||
if targetProductID > 0 {
|
||||
res, err := h.userSvc.GrantReward(ctx.RequestContext(), uid, usersvc.GrantRewardRequest{
|
||||
ProductID: targetProductID,
|
||||
Quantity: 1,
|
||||
Remark: "扫雷游戏奖励",
|
||||
})
|
||||
if err != nil || !res.Success {
|
||||
h.logger.Error("Failed to grant game product reward", zap.Error(err), zap.String("msg", res.Message))
|
||||
rewardMsg = "奖励发放失败"
|
||||
} else {
|
||||
rewardMsg = "获得奖品"
|
||||
}
|
||||
} else if targetPoints > 0 {
|
||||
err := h.userSvc.AddPointsWithAction(ctx.RequestContext(), uid, targetPoints, "game_reward", "扫雷游戏奖励", "minesweeper_settle", nil, nil)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to grant game points", zap.Error(err))
|
||||
}
|
||||
rewardMsg = strconv.FormatInt(targetPoints, 10) + "积分"
|
||||
}
|
||||
|
||||
ctx.Payload(&settleResponse{Success: true, Reward: rewardMsg})
|
||||
}
|
||||
}
|
||||
|
||||
// GetMinesweeperConfig Internal获取扫雷配置
|
||||
// @Summary 获取扫雷配置
|
||||
// @Tags Internal.游戏
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /internal/game/minesweeper/config [get]
|
||||
func (h *handler) GetMinesweeperConfig() core.HandlerFunc {
|
||||
return func(ctx core.Context) {
|
||||
configKey := "game_minesweeper_config"
|
||||
conf, err := h.readDB.SystemConfigs.WithContext(ctx.RequestContext()).Where(h.readDB.SystemConfigs.ConfigKey.Eq(configKey)).First()
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, "获取配置失败"))
|
||||
return
|
||||
}
|
||||
|
||||
var gameConfig map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(conf.ConfigValue), &gameConfig); err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, "解析配置失败"))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Payload(gameConfig)
|
||||
}
|
||||
}
|
||||
|
||||
// ========== Helpers ==========
|
||||
|
||||
func generateTicketToken(userID int64) string {
|
||||
return "GT" + randomString(16)
|
||||
}
|
||||
|
||||
func randomString(n int) string {
|
||||
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
b := make([]byte, n)
|
||||
for i := range b {
|
||||
b[i] = letters[i%len(letters)]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
@ -8,36 +8,44 @@ import (
|
||||
)
|
||||
|
||||
type cancelShippingRequest struct {
|
||||
InventoryID int64 `json:"inventory_id"`
|
||||
InventoryID int64 `json:"inventory_id"` // 单个资产ID(与batch_no二选一)
|
||||
BatchNo string `json:"batch_no"` // 批次号(与inventory_id二选一,取消整批)
|
||||
}
|
||||
|
||||
type cancelShippingResponse struct{}
|
||||
type cancelShippingResponse struct {
|
||||
CancelledCount int64 `json:"cancelled_count"` // 成功取消的数量
|
||||
}
|
||||
|
||||
// CancelShipping 取消发货申请
|
||||
// @Summary 取消发货申请
|
||||
// @Description 取消已提交但未发货的申请;恢复库存状态
|
||||
// @Description 取消已提交但未发货的申请;恢复库存状态。支持按单个资产ID取消或按批次号批量取消
|
||||
// @Tags APP端.用户
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security LoginVerifyToken
|
||||
// @Param user_id path integer true "用户ID"
|
||||
// @Param RequestBody body cancelShippingRequest 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 {
|
||||
if err := ctx.ShouldBindJSON(req); err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "参数错误"))
|
||||
return
|
||||
}
|
||||
// 必须提供 inventory_id 或 batch_no 其中之一
|
||||
if req.InventoryID == 0 && req.BatchNo == "" {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "请提供inventory_id或batch_no"))
|
||||
return
|
||||
}
|
||||
userID := int64(ctx.SessionUserInfo().Id)
|
||||
err := h.user.CancelShipping(ctx.RequestContext(), userID, req.InventoryID)
|
||||
count, err := h.user.CancelShipping(ctx.RequestContext(), userID, req.InventoryID, req.BatchNo)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, 10024, err.Error()))
|
||||
return
|
||||
}
|
||||
ctx.Payload(nil)
|
||||
ctx.Payload(&cancelShippingResponse{CancelledCount: count})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,22 +1,23 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"bindbox-game/internal/code"
|
||||
"bindbox-game/internal/pkg/core"
|
||||
"bindbox-game/internal/pkg/validation"
|
||||
"net/http"
|
||||
"bindbox-game/internal/code"
|
||||
"bindbox-game/internal/pkg/core"
|
||||
"bindbox-game/internal/pkg/validation"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type requestShippingBatchRequest struct {
|
||||
InventoryIDs []int64 `json:"inventory_ids"`
|
||||
AddressID *int64 `json:"address_id"`
|
||||
InventoryIDs []int64 `json:"inventory_ids"`
|
||||
AddressID *int64 `json:"address_id"`
|
||||
}
|
||||
|
||||
type requestShippingBatchResponse struct {
|
||||
AddressID int64 `json:"address_id"`
|
||||
SuccessIDs []int64 `json:"success_ids"`
|
||||
Skipped []map[string]any `json:"skipped"`
|
||||
Failed []map[string]any `json:"failed"`
|
||||
AddressID int64 `json:"address_id"`
|
||||
BatchNo string `json:"batch_no"`
|
||||
SuccessIDs []int64 `json:"success_ids"`
|
||||
Skipped []map[string]any `json:"skipped"`
|
||||
Failed []map[string]any `json:"failed"`
|
||||
}
|
||||
|
||||
// RequestShippingBatch 批量申请发货(使用默认地址或指定地址)
|
||||
@ -32,32 +33,32 @@ type requestShippingBatchResponse struct {
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/app/users/{user_id}/inventory/request-shipping-batch [post]
|
||||
func (h *handler) RequestShippingBatch() core.HandlerFunc {
|
||||
return func(ctx core.Context) {
|
||||
req := new(requestShippingBatchRequest)
|
||||
rsp := new(requestShippingBatchResponse)
|
||||
if err := ctx.ShouldBindJSON(req); err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
|
||||
return
|
||||
}
|
||||
if len(req.InventoryIDs) == 0 || len(req.InventoryIDs) > 100 {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "invalid inventory_ids"))
|
||||
return
|
||||
}
|
||||
userID := int64(ctx.SessionUserInfo().Id)
|
||||
addrID, success, skipped, failed, err := h.user.RequestShippings(ctx.RequestContext(), userID, req.InventoryIDs, req.AddressID)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, 10023, err.Error()))
|
||||
return
|
||||
}
|
||||
rsp.AddressID = addrID
|
||||
rsp.SuccessIDs = success
|
||||
for _, s := range skipped {
|
||||
rsp.Skipped = append(rsp.Skipped, map[string]any{"id": s.ID, "reason": s.Reason})
|
||||
}
|
||||
for _, f := range failed {
|
||||
rsp.Failed = append(rsp.Failed, map[string]any{"id": f.ID, "reason": f.Reason})
|
||||
}
|
||||
ctx.Payload(rsp)
|
||||
}
|
||||
return func(ctx core.Context) {
|
||||
req := new(requestShippingBatchRequest)
|
||||
rsp := new(requestShippingBatchResponse)
|
||||
if err := ctx.ShouldBindJSON(req); err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
|
||||
return
|
||||
}
|
||||
if len(req.InventoryIDs) == 0 || len(req.InventoryIDs) > 100 {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "invalid inventory_ids"))
|
||||
return
|
||||
}
|
||||
userID := int64(ctx.SessionUserInfo().Id)
|
||||
addrID, batchNo, success, skipped, failed, err := h.user.RequestShippings(ctx.RequestContext(), userID, req.InventoryIDs, req.AddressID)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, 10023, err.Error()))
|
||||
return
|
||||
}
|
||||
rsp.AddressID = addrID
|
||||
rsp.BatchNo = batchNo
|
||||
rsp.SuccessIDs = success
|
||||
for _, s := range skipped {
|
||||
rsp.Skipped = append(rsp.Skipped, map[string]any{"id": s.ID, "reason": s.Reason})
|
||||
}
|
||||
for _, f := range failed {
|
||||
rsp.Failed = append(rsp.Failed, map[string]any{"id": f.ID, "reason": f.Reason})
|
||||
}
|
||||
ctx.Payload(rsp)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
356
internal/repository/mysql/dao/game_ticket_logs.gen.go
Normal file
356
internal/repository/mysql/dao/game_ticket_logs.gen.go
Normal file
@ -0,0 +1,356 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
"gorm.io/gorm/schema"
|
||||
|
||||
"gorm.io/gen"
|
||||
"gorm.io/gen/field"
|
||||
|
||||
"gorm.io/plugin/dbresolver"
|
||||
|
||||
"bindbox-game/internal/repository/mysql/model"
|
||||
)
|
||||
|
||||
func newGameTicketLogs(db *gorm.DB, opts ...gen.DOOption) gameTicketLogs {
|
||||
_gameTicketLogs := gameTicketLogs{}
|
||||
|
||||
_gameTicketLogs.gameTicketLogsDo.UseDB(db, opts...)
|
||||
_gameTicketLogs.gameTicketLogsDo.UseModel(&model.GameTicketLogs{})
|
||||
|
||||
tableName := _gameTicketLogs.gameTicketLogsDo.TableName()
|
||||
_gameTicketLogs.ALL = field.NewAsterisk(tableName)
|
||||
_gameTicketLogs.ID = field.NewInt64(tableName, "id")
|
||||
_gameTicketLogs.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_gameTicketLogs.UserID = field.NewInt64(tableName, "user_id")
|
||||
_gameTicketLogs.GameCode = field.NewString(tableName, "game_code")
|
||||
_gameTicketLogs.ChangeType = field.NewInt32(tableName, "change_type")
|
||||
_gameTicketLogs.Amount = field.NewInt32(tableName, "amount")
|
||||
_gameTicketLogs.Balance = field.NewInt32(tableName, "balance")
|
||||
_gameTicketLogs.Source = field.NewString(tableName, "source")
|
||||
_gameTicketLogs.SourceID = field.NewInt64(tableName, "source_id")
|
||||
_gameTicketLogs.Remark = field.NewString(tableName, "remark")
|
||||
|
||||
_gameTicketLogs.fillFieldMap()
|
||||
|
||||
return _gameTicketLogs
|
||||
}
|
||||
|
||||
// gameTicketLogs 游戏资格变动日志
|
||||
type gameTicketLogs struct {
|
||||
gameTicketLogsDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Int64
|
||||
CreatedAt field.Time
|
||||
UserID field.Int64 // 用户ID
|
||||
GameCode field.String // 游戏代码
|
||||
ChangeType field.Int32 // 1=获得 2=使用
|
||||
Amount field.Int32 // 变动数量
|
||||
Balance field.Int32 // 变动后余额
|
||||
Source field.String // 来源: order/task/admin
|
||||
SourceID field.Int64 // 来源ID
|
||||
Remark field.String // 备注
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
func (g gameTicketLogs) Table(newTableName string) *gameTicketLogs {
|
||||
g.gameTicketLogsDo.UseTable(newTableName)
|
||||
return g.updateTableName(newTableName)
|
||||
}
|
||||
|
||||
func (g gameTicketLogs) As(alias string) *gameTicketLogs {
|
||||
g.gameTicketLogsDo.DO = *(g.gameTicketLogsDo.As(alias).(*gen.DO))
|
||||
return g.updateTableName(alias)
|
||||
}
|
||||
|
||||
func (g *gameTicketLogs) updateTableName(table string) *gameTicketLogs {
|
||||
g.ALL = field.NewAsterisk(table)
|
||||
g.ID = field.NewInt64(table, "id")
|
||||
g.CreatedAt = field.NewTime(table, "created_at")
|
||||
g.UserID = field.NewInt64(table, "user_id")
|
||||
g.GameCode = field.NewString(table, "game_code")
|
||||
g.ChangeType = field.NewInt32(table, "change_type")
|
||||
g.Amount = field.NewInt32(table, "amount")
|
||||
g.Balance = field.NewInt32(table, "balance")
|
||||
g.Source = field.NewString(table, "source")
|
||||
g.SourceID = field.NewInt64(table, "source_id")
|
||||
g.Remark = field.NewString(table, "remark")
|
||||
|
||||
g.fillFieldMap()
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *gameTicketLogs) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
_f, ok := g.fieldMap[fieldName]
|
||||
if !ok || _f == nil {
|
||||
return nil, false
|
||||
}
|
||||
_oe, ok := _f.(field.OrderExpr)
|
||||
return _oe, ok
|
||||
}
|
||||
|
||||
func (g *gameTicketLogs) fillFieldMap() {
|
||||
g.fieldMap = make(map[string]field.Expr, 10)
|
||||
g.fieldMap["id"] = g.ID
|
||||
g.fieldMap["created_at"] = g.CreatedAt
|
||||
g.fieldMap["user_id"] = g.UserID
|
||||
g.fieldMap["game_code"] = g.GameCode
|
||||
g.fieldMap["change_type"] = g.ChangeType
|
||||
g.fieldMap["amount"] = g.Amount
|
||||
g.fieldMap["balance"] = g.Balance
|
||||
g.fieldMap["source"] = g.Source
|
||||
g.fieldMap["source_id"] = g.SourceID
|
||||
g.fieldMap["remark"] = g.Remark
|
||||
}
|
||||
|
||||
func (g gameTicketLogs) clone(db *gorm.DB) gameTicketLogs {
|
||||
g.gameTicketLogsDo.ReplaceConnPool(db.Statement.ConnPool)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g gameTicketLogs) replaceDB(db *gorm.DB) gameTicketLogs {
|
||||
g.gameTicketLogsDo.ReplaceDB(db)
|
||||
return g
|
||||
}
|
||||
|
||||
type gameTicketLogsDo struct{ gen.DO }
|
||||
|
||||
func (g gameTicketLogsDo) Debug() *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Debug())
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) WithContext(ctx context.Context) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.WithContext(ctx))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) ReadDB() *gameTicketLogsDo {
|
||||
return g.Clauses(dbresolver.Read)
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) WriteDB() *gameTicketLogsDo {
|
||||
return g.Clauses(dbresolver.Write)
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Session(config *gorm.Session) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Session(config))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Clauses(conds ...clause.Expression) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Clauses(conds...))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Returning(value interface{}, columns ...string) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Returning(value, columns...))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Not(conds ...gen.Condition) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Not(conds...))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Or(conds ...gen.Condition) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Or(conds...))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Select(conds ...field.Expr) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Select(conds...))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Where(conds ...gen.Condition) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Where(conds...))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Order(conds ...field.Expr) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Order(conds...))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Distinct(cols ...field.Expr) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Distinct(cols...))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Omit(cols ...field.Expr) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Omit(cols...))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Join(table schema.Tabler, on ...field.Expr) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Join(table, on...))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) LeftJoin(table schema.Tabler, on ...field.Expr) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.LeftJoin(table, on...))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) RightJoin(table schema.Tabler, on ...field.Expr) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.RightJoin(table, on...))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Group(cols ...field.Expr) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Group(cols...))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Having(conds ...gen.Condition) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Having(conds...))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Limit(limit int) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Limit(limit))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Offset(offset int) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Offset(offset))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Scopes(funcs...))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Unscoped() *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Unscoped())
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Create(values ...*model.GameTicketLogs) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return g.DO.Create(values)
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) CreateInBatches(values []*model.GameTicketLogs, batchSize int) error {
|
||||
return g.DO.CreateInBatches(values, batchSize)
|
||||
}
|
||||
|
||||
// Save : !!! underlying implementation is different with GORM
|
||||
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
|
||||
func (g gameTicketLogsDo) Save(values ...*model.GameTicketLogs) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return g.DO.Save(values)
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) First() (*model.GameTicketLogs, error) {
|
||||
if result, err := g.DO.First(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.GameTicketLogs), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Take() (*model.GameTicketLogs, error) {
|
||||
if result, err := g.DO.Take(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.GameTicketLogs), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Last() (*model.GameTicketLogs, error) {
|
||||
if result, err := g.DO.Last(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.GameTicketLogs), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Find() ([]*model.GameTicketLogs, error) {
|
||||
result, err := g.DO.Find()
|
||||
return result.([]*model.GameTicketLogs), err
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.GameTicketLogs, err error) {
|
||||
buf := make([]*model.GameTicketLogs, 0, batchSize)
|
||||
err = g.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
|
||||
defer func() { results = append(results, buf...) }()
|
||||
return fc(tx, batch)
|
||||
})
|
||||
return results, err
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) FindInBatches(result *[]*model.GameTicketLogs, batchSize int, fc func(tx gen.Dao, batch int) error) error {
|
||||
return g.DO.FindInBatches(result, batchSize, fc)
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Attrs(attrs ...field.AssignExpr) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Attrs(attrs...))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Assign(attrs ...field.AssignExpr) *gameTicketLogsDo {
|
||||
return g.withDO(g.DO.Assign(attrs...))
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Joins(fields ...field.RelationField) *gameTicketLogsDo {
|
||||
for _, _f := range fields {
|
||||
g = *g.withDO(g.DO.Joins(_f))
|
||||
}
|
||||
return &g
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Preload(fields ...field.RelationField) *gameTicketLogsDo {
|
||||
for _, _f := range fields {
|
||||
g = *g.withDO(g.DO.Preload(_f))
|
||||
}
|
||||
return &g
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) FirstOrInit() (*model.GameTicketLogs, error) {
|
||||
if result, err := g.DO.FirstOrInit(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.GameTicketLogs), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) FirstOrCreate() (*model.GameTicketLogs, error) {
|
||||
if result, err := g.DO.FirstOrCreate(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.GameTicketLogs), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) FindByPage(offset int, limit int) (result []*model.GameTicketLogs, count int64, err error) {
|
||||
result, err = g.Offset(offset).Limit(limit).Find()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if size := len(result); 0 < limit && 0 < size && size < limit {
|
||||
count = int64(size + offset)
|
||||
return
|
||||
}
|
||||
|
||||
count, err = g.Offset(-1).Limit(-1).Count()
|
||||
return
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
|
||||
count, err = g.Count()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = g.Offset(offset).Limit(limit).Scan(result)
|
||||
return
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Scan(result interface{}) (err error) {
|
||||
return g.DO.Scan(result)
|
||||
}
|
||||
|
||||
func (g gameTicketLogsDo) Delete(models ...*model.GameTicketLogs) (result gen.ResultInfo, err error) {
|
||||
return g.DO.Delete(models)
|
||||
}
|
||||
|
||||
func (g *gameTicketLogsDo) withDO(do gen.Dao) *gameTicketLogsDo {
|
||||
g.DO = *do.(*gen.DO)
|
||||
return g
|
||||
}
|
||||
@ -27,6 +27,7 @@ var (
|
||||
Admin *admin
|
||||
Banner *banner
|
||||
Channels *channels
|
||||
GameTicketLogs *gameTicketLogs
|
||||
IssuePositionClaims *issuePositionClaims
|
||||
LogOperation *logOperation
|
||||
LogRequest *logRequest
|
||||
@ -60,6 +61,7 @@ var (
|
||||
TaskCenterTasks *taskCenterTasks
|
||||
UserAddresses *userAddresses
|
||||
UserCoupons *userCoupons
|
||||
UserGameTickets *userGameTickets
|
||||
UserInventory *userInventory
|
||||
UserInventoryTransfers *userInventoryTransfers
|
||||
UserInvites *userInvites
|
||||
@ -83,6 +85,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
|
||||
Admin = &Q.Admin
|
||||
Banner = &Q.Banner
|
||||
Channels = &Q.Channels
|
||||
GameTicketLogs = &Q.GameTicketLogs
|
||||
IssuePositionClaims = &Q.IssuePositionClaims
|
||||
LogOperation = &Q.LogOperation
|
||||
LogRequest = &Q.LogRequest
|
||||
@ -116,6 +119,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
|
||||
TaskCenterTasks = &Q.TaskCenterTasks
|
||||
UserAddresses = &Q.UserAddresses
|
||||
UserCoupons = &Q.UserCoupons
|
||||
UserGameTickets = &Q.UserGameTickets
|
||||
UserInventory = &Q.UserInventory
|
||||
UserInventoryTransfers = &Q.UserInventoryTransfers
|
||||
UserInvites = &Q.UserInvites
|
||||
@ -140,6 +144,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
|
||||
Admin: newAdmin(db, opts...),
|
||||
Banner: newBanner(db, opts...),
|
||||
Channels: newChannels(db, opts...),
|
||||
GameTicketLogs: newGameTicketLogs(db, opts...),
|
||||
IssuePositionClaims: newIssuePositionClaims(db, opts...),
|
||||
LogOperation: newLogOperation(db, opts...),
|
||||
LogRequest: newLogRequest(db, opts...),
|
||||
@ -173,6 +178,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
|
||||
TaskCenterTasks: newTaskCenterTasks(db, opts...),
|
||||
UserAddresses: newUserAddresses(db, opts...),
|
||||
UserCoupons: newUserCoupons(db, opts...),
|
||||
UserGameTickets: newUserGameTickets(db, opts...),
|
||||
UserInventory: newUserInventory(db, opts...),
|
||||
UserInventoryTransfers: newUserInventoryTransfers(db, opts...),
|
||||
UserInvites: newUserInvites(db, opts...),
|
||||
@ -198,6 +204,7 @@ type Query struct {
|
||||
Admin admin
|
||||
Banner banner
|
||||
Channels channels
|
||||
GameTicketLogs gameTicketLogs
|
||||
IssuePositionClaims issuePositionClaims
|
||||
LogOperation logOperation
|
||||
LogRequest logRequest
|
||||
@ -231,6 +238,7 @@ type Query struct {
|
||||
TaskCenterTasks taskCenterTasks
|
||||
UserAddresses userAddresses
|
||||
UserCoupons userCoupons
|
||||
UserGameTickets userGameTickets
|
||||
UserInventory userInventory
|
||||
UserInventoryTransfers userInventoryTransfers
|
||||
UserInvites userInvites
|
||||
@ -257,6 +265,7 @@ func (q *Query) clone(db *gorm.DB) *Query {
|
||||
Admin: q.Admin.clone(db),
|
||||
Banner: q.Banner.clone(db),
|
||||
Channels: q.Channels.clone(db),
|
||||
GameTicketLogs: q.GameTicketLogs.clone(db),
|
||||
IssuePositionClaims: q.IssuePositionClaims.clone(db),
|
||||
LogOperation: q.LogOperation.clone(db),
|
||||
LogRequest: q.LogRequest.clone(db),
|
||||
@ -290,6 +299,7 @@ func (q *Query) clone(db *gorm.DB) *Query {
|
||||
TaskCenterTasks: q.TaskCenterTasks.clone(db),
|
||||
UserAddresses: q.UserAddresses.clone(db),
|
||||
UserCoupons: q.UserCoupons.clone(db),
|
||||
UserGameTickets: q.UserGameTickets.clone(db),
|
||||
UserInventory: q.UserInventory.clone(db),
|
||||
UserInventoryTransfers: q.UserInventoryTransfers.clone(db),
|
||||
UserInvites: q.UserInvites.clone(db),
|
||||
@ -323,6 +333,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
|
||||
Admin: q.Admin.replaceDB(db),
|
||||
Banner: q.Banner.replaceDB(db),
|
||||
Channels: q.Channels.replaceDB(db),
|
||||
GameTicketLogs: q.GameTicketLogs.replaceDB(db),
|
||||
IssuePositionClaims: q.IssuePositionClaims.replaceDB(db),
|
||||
LogOperation: q.LogOperation.replaceDB(db),
|
||||
LogRequest: q.LogRequest.replaceDB(db),
|
||||
@ -356,6 +367,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
|
||||
TaskCenterTasks: q.TaskCenterTasks.replaceDB(db),
|
||||
UserAddresses: q.UserAddresses.replaceDB(db),
|
||||
UserCoupons: q.UserCoupons.replaceDB(db),
|
||||
UserGameTickets: q.UserGameTickets.replaceDB(db),
|
||||
UserInventory: q.UserInventory.replaceDB(db),
|
||||
UserInventoryTransfers: q.UserInventoryTransfers.replaceDB(db),
|
||||
UserInvites: q.UserInvites.replaceDB(db),
|
||||
@ -379,6 +391,7 @@ type queryCtx struct {
|
||||
Admin *adminDo
|
||||
Banner *bannerDo
|
||||
Channels *channelsDo
|
||||
GameTicketLogs *gameTicketLogsDo
|
||||
IssuePositionClaims *issuePositionClaimsDo
|
||||
LogOperation *logOperationDo
|
||||
LogRequest *logRequestDo
|
||||
@ -412,6 +425,7 @@ type queryCtx struct {
|
||||
TaskCenterTasks *taskCenterTasksDo
|
||||
UserAddresses *userAddressesDo
|
||||
UserCoupons *userCouponsDo
|
||||
UserGameTickets *userGameTicketsDo
|
||||
UserInventory *userInventoryDo
|
||||
UserInventoryTransfers *userInventoryTransfersDo
|
||||
UserInvites *userInvitesDo
|
||||
@ -435,6 +449,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
|
||||
Admin: q.Admin.WithContext(ctx),
|
||||
Banner: q.Banner.WithContext(ctx),
|
||||
Channels: q.Channels.WithContext(ctx),
|
||||
GameTicketLogs: q.GameTicketLogs.WithContext(ctx),
|
||||
IssuePositionClaims: q.IssuePositionClaims.WithContext(ctx),
|
||||
LogOperation: q.LogOperation.WithContext(ctx),
|
||||
LogRequest: q.LogRequest.WithContext(ctx),
|
||||
@ -468,6 +483,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
|
||||
TaskCenterTasks: q.TaskCenterTasks.WithContext(ctx),
|
||||
UserAddresses: q.UserAddresses.WithContext(ctx),
|
||||
UserCoupons: q.UserCoupons.WithContext(ctx),
|
||||
UserGameTickets: q.UserGameTickets.WithContext(ctx),
|
||||
UserInventory: q.UserInventory.WithContext(ctx),
|
||||
UserInventoryTransfers: q.UserInventoryTransfers.WithContext(ctx),
|
||||
UserInvites: q.UserInvites.WithContext(ctx),
|
||||
|
||||
348
internal/repository/mysql/dao/user_game_tickets.gen.go
Normal file
348
internal/repository/mysql/dao/user_game_tickets.gen.go
Normal file
@ -0,0 +1,348 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
"gorm.io/gorm/schema"
|
||||
|
||||
"gorm.io/gen"
|
||||
"gorm.io/gen/field"
|
||||
|
||||
"gorm.io/plugin/dbresolver"
|
||||
|
||||
"bindbox-game/internal/repository/mysql/model"
|
||||
)
|
||||
|
||||
func newUserGameTickets(db *gorm.DB, opts ...gen.DOOption) userGameTickets {
|
||||
_userGameTickets := userGameTickets{}
|
||||
|
||||
_userGameTickets.userGameTicketsDo.UseDB(db, opts...)
|
||||
_userGameTickets.userGameTicketsDo.UseModel(&model.UserGameTickets{})
|
||||
|
||||
tableName := _userGameTickets.userGameTicketsDo.TableName()
|
||||
_userGameTickets.ALL = field.NewAsterisk(tableName)
|
||||
_userGameTickets.ID = field.NewInt64(tableName, "id")
|
||||
_userGameTickets.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_userGameTickets.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||
_userGameTickets.UserID = field.NewInt64(tableName, "user_id")
|
||||
_userGameTickets.GameCode = field.NewString(tableName, "game_code")
|
||||
_userGameTickets.Available = field.NewInt32(tableName, "available")
|
||||
_userGameTickets.TotalEarned = field.NewInt32(tableName, "total_earned")
|
||||
_userGameTickets.TotalUsed = field.NewInt32(tableName, "total_used")
|
||||
|
||||
_userGameTickets.fillFieldMap()
|
||||
|
||||
return _userGameTickets
|
||||
}
|
||||
|
||||
// userGameTickets 用户游戏资格
|
||||
type userGameTickets struct {
|
||||
userGameTicketsDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Int64
|
||||
CreatedAt field.Time
|
||||
UpdatedAt field.Time
|
||||
UserID field.Int64 // 用户ID
|
||||
GameCode field.String // 游戏代码
|
||||
Available field.Int32 // 可用次数
|
||||
TotalEarned field.Int32 // 累计获得
|
||||
TotalUsed field.Int32 // 累计使用
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
func (u userGameTickets) Table(newTableName string) *userGameTickets {
|
||||
u.userGameTicketsDo.UseTable(newTableName)
|
||||
return u.updateTableName(newTableName)
|
||||
}
|
||||
|
||||
func (u userGameTickets) As(alias string) *userGameTickets {
|
||||
u.userGameTicketsDo.DO = *(u.userGameTicketsDo.As(alias).(*gen.DO))
|
||||
return u.updateTableName(alias)
|
||||
}
|
||||
|
||||
func (u *userGameTickets) updateTableName(table string) *userGameTickets {
|
||||
u.ALL = field.NewAsterisk(table)
|
||||
u.ID = field.NewInt64(table, "id")
|
||||
u.CreatedAt = field.NewTime(table, "created_at")
|
||||
u.UpdatedAt = field.NewTime(table, "updated_at")
|
||||
u.UserID = field.NewInt64(table, "user_id")
|
||||
u.GameCode = field.NewString(table, "game_code")
|
||||
u.Available = field.NewInt32(table, "available")
|
||||
u.TotalEarned = field.NewInt32(table, "total_earned")
|
||||
u.TotalUsed = field.NewInt32(table, "total_used")
|
||||
|
||||
u.fillFieldMap()
|
||||
|
||||
return u
|
||||
}
|
||||
|
||||
func (u *userGameTickets) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
_f, ok := u.fieldMap[fieldName]
|
||||
if !ok || _f == nil {
|
||||
return nil, false
|
||||
}
|
||||
_oe, ok := _f.(field.OrderExpr)
|
||||
return _oe, ok
|
||||
}
|
||||
|
||||
func (u *userGameTickets) fillFieldMap() {
|
||||
u.fieldMap = make(map[string]field.Expr, 8)
|
||||
u.fieldMap["id"] = u.ID
|
||||
u.fieldMap["created_at"] = u.CreatedAt
|
||||
u.fieldMap["updated_at"] = u.UpdatedAt
|
||||
u.fieldMap["user_id"] = u.UserID
|
||||
u.fieldMap["game_code"] = u.GameCode
|
||||
u.fieldMap["available"] = u.Available
|
||||
u.fieldMap["total_earned"] = u.TotalEarned
|
||||
u.fieldMap["total_used"] = u.TotalUsed
|
||||
}
|
||||
|
||||
func (u userGameTickets) clone(db *gorm.DB) userGameTickets {
|
||||
u.userGameTicketsDo.ReplaceConnPool(db.Statement.ConnPool)
|
||||
return u
|
||||
}
|
||||
|
||||
func (u userGameTickets) replaceDB(db *gorm.DB) userGameTickets {
|
||||
u.userGameTicketsDo.ReplaceDB(db)
|
||||
return u
|
||||
}
|
||||
|
||||
type userGameTicketsDo struct{ gen.DO }
|
||||
|
||||
func (u userGameTicketsDo) Debug() *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Debug())
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) WithContext(ctx context.Context) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.WithContext(ctx))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) ReadDB() *userGameTicketsDo {
|
||||
return u.Clauses(dbresolver.Read)
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) WriteDB() *userGameTicketsDo {
|
||||
return u.Clauses(dbresolver.Write)
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Session(config *gorm.Session) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Session(config))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Clauses(conds ...clause.Expression) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Clauses(conds...))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Returning(value interface{}, columns ...string) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Returning(value, columns...))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Not(conds ...gen.Condition) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Not(conds...))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Or(conds ...gen.Condition) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Or(conds...))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Select(conds ...field.Expr) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Select(conds...))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Where(conds ...gen.Condition) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Where(conds...))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Order(conds ...field.Expr) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Order(conds...))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Distinct(cols ...field.Expr) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Distinct(cols...))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Omit(cols ...field.Expr) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Omit(cols...))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Join(table schema.Tabler, on ...field.Expr) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Join(table, on...))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) LeftJoin(table schema.Tabler, on ...field.Expr) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.LeftJoin(table, on...))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) RightJoin(table schema.Tabler, on ...field.Expr) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.RightJoin(table, on...))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Group(cols ...field.Expr) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Group(cols...))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Having(conds ...gen.Condition) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Having(conds...))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Limit(limit int) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Limit(limit))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Offset(offset int) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Offset(offset))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Scopes(funcs...))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Unscoped() *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Unscoped())
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Create(values ...*model.UserGameTickets) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return u.DO.Create(values)
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) CreateInBatches(values []*model.UserGameTickets, batchSize int) error {
|
||||
return u.DO.CreateInBatches(values, batchSize)
|
||||
}
|
||||
|
||||
// Save : !!! underlying implementation is different with GORM
|
||||
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
|
||||
func (u userGameTicketsDo) Save(values ...*model.UserGameTickets) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return u.DO.Save(values)
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) First() (*model.UserGameTickets, error) {
|
||||
if result, err := u.DO.First(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.UserGameTickets), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Take() (*model.UserGameTickets, error) {
|
||||
if result, err := u.DO.Take(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.UserGameTickets), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Last() (*model.UserGameTickets, error) {
|
||||
if result, err := u.DO.Last(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.UserGameTickets), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Find() ([]*model.UserGameTickets, error) {
|
||||
result, err := u.DO.Find()
|
||||
return result.([]*model.UserGameTickets), err
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.UserGameTickets, err error) {
|
||||
buf := make([]*model.UserGameTickets, 0, batchSize)
|
||||
err = u.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
|
||||
defer func() { results = append(results, buf...) }()
|
||||
return fc(tx, batch)
|
||||
})
|
||||
return results, err
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) FindInBatches(result *[]*model.UserGameTickets, batchSize int, fc func(tx gen.Dao, batch int) error) error {
|
||||
return u.DO.FindInBatches(result, batchSize, fc)
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Attrs(attrs ...field.AssignExpr) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Attrs(attrs...))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Assign(attrs ...field.AssignExpr) *userGameTicketsDo {
|
||||
return u.withDO(u.DO.Assign(attrs...))
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Joins(fields ...field.RelationField) *userGameTicketsDo {
|
||||
for _, _f := range fields {
|
||||
u = *u.withDO(u.DO.Joins(_f))
|
||||
}
|
||||
return &u
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Preload(fields ...field.RelationField) *userGameTicketsDo {
|
||||
for _, _f := range fields {
|
||||
u = *u.withDO(u.DO.Preload(_f))
|
||||
}
|
||||
return &u
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) FirstOrInit() (*model.UserGameTickets, error) {
|
||||
if result, err := u.DO.FirstOrInit(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.UserGameTickets), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) FirstOrCreate() (*model.UserGameTickets, error) {
|
||||
if result, err := u.DO.FirstOrCreate(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.UserGameTickets), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) FindByPage(offset int, limit int) (result []*model.UserGameTickets, count int64, err error) {
|
||||
result, err = u.Offset(offset).Limit(limit).Find()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if size := len(result); 0 < limit && 0 < size && size < limit {
|
||||
count = int64(size + offset)
|
||||
return
|
||||
}
|
||||
|
||||
count, err = u.Offset(-1).Limit(-1).Count()
|
||||
return
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
|
||||
count, err = u.Count()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = u.Offset(offset).Limit(limit).Scan(result)
|
||||
return
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Scan(result interface{}) (err error) {
|
||||
return u.DO.Scan(result)
|
||||
}
|
||||
|
||||
func (u userGameTicketsDo) Delete(models ...*model.UserGameTickets) (result gen.ResultInfo, err error) {
|
||||
return u.DO.Delete(models)
|
||||
}
|
||||
|
||||
func (u *userGameTicketsDo) withDO(do gen.Dao) *userGameTicketsDo {
|
||||
u.DO = *do.(*gen.DO)
|
||||
return u
|
||||
}
|
||||
30
internal/repository/mysql/model/game_ticket_logs.gen.go
Normal file
30
internal/repository/mysql/model/game_ticket_logs.gen.go
Normal file
@ -0,0 +1,30 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const TableNameGameTicketLogs = "game_ticket_logs"
|
||||
|
||||
// GameTicketLogs 游戏资格变动日志
|
||||
type GameTicketLogs struct {
|
||||
ID int64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP(3)" json:"created_at"`
|
||||
UserID int64 `gorm:"column:user_id;not null;comment:用户ID" json:"user_id"` // 用户ID
|
||||
GameCode string `gorm:"column:game_code;not null;comment:游戏代码" json:"game_code"` // 游戏代码
|
||||
ChangeType int32 `gorm:"column:change_type;not null;comment:1=获得 2=使用" json:"change_type"` // 1=获得 2=使用
|
||||
Amount int32 `gorm:"column:amount;not null;comment:变动数量" json:"amount"` // 变动数量
|
||||
Balance int32 `gorm:"column:balance;not null;comment:变动后余额" json:"balance"` // 变动后余额
|
||||
Source string `gorm:"column:source;comment:来源: order/task/admin" json:"source"` // 来源: order/task/admin
|
||||
SourceID int64 `gorm:"column:source_id;comment:来源ID" json:"source_id"` // 来源ID
|
||||
Remark string `gorm:"column:remark;comment:备注" json:"remark"` // 备注
|
||||
}
|
||||
|
||||
// TableName GameTicketLogs's table name
|
||||
func (*GameTicketLogs) TableName() string {
|
||||
return TableNameGameTicketLogs
|
||||
}
|
||||
28
internal/repository/mysql/model/user_game_tickets.gen.go
Normal file
28
internal/repository/mysql/model/user_game_tickets.gen.go
Normal file
@ -0,0 +1,28 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const TableNameUserGameTickets = "user_game_tickets"
|
||||
|
||||
// UserGameTickets 用户游戏资格
|
||||
type UserGameTickets struct {
|
||||
ID int64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP(3)" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP(3)" json:"updated_at"`
|
||||
UserID int64 `gorm:"column:user_id;not null;comment:用户ID" json:"user_id"` // 用户ID
|
||||
GameCode string `gorm:"column:game_code;not null;default:minesweeper;comment:游戏代码" json:"game_code"` // 游戏代码
|
||||
Available int32 `gorm:"column:available;not null;comment:可用次数" json:"available"` // 可用次数
|
||||
TotalEarned int32 `gorm:"column:total_earned;not null;comment:累计获得" json:"total_earned"` // 累计获得
|
||||
TotalUsed int32 `gorm:"column:total_used;not null;comment:累计使用" json:"total_used"` // 累计使用
|
||||
}
|
||||
|
||||
// TableName UserGameTickets's table name
|
||||
func (*UserGameTickets) TableName() string {
|
||||
return TableNameUserGameTickets
|
||||
}
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"bindbox-game/internal/api/admin"
|
||||
appapi "bindbox-game/internal/api/app"
|
||||
commonapi "bindbox-game/internal/api/common"
|
||||
gameapi "bindbox-game/internal/api/game"
|
||||
payapi "bindbox-game/internal/api/pay"
|
||||
taskcenterapi "bindbox-game/internal/api/task_center"
|
||||
userapi "bindbox-game/internal/api/user"
|
||||
@ -65,16 +66,17 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo) (core.Mux, func(), er
|
||||
userHandler := userapi.New(logger, db)
|
||||
commonHandler := commonapi.New(logger, db)
|
||||
payHandler := payapi.New(logger, db, taskSvc)
|
||||
// minesweeperHandler := minesweeperapi.New(logger, db)
|
||||
gameHandler := gameapi.New(logger, db, rdb, userSvc)
|
||||
intc := interceptor.New(logger, db)
|
||||
|
||||
// 内部服务接口路由组 (供 Nakama 调用)
|
||||
// internalRouter := mux.Group("/internal")
|
||||
// {
|
||||
// TODO: 添加IP白名单或Internal-Key验证中间件
|
||||
// internalRouter.POST("/game/verify", minesweeperHandler.VerifyTicket())
|
||||
// internalRouter.POST("/game/settle", minesweeperHandler.SettleGame())
|
||||
// }
|
||||
internalRouter := mux.Group("/internal")
|
||||
{
|
||||
// TODO: 添加IP白名单或Internal-Key验证中间件
|
||||
internalRouter.POST("/game/verify", gameHandler.VerifyTicket())
|
||||
internalRouter.POST("/game/settle", gameHandler.SettleGame())
|
||||
internalRouter.GET("/game/minesweeper/config", gameHandler.GetMinesweeperConfig())
|
||||
}
|
||||
|
||||
// 管理端非认证接口路由组
|
||||
adminNonAuthApiRouter := mux.Group("/api/admin")
|
||||
@ -224,6 +226,10 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo) (core.Mux, func(), er
|
||||
adminAuthApiRouter.PUT("/matching_card_types/:id", adminHandler.ModifyMatchingCardType())
|
||||
adminAuthApiRouter.DELETE("/matching_card_types/:id", adminHandler.DeleteMatchingCardType())
|
||||
|
||||
// 游戏资格管理
|
||||
adminAuthApiRouter.GET("/users/:user_id/game_tickets", gameHandler.ListUserTickets())
|
||||
adminAuthApiRouter.POST("/users/:user_id/game_tickets", gameHandler.GrantUserTicket())
|
||||
|
||||
// 发货统计
|
||||
adminAuthApiRouter.GET("/ops_shipping_stats", intc.RequireAdminAction("ops:shipping:view"), adminHandler.ListShippingStats())
|
||||
adminAuthApiRouter.GET("/ops_shipping_stats/:id", intc.RequireAdminAction("ops:shipping:view"), adminHandler.GetShippingStat())
|
||||
@ -351,6 +357,10 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo) (core.Mux, func(), er
|
||||
appAuthApiRouter.POST("/matching/check", activityHandler.CheckMatchingGame())
|
||||
appAuthApiRouter.GET("/matching/state", activityHandler.GetMatchingGameState())
|
||||
|
||||
// 扫雷游戏
|
||||
appAuthApiRouter.GET("/users/:user_id/game_tickets", gameHandler.GetMyTickets())
|
||||
appAuthApiRouter.POST("/games/enter", gameHandler.EnterGame())
|
||||
|
||||
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())
|
||||
|
||||
190
internal/service/game/ticket_service.go
Normal file
190
internal/service/game/ticket_service.go
Normal file
@ -0,0 +1,190 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"bindbox-game/internal/pkg/logger"
|
||||
"bindbox-game/internal/repository/mysql"
|
||||
"bindbox-game/internal/repository/mysql/dao"
|
||||
"bindbox-game/internal/repository/mysql/model"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
// TicketService 游戏资格服务
|
||||
type TicketService interface {
|
||||
// GrantTicket 发放游戏资格
|
||||
GrantTicket(ctx context.Context, userID int64, gameCode string, amount int, source string, sourceID int64, remark string) error
|
||||
// UseTicket 使用游戏资格
|
||||
UseTicket(ctx context.Context, userID int64, gameCode string) error
|
||||
// GetUserTickets 获取用户资格
|
||||
GetUserTickets(ctx context.Context, userID int64) (map[string]int, error)
|
||||
// GetUserTicketByGame 获取用户指定游戏资格
|
||||
GetUserTicketByGame(ctx context.Context, userID int64, gameCode string) (*model.UserGameTickets, error)
|
||||
// GetTicketLogs 获取用户资格变动日志
|
||||
GetTicketLogs(ctx context.Context, userID int64, page, pageSize int) ([]*model.GameTicketLogs, int64, error)
|
||||
}
|
||||
|
||||
type ticketService struct {
|
||||
logger logger.CustomLogger
|
||||
readDB *dao.Query
|
||||
writeDB *dao.Query
|
||||
repo mysql.Repo
|
||||
}
|
||||
|
||||
// NewTicketService 创建资格服务
|
||||
func NewTicketService(l logger.CustomLogger, db mysql.Repo) TicketService {
|
||||
return &ticketService{
|
||||
logger: l,
|
||||
readDB: dao.Use(db.GetDbR()),
|
||||
writeDB: dao.Use(db.GetDbW()),
|
||||
repo: db,
|
||||
}
|
||||
}
|
||||
|
||||
// GrantTicket 发放游戏资格
|
||||
func (s *ticketService) GrantTicket(ctx context.Context, userID int64, gameCode string, amount int, source string, sourceID int64, remark string) error {
|
||||
if amount <= 0 {
|
||||
return fmt.Errorf("amount must be positive")
|
||||
}
|
||||
|
||||
return s.repo.GetDbW().Transaction(func(tx *gorm.DB) error {
|
||||
// Upsert user_game_tickets
|
||||
ticket := &model.UserGameTickets{
|
||||
UserID: userID,
|
||||
GameCode: gameCode,
|
||||
Available: int32(amount),
|
||||
TotalEarned: int32(amount),
|
||||
TotalUsed: 0,
|
||||
}
|
||||
|
||||
err := tx.Clauses(clause.OnConflict{
|
||||
Columns: []clause.Column{{Name: "user_id"}, {Name: "game_code"}},
|
||||
DoUpdates: clause.Assignments(map[string]interface{}{
|
||||
"available": gorm.Expr("available + ?", amount),
|
||||
"total_earned": gorm.Expr("total_earned + ?", amount),
|
||||
"updated_at": time.Now(),
|
||||
}),
|
||||
}).Create(ticket).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 查询变动后余额
|
||||
var balance int32
|
||||
tx.Model(&model.UserGameTickets{}).
|
||||
Where("user_id = ? AND game_code = ?", userID, gameCode).
|
||||
Pluck("available", &balance)
|
||||
|
||||
// 记录日志
|
||||
log := &model.GameTicketLogs{
|
||||
UserID: userID,
|
||||
GameCode: gameCode,
|
||||
ChangeType: 1, // 获得
|
||||
Amount: int32(amount),
|
||||
Balance: balance,
|
||||
Source: source,
|
||||
SourceID: sourceID,
|
||||
Remark: remark,
|
||||
}
|
||||
return tx.Create(log).Error
|
||||
})
|
||||
}
|
||||
|
||||
// UseTicket 使用游戏资格
|
||||
func (s *ticketService) UseTicket(ctx context.Context, userID int64, gameCode string) error {
|
||||
return s.repo.GetDbW().Transaction(func(tx *gorm.DB) error {
|
||||
// 检查并扣减
|
||||
result := tx.Model(&model.UserGameTickets{}).
|
||||
Where("user_id = ? AND game_code = ? AND available > 0", userID, gameCode).
|
||||
Updates(map[string]interface{}{
|
||||
"available": gorm.Expr("available - 1"),
|
||||
"total_used": gorm.Expr("total_used + 1"),
|
||||
"updated_at": time.Now(),
|
||||
})
|
||||
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return fmt.Errorf("insufficient game tickets")
|
||||
}
|
||||
|
||||
// 查询变动后余额
|
||||
var balance int32
|
||||
tx.Model(&model.UserGameTickets{}).
|
||||
Where("user_id = ? AND game_code = ?", userID, gameCode).
|
||||
Pluck("available", &balance)
|
||||
|
||||
// 记录日志
|
||||
log := &model.GameTicketLogs{
|
||||
UserID: userID,
|
||||
GameCode: gameCode,
|
||||
ChangeType: 2, // 使用
|
||||
Amount: 1,
|
||||
Balance: balance,
|
||||
Source: "game_enter",
|
||||
Remark: "进入游戏",
|
||||
}
|
||||
return tx.Create(log).Error
|
||||
})
|
||||
}
|
||||
|
||||
// GetUserTickets 获取用户所有游戏资格
|
||||
func (s *ticketService) GetUserTickets(ctx context.Context, userID int64) (map[string]int, error) {
|
||||
var tickets []*model.UserGameTickets
|
||||
err := s.repo.GetDbR().WithContext(ctx).
|
||||
Where("user_id = ?", userID).
|
||||
Find(&tickets).Error
|
||||
if err != nil {
|
||||
s.logger.Error("GetUserTickets failed", zap.Int64("user_id", userID), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make(map[string]int)
|
||||
for _, t := range tickets {
|
||||
result[t.GameCode] = int(t.Available)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetUserTicketByGame 获取用户指定游戏资格
|
||||
func (s *ticketService) GetUserTicketByGame(ctx context.Context, userID int64, gameCode string) (*model.UserGameTickets, error) {
|
||||
var ticket model.UserGameTickets
|
||||
err := s.repo.GetDbR().WithContext(ctx).
|
||||
Where("user_id = ? AND game_code = ?", userID, gameCode).
|
||||
First(&ticket).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ticket, nil
|
||||
}
|
||||
|
||||
// GetTicketLogs 获取用户游戏资格变动日志
|
||||
func (s *ticketService) GetTicketLogs(ctx context.Context, userID int64, page, pageSize int) ([]*model.GameTicketLogs, int64, error) {
|
||||
var logs []*model.GameTicketLogs
|
||||
var total int64
|
||||
|
||||
db := s.repo.GetDbR().WithContext(ctx).Model(&model.GameTicketLogs{}).Where("user_id = ?", userID)
|
||||
|
||||
if err := db.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
|
||||
offset := (page - 1) * pageSize
|
||||
if err := db.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&logs).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return logs, total, nil
|
||||
}
|
||||
@ -12,6 +12,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
gamesvc "bindbox-game/internal/service/game"
|
||||
titlesvc "bindbox-game/internal/service/title"
|
||||
usersvc "bindbox-game/internal/service/user"
|
||||
|
||||
@ -665,6 +666,16 @@ func (s *service) grantTierRewards(ctx context.Context, taskID int64, tierID int
|
||||
if pl.TitleID > 0 {
|
||||
err = s.titleSvc.AssignUserTitle(ctx, userID, pl.TitleID, nil, "task_center")
|
||||
}
|
||||
case "game_ticket":
|
||||
var pl struct {
|
||||
GameCode string `json:"game_code"`
|
||||
Amount int `json:"amount"`
|
||||
}
|
||||
_ = json.Unmarshal([]byte(r.RewardPayload), &pl)
|
||||
if pl.GameCode != "" && pl.Amount > 0 {
|
||||
gameSvc := gamesvc.NewTicketService(s.logger, s.repo)
|
||||
err = gameSvc.GrantTicket(ctx, userID, pl.GameCode, pl.Amount, "task_center", taskID, "任务奖励")
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
|
||||
@ -142,15 +142,15 @@ func generateBatchNo(userID int64) string {
|
||||
return fmt.Sprintf("B%d%d", userID, time.Now().UnixNano()/1000000)
|
||||
}
|
||||
|
||||
func (s *service) RequestShippings(ctx context.Context, userID int64, inventoryIDs []int64, addressID *int64) (int64, []int64, []struct {
|
||||
func (s *service) RequestShippings(ctx context.Context, userID int64, inventoryIDs []int64, addressID *int64) (addrID int64, batchNo string, success []int64, skipped []struct {
|
||||
ID int64
|
||||
Reason string
|
||||
}, []struct {
|
||||
}, failed []struct {
|
||||
ID int64
|
||||
Reason string
|
||||
}, error) {
|
||||
}, err error) {
|
||||
if len(inventoryIDs) == 0 {
|
||||
return 0, nil, nil, []struct {
|
||||
return 0, "", nil, nil, []struct {
|
||||
ID int64
|
||||
Reason string
|
||||
}{{ID: 0, Reason: "invalid_params"}}, nil
|
||||
@ -166,25 +166,24 @@ func (s *service) RequestShippings(ctx context.Context, userID int64, inventoryI
|
||||
}
|
||||
}
|
||||
if len(uniq) == 0 {
|
||||
return 0, nil, nil, []struct {
|
||||
return 0, "", nil, nil, []struct {
|
||||
ID int64
|
||||
Reason string
|
||||
}{{ID: 0, Reason: "invalid_params"}}, nil
|
||||
}
|
||||
var addrID int64
|
||||
if addressID != nil && *addressID > 0 {
|
||||
ua, _ := s.readDB.UserAddresses.WithContext(ctx).Where(s.readDB.UserAddresses.ID.Eq(*addressID), s.readDB.UserAddresses.UserID.Eq(userID)).First()
|
||||
if ua == nil {
|
||||
return 0, nil, nil, []struct {
|
||||
return 0, "", nil, nil, []struct {
|
||||
ID int64
|
||||
Reason string
|
||||
}{{ID: 0, Reason: "address_not_found"}}, nil
|
||||
}
|
||||
addrID = ua.ID
|
||||
} else {
|
||||
da, err := s.readDB.UserAddresses.WithContext(ctx).Where(s.readDB.UserAddresses.UserID.Eq(userID), s.readDB.UserAddresses.IsDefault.Eq(1)).First()
|
||||
if err != nil || da == nil {
|
||||
return 0, nil, nil, []struct {
|
||||
da, e := s.readDB.UserAddresses.WithContext(ctx).Where(s.readDB.UserAddresses.UserID.Eq(userID), s.readDB.UserAddresses.IsDefault.Eq(1)).First()
|
||||
if e != nil || da == nil {
|
||||
return 0, "", nil, nil, []struct {
|
||||
ID int64
|
||||
Reason string
|
||||
}{{ID: 0, Reason: "no_default_address"}}, nil
|
||||
@ -192,18 +191,15 @@ func (s *service) RequestShippings(ctx context.Context, userID int64, inventoryI
|
||||
addrID = da.ID
|
||||
}
|
||||
|
||||
// 生成批次号(只有多个有效项时才生成)
|
||||
batchNo := ""
|
||||
if len(uniq) > 1 {
|
||||
batchNo = generateBatchNo(userID)
|
||||
}
|
||||
// 始终生成批次号,方便用户查询和管理
|
||||
batchNo = generateBatchNo(userID)
|
||||
|
||||
success := make([]int64, 0, len(uniq))
|
||||
skipped := make([]struct {
|
||||
success = make([]int64, 0, len(uniq))
|
||||
skipped = make([]struct {
|
||||
ID int64
|
||||
Reason string
|
||||
}, 0)
|
||||
failed := make([]struct {
|
||||
failed = make([]struct {
|
||||
ID int64
|
||||
Reason string
|
||||
}, 0)
|
||||
@ -246,7 +242,7 @@ func (s *service) RequestShippings(ctx context.Context, userID int64, inventoryI
|
||||
}
|
||||
success = append(success, id)
|
||||
}
|
||||
return addrID, success, skipped, failed, nil
|
||||
return addrID, batchNo, success, skipped, failed, nil
|
||||
}
|
||||
|
||||
func (s *service) RedeemInventoryToPoints(ctx context.Context, userID int64, inventoryID int64) (int64, error) {
|
||||
|
||||
@ -7,40 +7,84 @@ import (
|
||||
)
|
||||
|
||||
// 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()
|
||||
// 支持按单个资产ID取消,或按批次号批量取消
|
||||
// 返回成功取消的记录数
|
||||
func (s *service) CancelShipping(ctx context.Context, userID int64, inventoryID int64, batchNo string) (int64, error) {
|
||||
var cancelledCount int64
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("shipping record not found or already processed")
|
||||
err := s.writeDB.Transaction(func(tx *dao.Query) error {
|
||||
var records []*struct {
|
||||
ID int64
|
||||
InventoryID int64
|
||||
}
|
||||
|
||||
// 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
|
||||
// 根据参数查询待取消的发货记录
|
||||
if batchNo != "" {
|
||||
// 按批次号查询
|
||||
rows, err := tx.ShippingRecords.WithContext(ctx).
|
||||
Select(tx.ShippingRecords.ID, tx.ShippingRecords.InventoryID).
|
||||
Where(tx.ShippingRecords.BatchNo.Eq(batchNo)).
|
||||
Where(tx.ShippingRecords.UserID.Eq(userID)).
|
||||
Where(tx.ShippingRecords.Status.Eq(1)). // 待发货状态
|
||||
Find()
|
||||
if err != nil {
|
||||
return fmt.Errorf("query shipping records failed: %w", err)
|
||||
}
|
||||
for _, r := range rows {
|
||||
records = append(records, &struct {
|
||||
ID int64
|
||||
InventoryID int64
|
||||
}{ID: r.ID, InventoryID: r.InventoryID})
|
||||
}
|
||||
} else if inventoryID > 0 {
|
||||
// 按单个资产ID查询
|
||||
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")
|
||||
}
|
||||
records = append(records, &struct {
|
||||
ID int64
|
||||
InventoryID int64
|
||||
}{ID: sr.ID, InventoryID: sr.InventoryID})
|
||||
}
|
||||
|
||||
// 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
|
||||
if len(records) == 0 {
|
||||
return fmt.Errorf("no pending shipping records found")
|
||||
}
|
||||
|
||||
// 批量处理每条记录
|
||||
for _, rec := range records {
|
||||
// 更新发货记录状态为已取消 (status=5)
|
||||
if _, err := tx.ShippingRecords.WithContext(ctx).
|
||||
Where(tx.ShippingRecords.ID.Eq(rec.ID)).
|
||||
Update(tx.ShippingRecords.Status, 5); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 恢复库存状态为可用 (status=1)
|
||||
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,
|
||||
rec.InventoryID,
|
||||
userID,
|
||||
).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cancelledCount++
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return cancelledCount, nil
|
||||
}
|
||||
|
||||
@ -1,16 +1,15 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRequestShippings_DedupAndSkip(t *testing.T) {
|
||||
s := New(nil, nil)
|
||||
// s.readDB/s.writeDB are nil in this placeholder; this test is a placeholder to ensure compilation.
|
||||
_, _, _, _, err := s.RequestShippings(context.Background(), 1, []int64{0, 1, 1}, nil)
|
||||
if err != nil {
|
||||
// no real DB; just ensure function can be called
|
||||
}
|
||||
s := New(nil, nil)
|
||||
// s.readDB/s.writeDB are nil in this placeholder; this test is a placeholder to ensure compilation.
|
||||
_, _, _, _, _, err := s.RequestShippings(context.Background(), 1, []int64{0, 1, 1}, nil)
|
||||
if err != nil {
|
||||
// no real DB; just ensure function can be called
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -50,14 +50,14 @@ 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 {
|
||||
CancelShipping(ctx context.Context, userID int64, inventoryID int64, batchNo string) (int64, error)
|
||||
RequestShippings(ctx context.Context, userID int64, inventoryIDs []int64, addressID *int64) (addrID int64, batchNo string, success []int64, skipped []struct {
|
||||
ID int64
|
||||
Reason string
|
||||
}, []struct {
|
||||
}, failed []struct {
|
||||
ID int64
|
||||
Reason string
|
||||
}, error)
|
||||
}, err error)
|
||||
VoidUserInventory(ctx context.Context, adminID int64, userID int64, inventoryID int64) error
|
||||
RedeemInventoryToPoints(ctx context.Context, userID int64, inventoryID int64) (int64, error)
|
||||
RedeemInventoriesToPoints(ctx context.Context, userID int64, inventoryIDs []int64) (int64, error)
|
||||
|
||||
@ -331,3 +331,39 @@
|
||||
{"level":"info","time":"2025-12-23 23:32:14","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2}
|
||||
{"level":"info","time":"2025-12-23 23:32:14","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3}
|
||||
{"level":"info","time":"2025-12-23 23:32:14","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0}
|
||||
{"level":"info","time":"2025-12-23 23:38: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 23:38:04","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"}
|
||||
{"level":"info","time":"2025-12-23 23:38:04","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1}
|
||||
{"level":"info","time":"2025-12-23 23:38:04","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2}
|
||||
{"level":"info","time":"2025-12-23 23:38:04","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0}
|
||||
{"level":"info","time":"2025-12-23 23:38:04","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3}
|
||||
{"level":"info","time":"2025-12-23 23:38:04","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4}
|
||||
{"level":"info","time":"2025-12-24 13:04:26","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"}
|
||||
{"level":"info","time":"2025-12-24 13:04:26","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"}
|
||||
{"level":"info","time":"2025-12-24 13:04:26","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4}
|
||||
{"level":"info","time":"2025-12-24 13:04:26","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0}
|
||||
{"level":"info","time":"2025-12-24 13:04:26","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3}
|
||||
{"level":"info","time":"2025-12-24 13:04:26","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1}
|
||||
{"level":"info","time":"2025-12-24 13:04:26","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2}
|
||||
{"level":"info","time":"2025-12-24 13:26:35","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"}
|
||||
{"level":"info","time":"2025-12-24 13:26:35","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"}
|
||||
{"level":"info","time":"2025-12-24 13:26:35","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2}
|
||||
{"level":"info","time":"2025-12-24 13:26:35","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0}
|
||||
{"level":"info","time":"2025-12-24 13:26:35","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3}
|
||||
{"level":"info","time":"2025-12-24 13:26:35","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1}
|
||||
{"level":"info","time":"2025-12-24 13:26:35","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4}
|
||||
{"level":"info","time":"2025-12-24 13:29:35","caller":"logger/logger.go:309","msg":"Processing event","domain":"mini-chat[fat]","type":"order_paid","worker_id":1}
|
||||
{"level":"info","time":"2025-12-24 14:48: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-24 14:48:36","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"}
|
||||
{"level":"info","time":"2025-12-24 14:48:36","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4}
|
||||
{"level":"info","time":"2025-12-24 14:48:36","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1}
|
||||
{"level":"info","time":"2025-12-24 14:48:36","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2}
|
||||
{"level":"info","time":"2025-12-24 14:48:36","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0}
|
||||
{"level":"info","time":"2025-12-24 14:48:36","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3}
|
||||
{"level":"info","time":"2025-12-24 17:05:25","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"}
|
||||
{"level":"info","time":"2025-12-24 17:05:26","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"}
|
||||
{"level":"info","time":"2025-12-24 17:05:26","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2}
|
||||
{"level":"info","time":"2025-12-24 17:05:26","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0}
|
||||
{"level":"info","time":"2025-12-24 17:05:26","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4}
|
||||
{"level":"info","time":"2025-12-24 17:05:26","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1}
|
||||
{"level":"info","time":"2025-12-24 17:05:26","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3}
|
||||
|
||||
32
migrations/006_game_tickets.sql
Normal file
32
migrations/006_game_tickets.sql
Normal file
@ -0,0 +1,32 @@
|
||||
-- 游戏资格系统表
|
||||
-- Migration: 006_game_tickets.sql
|
||||
|
||||
-- 用户游戏资格
|
||||
CREATE TABLE IF NOT EXISTS user_game_tickets (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
created_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
user_id BIGINT NOT NULL COMMENT '用户ID',
|
||||
game_code VARCHAR(32) NOT NULL DEFAULT 'minesweeper' COMMENT '游戏代码',
|
||||
available INT NOT NULL DEFAULT 0 COMMENT '可用次数',
|
||||
total_earned INT NOT NULL DEFAULT 0 COMMENT '累计获得',
|
||||
total_used INT NOT NULL DEFAULT 0 COMMENT '累计使用',
|
||||
UNIQUE KEY uk_user_game (user_id, game_code),
|
||||
INDEX idx_user_id (user_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户游戏资格';
|
||||
|
||||
-- 资格变动日志
|
||||
CREATE TABLE IF NOT EXISTS game_ticket_logs (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
created_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3),
|
||||
user_id BIGINT NOT NULL COMMENT '用户ID',
|
||||
game_code VARCHAR(32) NOT NULL COMMENT '游戏代码',
|
||||
change_type TINYINT NOT NULL COMMENT '1=获得 2=使用',
|
||||
amount INT NOT NULL COMMENT '变动数量',
|
||||
balance INT NOT NULL COMMENT '变动后余额',
|
||||
source VARCHAR(32) COMMENT '来源: order/task/admin',
|
||||
source_id BIGINT COMMENT '来源ID',
|
||||
remark VARCHAR(255) COMMENT '备注',
|
||||
INDEX idx_user_game (user_id, game_code),
|
||||
INDEX idx_created (created_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='游戏资格变动日志';
|
||||
Loading…
x
Reference in New Issue
Block a user