package admin import ( "fmt" "net/http" "time" "go.uber.org/zap" "bindbox-game/configs" "bindbox-game/internal/code" "bindbox-game/internal/pkg/core" "bindbox-game/internal/pkg/validation" "bindbox-game/internal/pkg/wechat" "bindbox-game/internal/repository/mysql/model" usersvc "bindbox-game/internal/service/user" ) type listPayOrdersRequest struct { Page int `form:"page"` Current int `form:"current"` PageSize int `form:"page_size"` Size int `form:"size"` Status *int32 `form:"status"` SourceType *int32 `form:"source_type"` ExcludeSourceType *int32 `form:"exclude_source_type"` UserID *int64 `form:"user_id"` OrderNo string `form:"order_no"` IsConsumed *int32 `form:"is_consumed"` StartDate string `form:"start_date"` EndDate string `form:"end_date"` PayStart string `form:"pay_start"` PayEnd string `form:"pay_end"` } type listPayOrdersResponse struct { Page int `json:"page"` PageSize int `json:"page_size"` Total int64 `json:"total"` List []map[string]any `json:"list"` } func (h *handler) ListPayOrders() core.HandlerFunc { return func(ctx core.Context) { req := new(listPayOrdersRequest) rsp := new(listPayOrdersResponse) if err := ctx.ShouldBindForm(req); err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) return } if req.Page <= 0 && req.Current > 0 { req.Page = req.Current } if req.PageSize <= 0 && req.Size > 0 { req.PageSize = req.Size } if req.Page <= 0 { req.Page = 1 } if req.PageSize <= 0 { req.PageSize = 20 } if req.PageSize > 100 { req.PageSize = 100 } q := h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB() if req.Status != nil { q = q.Where(h.readDB.Orders.Status.Eq(*req.Status)) } if req.SourceType != nil { q = q.Where(h.readDB.Orders.SourceType.Eq(*req.SourceType)) } if req.ExcludeSourceType != nil { q = q.Not(h.readDB.Orders.SourceType.Eq(*req.ExcludeSourceType)) } if req.UserID != nil { q = q.Where(h.readDB.Orders.UserID.Eq(*req.UserID)) } if req.OrderNo != "" { q = q.Where(h.readDB.Orders.OrderNo.Eq(req.OrderNo)) } if req.IsConsumed != nil { q = q.Where(h.readDB.Orders.IsConsumed.Eq(*req.IsConsumed)) } if req.StartDate != "" { if t, err := time.Parse("2006-01-02", req.StartDate); err == nil { q = q.Where(h.readDB.Orders.CreatedAt.Gte(t)) } } if req.EndDate != "" { if t, err := time.Parse("2006-01-02", req.EndDate); err == nil { t = t.Add(24 * time.Hour).Add(-time.Second) q = q.Where(h.readDB.Orders.CreatedAt.Lte(t)) } } if req.PayStart != "" { if t, err := time.Parse(time.RFC3339, req.PayStart); err == nil { q = q.Where(h.readDB.Orders.PaidAt.Gte(t)) } } if req.PayEnd != "" { if t, err := time.Parse(time.RFC3339, req.PayEnd); err == nil { q = q.Where(h.readDB.Orders.PaidAt.Lte(t)) } } total, err := q.Count() if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 21001, err.Error())) return } rows, err := q.Order(h.readDB.Orders.ID.Desc()).Offset((req.Page - 1) * req.PageSize).Limit(req.PageSize).Find() if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 21002, err.Error())) return } var pointsRate int64 = 1 if cfgRate, _ := h.readDB.SystemConfigs.WithContext(ctx.RequestContext()).Where(h.readDB.SystemConfigs.ConfigKey.Eq("points_exchange_per_cent")).First(); cfgRate != nil { var r int64 _, _ = fmt.Sscanf(cfgRate.ConfigValue, "%d", &r) if r > 0 { pointsRate = r } } // 批量查询优惠券和道具卡信息 userCouponIDs := make([]int64, 0) userItemCardIDs := make([]int64, 0) // 收集订单ID用于查询 OrderCoupons orderIDs := make([]int64, 0, len(rows)) for _, o := range rows { orderIDs = append(orderIDs, o.ID) if o.CouponID > 0 { userCouponIDs = append(userCouponIDs, o.CouponID) } if o.ItemCardID > 0 { userItemCardIDs = append(userItemCardIDs, o.ItemCardID) } } couponMap := make(map[int64]map[string]any) // orderID -> userCouponID -> appliedAmount appliedAmountMap := make(map[int64]map[int64]int64) if len(userCouponIDs) > 0 { userCoupons, _ := h.readDB.UserCoupons.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.UserCoupons.ID.In(userCouponIDs...)).Find() var sysCouponIDs []int64 ucMap := make(map[int64]*model.UserCoupons) for _, uc := range userCoupons { sysCouponIDs = append(sysCouponIDs, uc.CouponID) ucMap[uc.ID] = uc } if len(sysCouponIDs) > 0 { sysCoupons, _ := h.readDB.SystemCoupons.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.SystemCoupons.ID.In(sysCouponIDs...)).Find() scMap := make(map[int64]*model.SystemCoupons) for _, sc := range sysCoupons { scMap[sc.ID] = sc } for id, uc := range ucMap { if sc, ok := scMap[uc.CouponID]; ok { couponMap[id] = map[string]any{ "user_coupon_id": uc.ID, "name": sc.Name, "type": sc.DiscountType, "value": sc.DiscountValue, } } } } // 查询 OrderCoupons 获取实际抵扣金额 if len(orderIDs) > 0 { ocs, _ := h.readDB.OrderCoupons.WithContext(ctx.RequestContext()).ReadDB().Where( h.readDB.OrderCoupons.OrderID.In(orderIDs...), h.readDB.OrderCoupons.UserCouponID.In(userCouponIDs...), ).Find() for _, oc := range ocs { if _, ok := appliedAmountMap[oc.OrderID]; !ok { appliedAmountMap[oc.OrderID] = make(map[int64]int64) } appliedAmountMap[oc.OrderID][oc.UserCouponID] = oc.AppliedAmount } } } itemCardMap := make(map[int64]map[string]any) if len(userItemCardIDs) > 0 { userCards, _ := h.readDB.UserItemCards.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.UserItemCards.ID.In(userItemCardIDs...)).Find() var sysCardIDs []int64 ucMap := make(map[int64]*model.UserItemCards) for _, uc := range userCards { sysCardIDs = append(sysCardIDs, uc.CardID) ucMap[uc.ID] = uc } if len(sysCardIDs) > 0 { sysCards, _ := h.readDB.SystemItemCards.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.SystemItemCards.ID.In(sysCardIDs...)).Find() scMap := make(map[int64]*model.SystemItemCards) for _, sc := range sysCards { scMap[sc.ID] = sc } for id, uc := range ucMap { if sc, ok := scMap[uc.CardID]; ok { itemCardMap[id] = map[string]any{ "user_card_id": uc.ID, "name": sc.Name, "effect_type": sc.EffectType, } } } } } out := make([]map[string]any, 0, len(rows)) for _, o := range rows { ledgers, err := h.readDB.UserPointsLedger.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.UserPointsLedger.RefTable.Eq("orders"), h.readDB.UserPointsLedger.RefID.Eq(o.OrderNo)).Find() if err != nil { h.logger.Error("ListPayOrders fetch ledgers error", zap.Error(err), zap.String("order_no", o.OrderNo)) } var consumePointsSum int64 for _, lg := range ledgers { if lg.Points < 0 { consumePointsSum += -lg.Points } } pa := o.PointsAmount if pa == 0 && consumePointsSum > 0 { pa = consumePointsSum / pointsRate } pu := int64(0) if consumePointsSum > 0 { pu = consumePointsSum } else if pa > 0 { pu = pa * pointsRate } var cInfo map[string]any if baseInfo, ok := couponMap[o.CouponID]; ok { cInfo = make(map[string]any) for k, v := range baseInfo { cInfo[k] = v } // 尝试使用实际抵扣金额 if amMap, ok := appliedAmountMap[o.ID]; ok { if amount, ok2 := amMap[o.CouponID]; ok2 { cInfo["value"] = amount } } } item := map[string]any{ "id": o.ID, "order_no": o.OrderNo, "user_id": o.UserID, "source_type": o.SourceType, "actual_amount": o.ActualAmount, "status": o.Status, "paid_at": func() string { if !o.PaidAt.IsZero() { return o.PaidAt.Format("2006-01-02 15:04:05") } return "" }(), "created_at": o.CreatedAt, "is_consumed": o.IsConsumed, "remark": o.Remark, "points_amount": pa, "points_used": pu, "total_amount": o.TotalAmount, "coupon_info": cInfo, "item_card_info": itemCardMap[o.ItemCardID], } out = append(out, item) } rsp.Page = req.Page rsp.PageSize = req.PageSize rsp.Total = total rsp.List = out ctx.Payload(rsp) } } type getPayOrderResponse struct { Order *model.Orders `json:"order"` Items []*model.OrderItems `json:"items"` Shipments []*model.ShippingRecords `json:"shipments"` Ledgers []*model.UserPointsLedger `json:"ledgers"` User *model.Users `json:"user"` Coupons []*struct { UserCouponID int64 `json:"user_coupon_id"` AppliedAmount int64 `json:"applied_amount"` } `json:"coupons"` Activity *struct { ActivityID int64 `json:"activity_id"` ActivityName string `json:"activity_name"` IssueID int64 `json:"issue_id"` IssueNumber string `json:"issue_number"` IsWinner int32 `json:"is_winner"` Level int32 `json:"level"` RewardID int64 `json:"reward_id"` RewardName string `json:"reward_name"` ProductID int64 `json:"product_id"` Count int64 `json:"count"` ProductPrice int64 `json:"product_price"` PriceDraw int64 `json:"price_draw"` PriceDrawPts int64 `json:"price_draw_points"` } `json:"activity"` Payment *struct { Status int32 `json:"status"` ActualAmount int64 `json:"actual_amount"` PaidAt string `json:"paid_at"` PayPreorderID int64 `json:"pay_preorder_id"` TransactionID string `json:"transaction_id"` PointsAmount int64 `json:"points_amount"` PointsUsed int64 `json:"points_used"` CouponAppliedAmount int64 `json:"coupon_applied_amount"` TotalAmount int64 `json:"total_amount"` } `json:"payment"` Refunds []*struct { RefundNo string `json:"refund_no"` Amount int64 `json:"amount"` Status string `json:"status"` Channel string `json:"channel"` Reason string `json:"reason"` CreatedAt string `json:"created_at"` } `json:"refunds"` RefundableAmount int64 `json:"refundable_amount"` ComputedItems []*struct { Name string `json:"name"` Quantity int64 `json:"quantity"` UnitPrice int64 `json:"unit_price"` Amount int64 `json:"amount"` } `json:"computed_items"` RewardOrder *struct { OrderNo string `json:"order_no"` Status int32 `json:"status"` CreatedAt string `json:"created_at"` } `json:"reward_order"` RewardItems []*struct { Title string `json:"title"` Quantity int64 `json:"quantity"` UnitPrice int64 `json:"unit_price"` Amount int64 `json:"amount"` } `json:"reward_items"` RewardShipments []*model.ShippingRecords `json:"reward_shipments"` DrawReceipts []*usersvc.DrawReceiptInfo `json:"draw_receipts"` CouponInfo map[string]any `json:"coupon_info"` ItemCardInfo map[string]any `json:"item_card_info"` } func (h *handler) GetPayOrderDetail() core.HandlerFunc { return func(ctx core.Context) { rsp := new(getPayOrderResponse) orderNo := ctx.Param("order_no") if orderNo == "" { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "缺少订单号")) return } order, err := h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Orders.OrderNo.Eq(orderNo)).First() if err != nil || order == nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 21003, "订单不存在")) return } items, _ := h.readDB.OrderItems.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.OrderItems.OrderID.Eq(order.ID)).Find() shipments, _ := h.readDB.ShippingRecords.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ShippingRecords.OrderID.Eq(order.ID)).Find() ledgers, _ := h.readDB.UserPointsLedger.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.UserPointsLedger.RefTable.Eq("orders"), h.readDB.UserPointsLedger.RefID.Eq(orderNo)).Find() ocRows, _ := h.readDB.OrderCoupons.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.OrderCoupons.OrderID.Eq(order.ID)).Find() var couponList []*struct { UserCouponID int64 `json:"user_coupon_id"` AppliedAmount int64 `json:"applied_amount"` } var couponAppliedSum int64 for _, r := range ocRows { couponList = append(couponList, &struct { UserCouponID int64 `json:"user_coupon_id"` AppliedAmount int64 `json:"applied_amount"` }{UserCouponID: r.UserCouponID, AppliedAmount: r.AppliedAmount}) couponAppliedSum += r.AppliedAmount } var consumePointsSum int64 for _, lg := range ledgers { if lg.Action == "consume_order" && lg.Points < 0 { consumePointsSum += -lg.Points } } // 每分对应多少积分(默认1),用于展示积分抵扣与活动价格 var pointsRate int64 = 1 if cfgRate, _ := h.readDB.SystemConfigs.WithContext(ctx.RequestContext()).Where(h.readDB.SystemConfigs.ConfigKey.Eq("points_exchange_per_cent")).First(); cfgRate != nil { var r int64 _, _ = fmt.Sscanf(cfgRate.ConfigValue, "%d", &r) if r > 0 { pointsRate = r } } var pointsAmountCents int64 = order.PointsAmount if pointsAmountCents == 0 && consumePointsSum > 0 { pointsAmountCents = consumePointsSum / pointsRate } user, _ := h.readDB.Users.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Users.ID.Eq(order.UserID)).First() var activityID int64 var activityName string var issueID int64 var issueNumber string var isWinner int32 var level int32 var rewardID int64 var rewardName string var rewardProductID int64 var count int64 if order.SourceType == 2 { drawLog, _ := h.readDB.ActivityDrawLogs.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ActivityDrawLogs.OrderID.Eq(order.ID)).First() if drawLog != nil { isWinner = drawLog.IsWinner level = drawLog.Level rewardID = drawLog.RewardID issueID = drawLog.IssueID issue, _ := h.readDB.ActivityIssues.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ActivityIssues.ID.Eq(issueID)).First() if issue != nil { issueNumber = issue.IssueNumber activityID = issue.ActivityID activity, _ := h.readDB.Activities.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Activities.ID.Eq(activityID)).First() if activity != nil { activityName = activity.Name } } if rewardID > 0 { reward, _ := h.readDB.ActivityRewardSettings.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ActivityRewardSettings.ID.Eq(rewardID)).First() if reward != nil { rewardName = reward.Name rewardProductID = reward.ProductID } } } else { remark := order.Remark var aid, iss, cnt int64 p := 0 for i := 0; i <= len(remark); i++ { if i == len(remark) || remark[i] == '|' { seg := remark[p:i] if len(seg) > len("lottery:activity:") && seg[:len("lottery:activity:")] == "lottery:activity:" { var n int64 for j := len("lottery:activity:"); j < len(seg); j++ { c := seg[j] if c < '0' || c > '9' { break } n = n*10 + int64(c-'0') } aid = n } if len(seg) > 6 && seg[:6] == "issue:" { var n2 int64 for j := 6; j < len(seg); j++ { c := seg[j] if c < '0' || c > '9' { break } n2 = n2*10 + int64(c-'0') } iss = n2 } if len(seg) > 6 && seg[:6] == "count:" { var n3 int64 for j := 6; j < len(seg); j++ { c := seg[j] if c < '0' || c > '9' { break } n3 = n3*10 + int64(c-'0') } if n3 <= 0 { n3 = 1 } cnt = n3 } p = i + 1 } } if aid > 0 { activityID = aid } if iss > 0 { issueID = iss } count = cnt if issueID > 0 { issue, _ := h.readDB.ActivityIssues.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ActivityIssues.ID.Eq(issueID)).First() if issue != nil { issueNumber = issue.IssueNumber activityID = issue.ActivityID } } if activityID > 0 { activity, _ := h.readDB.Activities.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Activities.ID.Eq(activityID)).First() if activity != nil { activityName = activity.Name } } } } var productPrice int64 if rewardProductID > 0 { prod, _ := h.readDB.Products.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Products.ID.Eq(rewardProductID)).First() if prod != nil { productPrice = prod.Price } } if count <= 0 { count = func() int64 { remark := order.Remark var n int64 p := 0 for i := 0; i <= len(remark); i++ { if i == len(remark) || remark[i] == '|' { seg := remark[p:i] if len(seg) > 6 && seg[:6] == "count:" { for j := 6; j < len(seg); j++ { c := seg[j] if c < '0' || c > '9' { break } n = n*10 + int64(c-'0') } } p = i + 1 } } if n <= 0 { n = 1 } return n }() } // 使用 pointsRate 展示活动价格(积分) rsp.Activity = &struct { ActivityID int64 `json:"activity_id"` ActivityName string `json:"activity_name"` IssueID int64 `json:"issue_id"` IssueNumber string `json:"issue_number"` IsWinner int32 `json:"is_winner"` Level int32 `json:"level"` RewardID int64 `json:"reward_id"` RewardName string `json:"reward_name"` ProductID int64 `json:"product_id"` Count int64 `json:"count"` ProductPrice int64 `json:"product_price"` PriceDraw int64 `json:"price_draw"` PriceDrawPts int64 `json:"price_draw_points"` }{ ActivityID: activityID, ActivityName: activityName, IssueID: issueID, IssueNumber: issueNumber, IsWinner: isWinner, Level: level, RewardID: rewardID, RewardName: rewardName, ProductID: rewardProductID, Count: count, ProductPrice: productPrice, PriceDraw: order.TotalAmount / func() int64 { if count > 0 { return count } return 1 }(), PriceDrawPts: (order.TotalAmount / func() int64 { if count > 0 { return count } return 1 }()) * pointsRate, } if user != nil { rsp.User = user } rsp.Payment = &struct { Status int32 `json:"status"` ActualAmount int64 `json:"actual_amount"` PaidAt string `json:"paid_at"` PayPreorderID int64 `json:"pay_preorder_id"` TransactionID string `json:"transaction_id"` PointsAmount int64 `json:"points_amount"` PointsUsed int64 `json:"points_used"` CouponAppliedAmount int64 `json:"coupon_applied_amount"` TotalAmount int64 `json:"total_amount"` }{ Status: order.Status, ActualAmount: order.ActualAmount, PaidAt: func() string { if !order.PaidAt.IsZero() { return order.PaidAt.Format("2006-01-02 15:04:05") } return "" }(), PayPreorderID: order.PayPreorderID, TransactionID: "", PointsAmount: pointsAmountCents, PointsUsed: func() int64 { if consumePointsSum > 0 { return consumePointsSum } if pointsAmountCents > 0 { return pointsAmountCents * pointsRate } return 0 }(), CouponAppliedAmount: couponAppliedSum, TotalAmount: order.TotalAmount, } tx, _ := h.readDB.PaymentTransactions.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.PaymentTransactions.OrderNo.Eq(order.OrderNo)).Order(h.readDB.PaymentTransactions.ID.Desc()).First() if tx != nil { rsp.Payment.PaidAt = tx.SuccessTime.Format("2006-01-02 15:04:05") rsp.Payment.TransactionID = tx.TransactionID } var refundedSum int64 var refunds []*struct { RefundNo string `json:"refund_no"` Amount int64 `json:"amount"` Status string `json:"status"` Channel string `json:"channel"` Reason string `json:"reason"` CreatedAt string `json:"created_at"` } prList, _ := h.readDB.PaymentRefunds.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.PaymentRefunds.OrderNo.Eq(order.OrderNo)).Order(h.readDB.PaymentRefunds.ID.Desc()).Find() for _, r := range prList { refundedSum += r.AmountRefund refunds = append(refunds, &struct { RefundNo string `json:"refund_no"` Amount int64 `json:"amount"` Status string `json:"status"` Channel string `json:"channel"` Reason string `json:"reason"` CreatedAt string `json:"created_at"` }{RefundNo: r.RefundNo, Amount: r.AmountRefund, Status: r.Status, Channel: r.Channel, Reason: r.Reason, CreatedAt: r.CreatedAt.Format("2006-01-02 15:04:05")}) } var refundable int64 = order.ActualAmount - refundedSum if refundable < 0 { refundable = 0 } rsp.Refunds = refunds rsp.RefundableAmount = refundable rsp.Order = order rsp.Items = items rsp.Coupons = couponList rsp.Shipments = shipments rsp.Ledgers = ledgers if order.SourceType == 2 { unit := int64(0) if count > 0 { unit = order.TotalAmount / count } name := rewardName if name == "" { name = "抽奖参与" } rsp.ComputedItems = []*struct { Name string `json:"name"` Quantity int64 `json:"quantity"` UnitPrice int64 `json:"unit_price"` Amount int64 `json:"amount"` }{ {Name: name, Quantity: count, UnitPrice: unit, Amount: order.TotalAmount}, } // 合并中奖发放信息:按该订单的全部抽奖日志汇总发放订单项与物流 logsAll, _ := h.readDB.ActivityDrawLogs.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ActivityDrawLogs.OrderID.Eq(order.ID)).Find() var risAll []*struct { Title string `json:"title"` Quantity int64 `json:"quantity"` UnitPrice int64 `json:"unit_price"` Amount int64 `json:"amount"` } var shipsAll []*model.ShippingRecords for _, lg := range logsAll { if lg.RewardID <= 0 { continue } inv, _ := h.readDB.UserInventory.WithContext(ctx.RequestContext()).ReadDB().Where( h.readDB.UserInventory.UserID.Eq(order.UserID), h.readDB.UserInventory.RewardID.Eq(lg.RewardID), ).Order(h.readDB.UserInventory.ID.Desc()).First() if inv == nil || inv.OrderID <= 0 { continue } rg, _ := h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Orders.ID.Eq(inv.OrderID)).First() if rg == nil { continue } if rsp.RewardOrder == nil { rsp.RewardOrder = &struct { OrderNo string `json:"order_no"` Status int32 `json:"status"` CreatedAt string `json:"created_at"` }{ OrderNo: rg.OrderNo, Status: rg.Status, CreatedAt: rg.CreatedAt.Format("2006-01-02 15:04:05"), } } ritems, _ := h.readDB.OrderItems.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.OrderItems.OrderID.Eq(rg.ID)).Find() for _, it := range ritems { risAll = append(risAll, &struct { Title string `json:"title"` Quantity int64 `json:"quantity"` UnitPrice int64 `json:"unit_price"` Amount int64 `json:"amount"` }{ Title: it.Title, Quantity: it.Quantity, UnitPrice: it.Price, Amount: it.TotalAmount, }) } rships, _ := h.readDB.ShippingRecords.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ShippingRecords.OrderID.Eq(rg.ID)).Find() shipsAll = append(shipsAll, rships...) } if len(risAll) > 0 { rsp.RewardItems = risAll } if len(shipsAll) > 0 { rsp.RewardShipments = shipsAll } } // 填充抽奖凭据 Verify Seed Data if order.SourceType == 2 { // Buffer/Lottery orders drawLogs, _ := h.readDB.ActivityDrawLogs.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ActivityDrawLogs.OrderID.Eq(order.ID)).Find() var logIDs []int64 for _, l := range drawLogs { logIDs = append(logIDs, l.ID) } if len(logIDs) > 0 { receipts, _ := h.readDB.ActivityDrawReceipts.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ActivityDrawReceipts.DrawLogID.In(logIDs...)).Find() var drList []*usersvc.DrawReceiptInfo for i, r := range receipts { // Find rewardID from logs if possible, though receipt has DrawLogID var rid int64 for _, l := range drawLogs { if l.ID == r.DrawLogID { rid = l.RewardID break } } drList = append(drList, &usersvc.DrawReceiptInfo{ DrawLogID: r.DrawLogID, RewardID: rid, DrawIndex: i + 1, // approximate 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, }) } rsp.DrawReceipts = drList } } // 补充优惠券和道具卡信息 if order.CouponID > 0 { if uc, _ := h.readDB.UserCoupons.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.UserCoupons.ID.Eq(order.CouponID)).First(); uc != nil { if sc, _ := h.readDB.SystemCoupons.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.SystemCoupons.ID.Eq(uc.CouponID)).First(); sc != nil { val := sc.DiscountValue // 尝试查找实际抵扣金额 // 上面已经查询了 ocRows,直接遍历查找 for _, r := range ocRows { if r.UserCouponID == order.CouponID { val = r.AppliedAmount break } } rsp.CouponInfo = map[string]any{ "user_coupon_id": uc.ID, "name": sc.Name, "type": sc.DiscountType, "value": val, } } } } if order.ItemCardID > 0 { if uc, _ := h.readDB.UserItemCards.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.UserItemCards.ID.Eq(order.ItemCardID)).First(); uc != nil { if sc, _ := h.readDB.SystemItemCards.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.SystemItemCards.ID.Eq(uc.CardID)).First(); sc != nil { rsp.ItemCardInfo = map[string]any{ "user_card_id": uc.ID, "name": sc.Name, "effect_type": sc.EffectType, } } } } ctx.Payload(rsp) } } func (h *handler) UploadMiniAppVirtualShippingForOrder() core.HandlerFunc { return func(ctx core.Context) { orderNo := ctx.Param("order_no") if orderNo == "" { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "缺少订单号")) return } order, err := h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Orders.OrderNo.Eq(orderNo)).First() if err != nil || order == nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 21003, "订单不存在")) return } if order.Status != 2 { ctx.AbortWithError(core.Error(http.StatusBadRequest, 21006, "仅支持已支付订单发货")) return } tx, _ := h.readDB.PaymentTransactions.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.PaymentTransactions.OrderNo.Eq(order.OrderNo)).Order(h.readDB.PaymentTransactions.ID.Desc()).First() if tx == nil || tx.TransactionID == "" { ctx.AbortWithError(core.Error(http.StatusBadRequest, 21004, "未找到支付交易")) return } var itemDesc string xs, _ := h.readDB.OrderItems.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.OrderItems.OrderID.Eq(order.ID)).Find() if len(xs) == 0 { itemDesc = "订单" + order.OrderNo } else { s := "" for i, it := range xs { seg := it.Title + "*" + func(q int64) string { return fmt.Sprintf("%d", q) }(it.Quantity) if i == 0 { s = seg } else { s = s + ", " + seg } } if len(s) > 120 { s = s[:120] } itemDesc = s } pre, _ := h.readDB.PaymentPreorders.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.PaymentPreorders.OrderID.Eq(order.ID)).Order(h.readDB.PaymentPreorders.ID.Desc()).First() payerOpenid := "" if pre != nil { payerOpenid = pre.PayerOpenid } cfg := configs.Get() wxc := &wechat.WechatConfig{AppID: cfg.Wechat.AppID, AppSecret: cfg.Wechat.AppSecret} if err := wechat.UploadVirtualShippingWithFallback(ctx, wxc, tx.TransactionID, order.OrderNo, payerOpenid, itemDesc, time.Now()); err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 21005, err.Error())) return } ctx.Payload(map[string]any{"ok": true}) } } type updateOrderRemarkRequest struct { Remark string `json:"remark"` } type simpleResponse struct { Success bool `json:"success"` } func (h *handler) UpdateOrderRemark() core.HandlerFunc { return func(ctx core.Context) { req := new(updateOrderRemarkRequest) rsp := new(simpleResponse) if err := ctx.ShouldBindJSON(req); err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) return } orderNo := ctx.Param("order_no") if orderNo == "" { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "缺少订单号")) return } order, err := h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Orders.OrderNo.Eq(orderNo)).First() if err != nil || order == nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 21004, "订单不存在")) return } _, err = h.writeDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.ID.Eq(order.ID)).Updates(map[string]any{ h.writeDB.Orders.Remark.ColumnName().String(): req.Remark, h.writeDB.Orders.UpdatedAt.ColumnName().String(): time.Now(), }) if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 21005, err.Error())) return } rsp.Success = true ctx.Payload(rsp) } } func (h *handler) CancelOrder() core.HandlerFunc { return func(ctx core.Context) { orderNo := ctx.Param("order_no") if orderNo == "" { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "缺少订单号")) return } order, err := h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Orders.OrderNo.Eq(orderNo)).First() if err != nil || order == nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 21006, "订单不存在")) return } _, err = h.writeDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.ID.Eq(order.ID), h.readDB.Orders.Status.Eq(1)).Updates(map[string]any{ h.readDB.Orders.Status.ColumnName().String(): 3, h.readDB.Orders.CancelledAt.ColumnName().String(): time.Now(), }) if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 21007, err.Error())) return } ctx.Payload(&simpleResponse{Success: true}) } } func (h *handler) ConsumeOrder() core.HandlerFunc { return func(ctx core.Context) { orderNo := ctx.Param("order_no") if orderNo == "" { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "缺少订单号")) return } order, err := h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Orders.OrderNo.Eq(orderNo)).First() if err != nil || order == nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 21008, "订单不存在")) return } _, err = h.writeDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.ID.Eq(order.ID), h.readDB.Orders.Status.Eq(2)).Updates(map[string]any{ h.readDB.Orders.IsConsumed.ColumnName().String(): 1, }) if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 21009, err.Error())) return } ctx.Payload(&simpleResponse{Success: true}) } }