package coinser import ( "context" "time" "videoweb/app/api/proto" "videoweb/pub/common/response" "videoweb/pub/common/srvdef" "videoweb/pub/common/utils" "videoweb/pub/models/cointransmod" "videoweb/pub/pubdef" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" ) const ( // coinTransDefaultPageSize 金币余额明细默认每页数量 coinTransDefaultPageSize int64 = 10 // coinTransMaxPageSize 金币余额明细最大每页数量,避免单次请求拉取过多流水 coinTransMaxPageSize int64 = 100 ) var ( // coinTransListProjection 余额明细只读取列表展示需要的字段,降低Mongo读取成本 coinTransListProjection = bson.D{ {Key: cointransmod.FN.CoinTrans.Id, Value: 1}, {Key: cointransmod.FN.CoinTrans.FlowDir, Value: 1}, {Key: cointransmod.FN.CoinTrans.TranType, Value: 1}, {Key: cointransmod.FN.CoinTrans.Amount, Value: 1}, {Key: cointransmod.FN.CoinTrans.BalanceBefore, Value: 1}, {Key: cointransmod.FN.CoinTrans.BalanceAfter, Value: 1}, {Key: cointransmod.FN.CoinTrans.OrderNo, Value: 1}, {Key: cointransmod.FN.CoinTrans.RelatedId, Value: 1}, {Key: cointransmod.FN.CoinTrans.Remark, Value: 1}, {Key: cointransmod.FN.CoinTrans.CreatedAt, Value: 1}, } // coinTransListSort 按交易时间倒序展示余额明细 coinTransListSort = bson.D{ {Key: cointransmod.FN.CoinTrans.CreatedAt, Value: -1}, } // coinTransListHint 匹配 coin_trans 的 userId+createdAt 复合索引 coinTransListHint = bson.D{ {Key: cointransmod.FN.CoinTrans.UserId, Value: 1}, {Key: cointransmod.FN.CoinTrans.CreatedAt, Value: -1}, } ) // GetCoinTransList 查询当前用户金币余额明细 // // 只查询当前登录用户的流水,按交易时间倒序分页,pageSize 最大限制为100 func GetCoinTransList(ctx context.Context, userId int64, page, pageSize int64) (*response.BasePageResp, *srvdef.SrvErr) { if userId <= 0 { return nil, srvdef.New(srvdef.ErrUserNotExsit, "", "") } page, pageSize = normalizeCoinTransPage(page, pageSize) filter := bson.M{ cointransmod.FN.CoinTrans.UserId: userId, } total, err := cointransmod.CountCoinTrans(ctx, filter) if err != nil { return nil, err } if total <= 0 { return &response.BasePageResp{ TotalCount: 0, PageSize: int(pageSize), PageNum: int(page), List: make([]*proto.CoinTransListItemResp, 0), }, nil } list, err1 := cointransmod.FindManyCoinTrans(ctx, filter, options.Find(). SetProjection(coinTransListProjection). SetSort(coinTransListSort). SetHint(coinTransListHint). SetSkip((page-1)*pageSize). SetLimit(pageSize)) if err1 != nil { return nil, err1 } resp := make([]*proto.CoinTransListItemResp, 0, len(list)) for _, item := range list { resp = append(resp, coinTransToListItemResp(item)) } return &response.BasePageResp{ TotalCount: total, PageSize: int(pageSize), PageNum: int(page), List: resp, }, nil } // normalizeCoinTransPage 规范化金币流水分页参数 // // page 小于等于0时按第1页处理;pageSize 未传时使用默认值,超过上限时截断 func normalizeCoinTransPage(page, pageSize int64) (int64, int64) { if page <= 0 { page = 1 } if pageSize <= 0 { pageSize = coinTransDefaultPageSize } if pageSize > coinTransMaxPageSize { pageSize = coinTransMaxPageSize } return page, pageSize } // coinTransToListItemResp 转换金币流水响应项 // // amount 保持数据库中的正数金额,changeAmount 根据收支方向转换为带正负号的变动金额 func coinTransToListItemResp(item *cointransmod.CoinTrans) *proto.CoinTransListItemResp { title := item.Remark if title == "" { title = item.TranType.Desc() } changeAmount := item.Amount if item.FlowDir == cointransmod.FlowOut { changeAmount = -item.Amount } return &proto.CoinTransListItemResp{ Id: item.Id, Title: title, FlowDir: item.FlowDir, FlowDirName: item.FlowDir.Desc(), TranType: item.TranType, TranTypeName: item.TranType.Desc(), Amount: item.Amount, ChangeAmount: changeAmount, BalanceBefore: item.BalanceBefore, BalanceAfter: item.BalanceAfter, OrderNo: item.OrderNo, RelatedId: item.RelatedId, Remark: item.Remark, CreatedAt: formatCoinTransTime(item.CreatedAt), } } func formatCoinTransTime(t time.Time) string { if t.IsZero() { return "" } return t.In(utils.LocBJ).Format(pubdef.TimeISO) }