2026-03-07 22:58:43 +08:00
package handler
import (
"context"
"encoding/json"
"net/http"
"strconv"
"strings"
"sync"
"time"
"soul-api/internal/database"
"soul-api/internal/model"
"soul-api/internal/wechat"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// OrdersList GET /api/orders( 带用户昵称/头像/手机号,分销佣金按配置比例计算;支持分页 page、pageSize, 筛选 status, 搜索 search)
func OrdersList ( c * gin . Context ) {
db := database . DB ( )
page , _ := strconv . Atoi ( c . DefaultQuery ( "page" , "1" ) )
pageSize , _ := strconv . Atoi ( c . DefaultQuery ( "pageSize" , "10" ) )
statusFilter := c . Query ( "status" )
search := strings . TrimSpace ( c . Query ( "search" ) )
if page < 1 {
page = 1
}
if pageSize < 1 || pageSize > 100 {
pageSize = 10
}
// 预加载 referral_config, 避免订单循环内 N+1 查询
var refCfgRow model . SystemConfig
refCfg := ( * model . SystemConfig ) ( nil )
if err := db . Where ( "config_key = ?" , "referral_config" ) . First ( & refCfgRow ) . Error ; err == nil {
refCfg = & refCfgRow
}
// 构建带筛选的查询( count 与 list 共用条件)
applyOrdersFilter := func ( q * gorm . DB ) * gorm . DB {
if statusFilter != "" && statusFilter != "all" {
if statusFilter == "completed" {
q = q . Where ( "status IN ?" , [ ] string { "paid" , "completed" } )
} else {
q = q . Where ( "status = ?" , statusFilter )
}
}
if search != "" {
pattern := "%" + search + "%"
q = q . Where ( "order_sn LIKE ? OR id LIKE ? OR user_id IN (SELECT id FROM users WHERE COALESCE(nickname,'') LIKE ? OR COALESCE(phone,'') LIKE ? OR id LIKE ?)" ,
pattern , pattern , pattern , pattern , pattern )
}
return q
}
var total int64
var totalRevenue , todayRevenue float64
var orders [ ] model . Order
var ordersErr error
var wg sync . WaitGroup
// 并行: count、营收统计、订单列表
wg . Add ( 3 )
go func ( ) {
defer wg . Done ( )
applyOrdersFilter ( db . Model ( & model . Order { } ) ) . Count ( & total )
} ( )
go func ( ) {
defer wg . Done ( )
db . Model ( & model . Order { } ) . Select ( "COALESCE(SUM(amount), 0)" ) .
Where ( "status IN ?" , [ ] string { "paid" , "completed" } ) . Scan ( & totalRevenue )
todayStart := time . Now ( ) . Truncate ( 24 * time . Hour )
todayEnd := todayStart . Add ( 24 * time . Hour )
db . Model ( & model . Order { } ) . Select ( "COALESCE(SUM(amount), 0)" ) .
Where ( "status IN ? AND created_at >= ? AND created_at < ?" , [ ] string { "paid" , "completed" } , todayStart , todayEnd ) .
Scan ( & todayRevenue )
} ( )
go func ( ) {
defer wg . Done ( )
query := applyOrdersFilter ( db . Model ( & model . Order { } ) )
ordersErr = query . Order ( "created_at DESC" ) .
Offset ( ( page - 1 ) * pageSize ) .
Limit ( pageSize ) .
Find ( & orders ) . Error
} ( )
wg . Wait ( )
if ordersErr != nil {
c . JSON ( http . StatusOK , gin . H { "success" : false , "error" : ordersErr . Error ( ) , "orders" : [ ] interface { } { } , "total" : 0 } )
return
}
totalPages := int ( total ) / pageSize
if int ( total ) % pageSize > 0 {
totalPages ++
}
if len ( orders ) == 0 {
c . JSON ( http . StatusOK , gin . H {
"success" : true , "orders" : [ ] interface { } { } ,
"total" : total , "page" : page , "pageSize" : pageSize , "totalPages" : totalPages ,
} )
return
}
2026-03-17 11:44:36 +08:00
// 收集订单中的 user_id、referrer_id、payer_user_id( 代付人) , 查用户信息
2026-03-07 22:58:43 +08:00
userIDs := make ( map [ string ] bool )
for _ , o := range orders {
if o . UserID != "" {
userIDs [ o . UserID ] = true
}
if o . ReferrerID != nil && * o . ReferrerID != "" {
userIDs [ * o . ReferrerID ] = true
}
2026-03-17 11:44:36 +08:00
if o . PayerUserID != nil && * o . PayerUserID != "" {
userIDs [ * o . PayerUserID ] = true
}
2026-03-07 22:58:43 +08:00
}
ids := make ( [ ] string , 0 , len ( userIDs ) )
for id := range userIDs {
ids = append ( ids , id )
}
var users [ ] model . User
if len ( ids ) > 0 {
db . Where ( "id IN ?" , ids ) . Find ( & users )
}
userMap := make ( map [ string ] * model . User )
for i := range users {
userMap [ users [ i ] . ID ] = & users [ i ]
}
getStr := func ( s * string ) string {
if s == nil || * s == "" {
return ""
}
return * s
}
out := make ( [ ] gin . H , 0 , len ( orders ) )
for _ , o := range orders {
// 序列化订单为基础字段
b , _ := json . Marshal ( o )
var m map [ string ] interface { }
_ = json . Unmarshal ( b , & m )
// 用户信息
if u := userMap [ o . UserID ] ; u != nil {
m [ "userNickname" ] = getStr ( u . Nickname )
m [ "userPhone" ] = getStr ( u . Phone )
m [ "userAvatar" ] = getStr ( u . Avatar )
} else {
m [ "userNickname" ] = ""
m [ "userPhone" ] = ""
m [ "userAvatar" ] = ""
}
// 推荐人信息
if o . ReferrerID != nil && * o . ReferrerID != "" {
if u := userMap [ * o . ReferrerID ] ; u != nil {
m [ "referrerNickname" ] = getStr ( u . Nickname )
m [ "referrerCode" ] = getStr ( u . ReferralCode )
}
}
2026-03-17 11:44:36 +08:00
// 代付人信息(实际付款人)
if o . PayerUserID != nil && * o . PayerUserID != "" {
if u := userMap [ * o . PayerUserID ] ; u != nil {
m [ "payerNickname" ] = getStr ( u . Nickname )
} else {
m [ "payerNickname" ] = ""
}
}
2026-03-07 22:58:43 +08:00
// 分销佣金:仅对已支付且存在推荐人的订单,按 computeOrderCommission( 会员 20%/10%,内容 90%)
status := getStr ( o . Status )
if status == "paid" && o . ReferrerID != nil && * o . ReferrerID != "" {
var refUser * model . User
if u := userMap [ * o . ReferrerID ] ; u != nil {
refUser = u
}
m [ "referrerEarnings" ] = computeOrderCommission ( db , & o , refUser , refCfg )
} else {
m [ "referrerEarnings" ] = nil
}
out = append ( out , m )
}
c . JSON ( http . StatusOK , gin . H {
"success" : true , "orders" : out ,
"total" : total , "page" : page , "pageSize" : pageSize , "totalPages" : totalPages ,
"totalRevenue" : totalRevenue , "todayRevenue" : todayRevenue ,
} )
}
// MiniprogramOrders GET /api/miniprogram/orders 小程序-当前用户订单列表(按 userId 过滤,返回 data)
func MiniprogramOrders ( c * gin . Context ) {
userID := c . Query ( "userId" )
if userID == "" {
c . JSON ( http . StatusOK , gin . H { "success" : true , "data" : [ ] interface { } { } } )
return
}
db := database . DB ( )
var orders [ ] model . Order
if err := db . Where ( "user_id = ?" , userID ) . Order ( "created_at DESC" ) . Find ( & orders ) . Error ; err != nil {
c . JSON ( http . StatusOK , gin . H { "success" : true , "data" : [ ] interface { } { } } )
return
}
out := make ( [ ] gin . H , 0 , len ( orders ) )
for _ , o := range orders {
desc := ""
if o . Description != nil {
desc = * o . Description
}
productID := ""
if o . ProductID != nil {
productID = * o . ProductID
}
status := "created"
if o . Status != nil {
status = * o . Status
}
out = append ( out , gin . H {
"id" : o . ID , "order_sn" : o . OrderSN , "user_id" : o . UserID ,
"product_id" : productID , "product_type" : o . ProductType ,
"product_name" : desc , "section_id" : productID ,
"amount" : o . Amount , "status" : status ,
"created_at" : o . CreatedAt ,
} )
}
c . JSON ( http . StatusOK , gin . H { "success" : true , "data" : out } )
}
// AdminOrderRefund PUT /api/admin/orders/refund 管理端-订单退款(仅支持已支付订单,调用微信支付退款)
func AdminOrderRefund ( c * gin . Context ) {
var req struct {
OrderSn string ` json:"orderSn" binding:"required" `
Reason string ` json:"reason" `
}
if err := c . ShouldBindJSON ( & req ) ; err != nil {
c . JSON ( http . StatusOK , gin . H { "success" : false , "error" : "缺少订单号" } )
return
}
db := database . DB ( )
var order model . Order
if err := db . Where ( "order_sn = ?" , req . OrderSn ) . First ( & order ) . Error ; err != nil {
c . JSON ( http . StatusOK , gin . H { "success" : false , "error" : "订单不存在" } )
return
}
status := ""
if order . Status != nil {
status = * order . Status
}
if status != "paid" && status != "completed" {
c . JSON ( http . StatusOK , gin . H { "success" : false , "error" : "仅支持已支付订单退款" } )
return
}
transactionID := ""
if order . TransactionID != nil {
transactionID = * order . TransactionID
}
if transactionID == "" {
c . JSON ( http . StatusOK , gin . H { "success" : false , "error" : "订单缺少微信支付单号,无法退款" } )
return
}
totalCents := int ( order . Amount * 100 )
if totalCents < 1 {
totalCents = 1
}
if err := wechat . RefundOrder ( context . Background ( ) , order . OrderSN , transactionID , totalCents , req . Reason ) ; err != nil {
c . JSON ( http . StatusOK , gin . H { "success" : false , "error" : "微信退款失败: " + err . Error ( ) } )
return
}
refunded := "refunded"
updates := map [ string ] interface { } { "status" : refunded }
if req . Reason != "" {
updates [ "refund_reason" ] = req . Reason
}
if err := db . Model ( & order ) . Updates ( updates ) . Error ; err != nil {
c . JSON ( http . StatusOK , gin . H { "success" : false , "error" : "退款成功但更新订单状态失败: " + err . Error ( ) } )
return
}
2026-03-18 20:33:50 +08:00
// 代付批量订单退款:同步更新 gift_pay_requests 状态,避免小程序仍可分享/领取
if order . GiftPayRequestID != nil && * order . GiftPayRequestID != "" {
_ = db . Model ( & model . GiftPayRequest { } ) .
Where ( "id = ?" , * order . GiftPayRequestID ) .
Updates ( map [ string ] interface { } { "status" : "refunded" } ) . Error
}
2026-03-07 22:58:43 +08:00
c . JSON ( http . StatusOK , gin . H { "success" : true , "message" : "退款成功" } )
}