来源:hgdjnet-server 9bfbafe10d417deea959bd53117bc88a10e60a83 项目身份 HKDJ / JHA-216,独立官网仓库,Jenkins 守卫键 hkdjnet 测试目录 /server/hkdj_net,App/Web API 30331/30332 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
166 lines
5.4 KiB
Go
166 lines
5.4 KiB
Go
package srv_viporder
|
|
|
|
import (
|
|
"context"
|
|
"hkdj-web-server/pub/common/srvdef"
|
|
"hkdj-web-server/pub/common/utils"
|
|
"hkdj-web-server/pub/models/vipordermod"
|
|
"hkdj-web-server/pub/pubdef"
|
|
"strings"
|
|
"time"
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
)
|
|
|
|
// RechargeSummaryReq 充值汇总请求,时间按北京时间解析。
|
|
type RechargeSummaryReq struct {
|
|
StartTime string `form:"startTime" json:"startTime"` // 开始时间,北京时间:2006-01-02 / 2006-01-02 15:04:05 / RFC3339
|
|
EndTime string `form:"endTime" json:"endTime"` // 结束时间,北京时间:2006-01-02 / 2006-01-02 15:04:05 / RFC3339
|
|
}
|
|
|
|
// RechargeSummaryResp 充值汇总结果,金额单位为分。
|
|
type RechargeSummaryResp struct {
|
|
StartTime string `json:"startTime"` // 实际统计开始时间(北京时间)
|
|
EndTime string `json:"endTime"` // 实际统计结束时间(北京时间)
|
|
OrderCount int64 `json:"orderCount"` // 成功订单数
|
|
TotalAmount int64 `json:"totalAmount"` // 成功订单金额合计(分)
|
|
VipOrderCount int64 `json:"vipOrderCount"` // 会员成功订单数
|
|
VipAmount int64 `json:"vipAmount"` // 会员成功订单金额(分)
|
|
CoinOrderCount int64 `json:"coinOrderCount"` // 金币成功订单数
|
|
CoinAmount int64 `json:"coinAmount"` // 金币成功订单金额(分)
|
|
UserCount int64 `json:"userCount"` // 成功充值用户数(去重)
|
|
}
|
|
|
|
type rechargeSummaryAgg struct {
|
|
OrderCount int64 `bson:"orderCount"`
|
|
TotalAmount int64 `bson:"totalAmount"`
|
|
VipOrderCount int64 `bson:"vipOrderCount"`
|
|
VipAmount int64 `bson:"vipAmount"`
|
|
CoinOrderCount int64 `bson:"coinOrderCount"`
|
|
CoinAmount int64 `bson:"coinAmount"`
|
|
UserCount int64 `bson:"userCount"`
|
|
}
|
|
|
|
// GetRechargeSummary 按支付完成时间汇总成功充值订单。
|
|
func GetRechargeSummary(ctx context.Context, req *RechargeSummaryReq) (*RechargeSummaryResp, *srvdef.SrvErr) {
|
|
if req == nil {
|
|
return nil, srvdef.New(srvdef.ErrParamError, "参数不能为空")
|
|
}
|
|
start, end, err := parseBeijingTimeRange(req.StartTime, req.EndTime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp := &RechargeSummaryResp{
|
|
StartTime: start.In(utils.LocBJ).Format(pubdef.TimeISO),
|
|
EndTime: end.In(utils.LocBJ).Format(pubdef.TimeISO),
|
|
}
|
|
list := make([]rechargeSummaryAgg, 0)
|
|
if serr := vipordermod.AggregateVipOrder(ctx, &list, rechargeSummaryPipeline(start, end)); serr != nil {
|
|
return nil, serr
|
|
}
|
|
if len(list) == 0 {
|
|
return resp, nil
|
|
}
|
|
item := list[0]
|
|
resp.OrderCount = item.OrderCount
|
|
resp.TotalAmount = item.TotalAmount
|
|
resp.VipOrderCount = item.VipOrderCount
|
|
resp.VipAmount = item.VipAmount
|
|
resp.CoinOrderCount = item.CoinOrderCount
|
|
resp.CoinAmount = item.CoinAmount
|
|
resp.UserCount = item.UserCount
|
|
return resp, nil
|
|
}
|
|
|
|
func rechargeSummaryPipeline(start, end time.Time) []bson.M {
|
|
vipType := bson.M{"$eq": bson.A{bson.M{"$ifNull": bson.A{"$" + vipordermod.FN.VipOrder.OrderType, vipordermod.OrderTypeVip}}, vipordermod.OrderTypeVip}}
|
|
return []bson.M{
|
|
{
|
|
"$match": bson.M{
|
|
vipordermod.FN.VipOrder.Status: pubdef.VipOrderSuccess,
|
|
vipordermod.FN.VipOrder.FinishedAt: bson.M{
|
|
"$gte": start,
|
|
"$lte": end,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"$group": bson.M{
|
|
"_id": nil,
|
|
"orderCount": bson.M{"$sum": 1},
|
|
"totalAmount": bson.M{"$sum": "$" + vipordermod.FN.VipOrder.Amount},
|
|
"vipOrderCount": bson.M{"$sum": bson.M{"$cond": bson.A{vipType, 1, 0}}},
|
|
"vipAmount": bson.M{"$sum": bson.M{"$cond": bson.A{
|
|
vipType,
|
|
"$" + vipordermod.FN.VipOrder.Amount,
|
|
0,
|
|
}}},
|
|
"coinOrderCount": bson.M{"$sum": bson.M{"$cond": bson.A{
|
|
bson.M{"$eq": bson.A{"$" + vipordermod.FN.VipOrder.OrderType, vipordermod.OrderTypeCoin}},
|
|
1,
|
|
0,
|
|
}}},
|
|
"coinAmount": bson.M{"$sum": bson.M{"$cond": bson.A{
|
|
bson.M{"$eq": bson.A{"$" + vipordermod.FN.VipOrder.OrderType, vipordermod.OrderTypeCoin}},
|
|
"$" + vipordermod.FN.VipOrder.Amount,
|
|
0,
|
|
}}},
|
|
"userIds": bson.M{"$addToSet": "$" + vipordermod.FN.VipOrder.UserId},
|
|
},
|
|
},
|
|
{
|
|
"$project": bson.M{
|
|
"_id": 0,
|
|
"orderCount": 1,
|
|
"totalAmount": 1,
|
|
"vipOrderCount": 1,
|
|
"vipAmount": 1,
|
|
"coinOrderCount": 1,
|
|
"coinAmount": 1,
|
|
"userCount": bson.M{"$size": "$userIds"},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func parseBeijingTimeRange(startRaw, endRaw string) (time.Time, time.Time, *srvdef.SrvErr) {
|
|
start, ok := parseBeijingTime(startRaw, true)
|
|
if !ok {
|
|
return time.Time{}, time.Time{}, srvdef.New(srvdef.ErrParamError, "开始时间无效")
|
|
}
|
|
end, ok := parseBeijingTime(endRaw, false)
|
|
if !ok {
|
|
return time.Time{}, time.Time{}, srvdef.New(srvdef.ErrParamError, "结束时间无效")
|
|
}
|
|
if end.Before(start) {
|
|
return time.Time{}, time.Time{}, srvdef.New(srvdef.ErrParamError, "结束时间不能早于开始时间")
|
|
}
|
|
return start, end, nil
|
|
}
|
|
|
|
func parseBeijingTime(raw string, isStart bool) (time.Time, bool) {
|
|
raw = strings.TrimSpace(raw)
|
|
if raw == "" {
|
|
return time.Time{}, false
|
|
}
|
|
layouts := []string{
|
|
pubdef.TimeISO,
|
|
time.RFC3339,
|
|
time.RFC3339Nano,
|
|
"2006-01-02 15:04:05 -0700 MST",
|
|
"2006-01-02 15:04:05.999999999 -0700 MST",
|
|
}
|
|
for _, layout := range layouts {
|
|
if t, err := time.ParseInLocation(layout, raw, utils.LocBJ); err == nil {
|
|
return t, true
|
|
}
|
|
}
|
|
if t, err := time.ParseInLocation(pubdef.DateISO, raw, utils.LocBJ); err == nil {
|
|
if isStart {
|
|
return utils.BeginOfTime(t), true
|
|
}
|
|
return utils.EndOfTime(t), true
|
|
}
|
|
return time.Time{}, false
|
|
}
|