package user import ( "context" "strings" "bindbox-game/internal/repository/mysql/model" ) // DrawReceiptInfo 抽奖验证凭据信息 type DrawReceiptInfo struct { DrawLogID int64 `json:"draw_log_id"` RewardID int64 `json:"reward_id,omitempty"` DrawIndex int `json:"draw_index"` AlgoVersion string `json:"algo_version"` RoundID int64 `json:"round_id"` DrawID int64 `json:"draw_id"` ClientID int64 `json:"client_id"` Timestamp int64 `json:"timestamp"` ServerSeedHash string `json:"server_seed_hash"` ServerSubSeed string `json:"server_sub_seed"` ClientSeed string `json:"client_seed"` Nonce int64 `json:"nonce"` ItemsRoot string `json:"items_root"` WeightsTotal int64 `json:"weights_total"` SelectedIndex int32 `json:"selected_index"` RandProof string `json:"rand_proof"` Signature string `json:"signature,omitempty"` ItemsSnapshot string `json:"items_snapshot,omitempty"` } // OrderWithItems 包含订单项的订单信息 type OrderWithItems struct { *model.Orders Items []*model.OrderItems `json:"items"` ActivityName string `json:"activity_name"` ActivityID int64 `json:"activity_id,omitempty"` PlayType string `json:"play_type,omitempty"` CategoryID int64 `json:"category_id,omitempty"` CategoryName string `json:"category_name,omitempty"` IssueNumber string `json:"issue_number"` IsDraw bool `json:"is_draw"` IsWinner bool `json:"is_winner"` RewardLevel int32 `json:"reward_level"` DrawReceipts []*DrawReceiptInfo `json:"draw_receipts"` } func (s *service) ListOrders(ctx context.Context, userID int64, page, pageSize int) (items []*model.Orders, total int64, err error) { // 查询用户的所有订单,包括商城直购(1)、抽奖票据(2)和系统发放(3) q := s.readDB.Orders.WithContext(ctx).ReadDB().Where(s.readDB.Orders.UserID.Eq(userID)) total, err = q.Count() if err != nil { return nil, 0, err } if page <= 0 { page = 1 } if pageSize <= 0 { pageSize = 20 } if pageSize > 100 { pageSize = 100 } items, err = q.Order(s.readDB.Orders.ID.Desc()).Offset((page - 1) * pageSize).Limit(pageSize).Find() if err != nil { return nil, 0, err } return items, total, nil } // GetOrderWithItems 查询单个订单详情 func (s *service) GetOrderWithItems(ctx context.Context, userID int64, orderID int64) (*OrderWithItems, error) { order, err := s.readDB.Orders.WithContext(ctx).ReadDB().Where(s.readDB.Orders.ID.Eq(orderID), s.readDB.Orders.UserID.Eq(userID)).First() if err != nil { return nil, err } if order == nil { return nil, nil } res := &OrderWithItems{ Orders: order, } items, err := s.readDB.OrderItems.WithContext(ctx).ReadDB().Where(s.readDB.OrderItems.OrderID.Eq(order.ID)).Find() if err != nil { return nil, err } if len(items) > 0 { ids := make(map[int64]struct{}) for _, it := range items { if strings.TrimSpace(it.Title) == "" || strings.TrimSpace(it.Title) == "系统发放奖励" || it.ProductImages == "" || it.ProductImages == "[]" { ids[it.ProductID] = struct{}{} } } if len(ids) > 0 { pidList := make([]int64, 0, len(ids)) for id := range ids { pidList = append(pidList, id) } pros, err2 := s.readDB.Products.WithContext(ctx).ReadDB().Where(s.readDB.Products.ID.In(pidList...)).Find() if err2 != nil { return nil, err2 } pm := make(map[int64]*model.Products, len(pros)) for _, p := range pros { pm[p.ID] = p } for _, it := range items { p := pm[it.ProductID] if p == nil { continue } if strings.TrimSpace(it.Title) == "" || strings.TrimSpace(it.Title) == "系统发放奖励" { it.Title = p.Name } if it.ProductImages == "" || it.ProductImages == "[]" { it.ProductImages = p.ImagesJSON } } } res.Items = items } // 补充开奖信息 logs, _ := s.readDB.ActivityDrawLogs.WithContext(ctx).ReadDB().Where(s.readDB.ActivityDrawLogs.OrderID.Eq(order.ID)).Find() if len(logs) > 0 { res.IsDraw = true // 取第一条记录的信息 log := logs[0] res.IsWinner = log.IsWinner == 1 res.RewardLevel = log.Level issue, _ := s.readDB.ActivityIssues.WithContext(ctx).ReadDB().Where(s.readDB.ActivityIssues.ID.Eq(log.IssueID)).First() if issue != nil { res.IssueNumber = issue.IssueNumber act, _ := s.readDB.Activities.WithContext(ctx).ReadDB().Where(s.readDB.Activities.ID.Eq(issue.ActivityID)).First() if act != nil { res.ActivityName = act.Name res.ActivityID = act.ID res.PlayType = act.PlayType res.CategoryID = act.ActivityCategoryID if act.ActivityCategoryID > 0 { cat, _ := s.readDB.ActivityCategories.WithContext(ctx).ReadDB().Where(s.readDB.ActivityCategories.ID.Eq(act.ActivityCategoryID)).First() if cat != nil { res.CategoryName = cat.Name } } } } // 查询抽奖凭据 drawLogIDs := make([]int64, len(logs)) drawLogIDToIndex := make(map[int64]int) for i, lg := range logs { drawLogIDs[i] = lg.ID drawLogIDToIndex[lg.ID] = i + 1 } receipts, _ := s.readDB.ActivityDrawReceipts.WithContext(ctx).ReadDB().Where(s.readDB.ActivityDrawReceipts.DrawLogID.In(drawLogIDs...)).Find() if len(receipts) > 0 { res.DrawReceipts = make([]*DrawReceiptInfo, 0, len(receipts)) for _, r := range receipts { drawIndex := drawLogIDToIndex[r.DrawLogID] // 找到对应的 reward_id var rewardID int64 for _, lg := range logs { if lg.ID == r.DrawLogID { rewardID = lg.RewardID break } } res.DrawReceipts = append(res.DrawReceipts, &DrawReceiptInfo{ DrawLogID: r.DrawLogID, RewardID: rewardID, DrawIndex: drawIndex, AlgoVersion: r.AlgoVersion, RoundID: r.RoundID, DrawID: r.DrawID, ClientID: r.ClientID, Timestamp: r.Timestamp, ServerSeedHash: r.ServerSeedHash, ServerSubSeed: r.ServerSubSeed, ClientSeed: r.ClientSeed, Nonce: r.Nonce, ItemsRoot: r.ItemsRoot, WeightsTotal: r.WeightsTotal, SelectedIndex: r.SelectedIndex, RandProof: r.RandProof, Signature: r.Signature, ItemsSnapshot: r.ItemsSnapshot, }) } } } return res, nil } // ListOrdersWithItems 查询用户的订单列表,包含订单项详情 func (s *service) ListOrdersWithItems(ctx context.Context, userID int64, status int32, isConsumed *int32, page, pageSize int) (items []*OrderWithItems, total int64, err error) { // 查询用户的所有订单,包括商城直购(1)、抽奖票据(2)和系统发放(3) q := s.readDB.Orders.WithContext(ctx).ReadDB().Where(s.readDB.Orders.UserID.Eq(userID)) if status > 0 { q = q.Where(s.readDB.Orders.Status.Eq(status)) } if isConsumed != nil { q = q.Where(s.readDB.Orders.IsConsumed.Eq(*isConsumed)) } total, err = q.Count() if err != nil { return nil, 0, err } if page <= 0 { page = 1 } if pageSize <= 0 { pageSize = 20 } if pageSize > 100 { pageSize = 100 } // 查询订单列表 orders, err := q.Order(s.readDB.Orders.ID.Desc()).Offset((page - 1) * pageSize).Limit(pageSize).Find() if err != nil { return nil, 0, err } // 构建订单ID列表 orderIDs := make([]int64, len(orders)) for i, order := range orders { orderIDs[i] = order.ID } var allItems []*model.OrderItems if len(orderIDs) > 0 { allItems, err = s.readDB.OrderItems.WithContext(ctx).ReadDB().Where(s.readDB.OrderItems.OrderID.In(orderIDs...)).Find() if err != nil { return nil, 0, err } ids := make(map[int64]struct{}) for _, it := range allItems { if strings.TrimSpace(it.Title) == "" || strings.TrimSpace(it.Title) == "系统发放奖励" || it.ProductImages == "" || it.ProductImages == "[]" { ids[it.ProductID] = struct{}{} } } if len(ids) > 0 { pidList := make([]int64, 0, len(ids)) for id := range ids { pidList = append(pidList, id) } pros, err2 := s.readDB.Products.WithContext(ctx).ReadDB().Where(s.readDB.Products.ID.In(pidList...)).Find() if err2 != nil { return nil, 0, err2 } pm := make(map[int64]*model.Products, len(pros)) for _, p := range pros { pm[p.ID] = p } for _, it := range allItems { p := pm[it.ProductID] if p == nil { continue } if strings.TrimSpace(it.Title) == "" || strings.TrimSpace(it.Title) == "系统发放奖励" { it.Title = p.Name } if it.ProductImages == "" || it.ProductImages == "[]" { it.ProductImages = p.ImagesJSON } } } } // 构建订单ID到订单项的映射 itemsMap := make(map[int64][]*model.OrderItems) for _, item := range allItems { itemsMap[item.OrderID] = append(itemsMap[item.OrderID], item) } // 批量查询活动开奖信息 drawLogsListMap := make(map[int64][]*model.ActivityDrawLogs) // orderID -> logs activityMap := make(map[int64]*model.Activities) issueMap := make(map[int64]*model.ActivityIssues) var allDrawLogIDs []int64 if len(orderIDs) > 0 { logs, _ := s.readDB.ActivityDrawLogs.WithContext(ctx).ReadDB().Where(s.readDB.ActivityDrawLogs.OrderID.In(orderIDs...)).Find() var issueIDs []int64 for _, log := range logs { drawLogsListMap[log.OrderID] = append(drawLogsListMap[log.OrderID], log) issueIDs = append(issueIDs, log.IssueID) allDrawLogIDs = append(allDrawLogIDs, log.ID) } if len(issueIDs) > 0 { issues, _ := s.readDB.ActivityIssues.WithContext(ctx).ReadDB().Where(s.readDB.ActivityIssues.ID.In(issueIDs...)).Find() var activityIDs []int64 for _, issue := range issues { issueMap[issue.ID] = issue activityIDs = append(activityIDs, issue.ActivityID) } if len(activityIDs) > 0 { activities, _ := s.readDB.Activities.WithContext(ctx).ReadDB().Where(s.readDB.Activities.ID.In(activityIDs...)).Find() for _, act := range activities { activityMap[act.ID] = act } } } // 为没有开奖记录的抽奖订单补充查询活动信息 var extraActIDs []int64 for _, order := range orders { if order.SourceType == 2 { if _, hasLogs := drawLogsListMap[order.ID]; !hasLogs { actID := parseActivityIDFromRemark(order.Remark) if actID > 0 { if _, exists := activityMap[actID]; !exists { extraActIDs = append(extraActIDs, actID) } } } } } if len(extraActIDs) > 0 { extraActs, _ := s.readDB.Activities.WithContext(ctx).ReadDB().Where(s.readDB.Activities.ID.In(extraActIDs...)).Find() for _, act := range extraActs { activityMap[act.ID] = act } } } // 批量查询活动分类信息 categoryMap := make(map[int64]*model.ActivityCategories) var categoryIDs []int64 for _, act := range activityMap { if act.ActivityCategoryID > 0 { categoryIDs = append(categoryIDs, act.ActivityCategoryID) } } if len(categoryIDs) > 0 { categories, _ := s.readDB.ActivityCategories.WithContext(ctx).ReadDB().Where(s.readDB.ActivityCategories.ID.In(categoryIDs...)).Find() for _, cat := range categories { categoryMap[cat.ID] = cat } } // 批量查询抽奖凭据 receiptsMap := make(map[int64]*model.ActivityDrawReceipts) // drawLogID -> receipt if len(allDrawLogIDs) > 0 { receipts, _ := s.readDB.ActivityDrawReceipts.WithContext(ctx).ReadDB().Where(s.readDB.ActivityDrawReceipts.DrawLogID.In(allDrawLogIDs...)).Find() for _, r := range receipts { receiptsMap[r.DrawLogID] = r } } // 构建返回结果 items = make([]*OrderWithItems, len(orders)) for i, order := range orders { items[i] = &OrderWithItems{ Orders: order, Items: itemsMap[order.ID], } if logs, ok := drawLogsListMap[order.ID]; ok && len(logs) > 0 { items[i].IsDraw = true // 取第一条记录的基本信息 log := logs[0] items[i].IsWinner = log.IsWinner == 1 items[i].RewardLevel = log.Level if issue, ok := issueMap[log.IssueID]; ok { items[i].IssueNumber = issue.IssueNumber if act, ok := activityMap[issue.ActivityID]; ok { items[i].ActivityName = act.Name items[i].ActivityID = act.ID items[i].PlayType = act.PlayType items[i].CategoryID = act.ActivityCategoryID if cat, ok := categoryMap[act.ActivityCategoryID]; ok { items[i].CategoryName = cat.Name } } } // 填充抽奖凭据 for idx, lg := range logs { if r, ok := receiptsMap[lg.ID]; ok { items[i].DrawReceipts = append(items[i].DrawReceipts, &DrawReceiptInfo{ DrawLogID: r.DrawLogID, RewardID: lg.RewardID, DrawIndex: idx + 1, AlgoVersion: r.AlgoVersion, RoundID: r.RoundID, DrawID: r.DrawID, ClientID: r.ClientID, Timestamp: r.Timestamp, ServerSeedHash: r.ServerSeedHash, ServerSubSeed: r.ServerSubSeed, ClientSeed: r.ClientSeed, Nonce: r.Nonce, ItemsRoot: r.ItemsRoot, WeightsTotal: r.WeightsTotal, SelectedIndex: r.SelectedIndex, RandProof: r.RandProof, Signature: r.Signature, ItemsSnapshot: r.ItemsSnapshot, }) } } } else if order.SourceType == 2 { // 抽奖订单但没有开奖记录(如对对碰支付后还没玩),从备注解析活动信息 actID := parseActivityIDFromRemark(order.Remark) if actID > 0 { if act, ok := activityMap[actID]; ok { items[i].ActivityName = act.Name items[i].ActivityID = act.ID items[i].PlayType = act.PlayType items[i].CategoryID = act.ActivityCategoryID if cat, ok := categoryMap[act.ActivityCategoryID]; ok { items[i].CategoryName = cat.Name } } } } } return items, total, nil } // parseActivityIDFromRemark 从订单备注解析活动ID func parseActivityIDFromRemark(remark string) int64 { if remark == "" { return 0 } parts := strings.Split(remark, "|") for _, p := range parts { if strings.HasPrefix(p, "lottery:activity:") { idStr := p[len("lottery:activity:"):] var n int64 for i := 0; i < len(idStr); i++ { c := idStr[i] if c < '0' || c > '9' { break } n = n*10 + int64(c-'0') } return n } } return 0 }