bindbox-game/internal/api/admin/pay_orders_admin.go
邹方成 16e2ede037 feat: 新增订单列表筛选条件与活动信息展示
refactor(orders): 重构订单列表查询逻辑,支持按消耗状态筛选
feat(orders): 订单列表返回新增活动分类与玩法类型信息
fix(orders): 修复订单支付时间空指针问题
docs(swagger): 更新订单相关接口文档
test(matching): 添加对对碰奖励匹配测试用例
chore: 清理无用脚本文件
2025-12-22 15:15:18 +08:00

805 lines
27 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}
}
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
}
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 != nil && !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,
}
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"`
}
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 != nil && !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
}
}
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})
}
}