feat(backend): 对齐业务服务额度与内容治理口径
仪表盘待办与穿搭概况改为公开/私密/已封禁口径,移除恒为零的待审核统计;补齐额度凭证来源 source=4 与流水 change_type=7 枚举及订单商品类型、额度有效期快照字段;管理员发放与回收额度凭证在同一事务内写入人工调整流水,回收流水使用独立 order_no 避免撞唯一索引,并增加凭证归属校验;对齐 Quota 配置免费额度值,同步更新 README 与配置说明。
This commit is contained in:
@@ -33,14 +33,19 @@ const (
|
||||
QuotaTypeClosetShare int8 = 4 // 衣柜分享
|
||||
|
||||
// 额度凭证来源(quota_grants.source)
|
||||
QuotaGrantSourceMember int8 = 1 // 会员包(跟随 VIP 有效期)
|
||||
QuotaGrantSourceMember int8 = 1 // 会员包(跟随 VIP 有效期,续期自动延长)
|
||||
QuotaGrantSourceAddon int8 = 2 // 加量包(独立有效期)
|
||||
QuotaGrantSourceAdReward int8 = 3 // 广告激励(当天有效)
|
||||
QuotaGrantSourceTrial int8 = 4 // 体验会员赠送(新用户注册;固定有效期=体验期结束,不随续费延长)
|
||||
|
||||
QuotaChangeTargetAdjust int8 = 1 // 配额调整
|
||||
QuotaChangeTargetUse int8 = 2 // 使用
|
||||
|
||||
QuotaChangeTypeManual int8 = 5 // 人工调整
|
||||
// 额度流水变更类型(quota_flow_logs.change_type)。
|
||||
// 购买获赠(1)/活动赠送(2)/上传消耗(3)/删除回退(4)/退款回收(6)/到期失效(7) 由业务服务写入,
|
||||
// 管理后台仅写入人工调整(5)。
|
||||
QuotaChangeTypeManual int8 = 5 // 人工调整(管理后台发放/回收额度时写入)
|
||||
QuotaChangeTypeExpire int8 = 7 // 到期失效(固定有效期凭证过期,由业务服务定时任务补记)
|
||||
)
|
||||
|
||||
// 免费额度内置兜底值(与业务服务 DefaultQuota* 对齐;仅当 Quota 配置段未配置时使用)。
|
||||
|
||||
@@ -92,17 +92,18 @@ func (s *DashboardService) Summary(ctx context.Context) (*dto.DashboardResponse,
|
||||
RefundTotal: revenueStats.RefundTotal,
|
||||
},
|
||||
Todo: dto.DashboardTodo{
|
||||
RefundPending: todoStats.RefundPending,
|
||||
OutfitPendingAudit: todoStats.OutfitPendingAudit,
|
||||
GoodsOnShelf: todoStats.GoodsOnShelf,
|
||||
RefundPending: todoStats.RefundPending,
|
||||
// 穿搭口径复用内容概况聚合结果,避免对 outfits 表重复计数。
|
||||
OutfitPublicTotal: contentStats.OutfitPublic,
|
||||
OutfitBanned: contentStats.OutfitBanned,
|
||||
GoodsOnShelf: todoStats.GoodsOnShelf,
|
||||
},
|
||||
Content: dto.DashboardContent{
|
||||
Outfit: dto.DashboardOutfitStats{
|
||||
Total: contentStats.OutfitTotal,
|
||||
Pending: contentStats.OutfitPending,
|
||||
Passed: contentStats.OutfitPassed,
|
||||
Rejected: contentStats.OutfitRejected,
|
||||
Banned: contentStats.OutfitBanned,
|
||||
Total: contentStats.OutfitTotal,
|
||||
Public: contentStats.OutfitPublic,
|
||||
Private: contentStats.OutfitPrivate,
|
||||
Banned: contentStats.OutfitBanned,
|
||||
},
|
||||
Clothes: dto.DashboardClothesStats{
|
||||
Total: contentStats.ClothesTotal,
|
||||
|
||||
@@ -164,6 +164,8 @@ func buildOrderItem(o *mysqlmodel.Order) dto.OrderItem {
|
||||
OrderStatus: o.OrderStatus,
|
||||
TransactionID: o.TransactionID,
|
||||
MemberDays: o.MemberDays,
|
||||
GoodsType: o.GoodsType,
|
||||
QuotaValidDays: o.QuotaValidDays,
|
||||
UploadQuota: o.CosQuota,
|
||||
AIRecommendQuota: o.AIRecommendQuota,
|
||||
ClosetShareQuota: o.ClosetShareQuota,
|
||||
|
||||
@@ -233,7 +233,18 @@ func (s *UserService) QuotaAdjust(ctx context.Context, adminID int64, userUUID s
|
||||
grant.CreatedBy = adminID
|
||||
grant.UpdatedBy = adminID
|
||||
|
||||
if err := s.quotaGrantDao.QuotaGrantCreate(ctx, grant); err != nil {
|
||||
// 凭证与「人工调整」流水同事务写入:小程序额度明细中能看到对应的增加记录,
|
||||
// 避免业务侧到期失效任务补记了扣减、却查不到发放来源的账实不符。
|
||||
if err := s.quotaGrantDao.QuotaGrantCreateWithFlow(ctx, grant, mysqldao.QuotaFlowParams{
|
||||
UserID: user.ID,
|
||||
VipExpire: user.VIPExpire,
|
||||
QuotaType: request.QuotaType,
|
||||
Delta: request.Amount,
|
||||
FreeTotal: defaultQuotaValue(request.QuotaType),
|
||||
StatDate: todayStatDate(),
|
||||
OrderNo: grant.RefID, // 与业务侧到期失效流水按 ref_id 关联的口径保持一致
|
||||
Reason: "管理员发放额度凭证",
|
||||
}); err != nil {
|
||||
return errcode.DatabaseError
|
||||
}
|
||||
return nil
|
||||
@@ -252,14 +263,36 @@ func (s *UserService) GrantRevoke(ctx context.Context, adminID int64, userUUID s
|
||||
return errcode.DatabaseError
|
||||
}
|
||||
|
||||
if _, err := s.quotaGrantDao.QuotaGrantGetByID(ctx, grantID); err != nil {
|
||||
grant, err := s.quotaGrantDao.QuotaGrantGetByID(ctx, grantID)
|
||||
if err != nil {
|
||||
if isNotFound(err) {
|
||||
return errcode.UserGrantNotFound
|
||||
}
|
||||
return errcode.DatabaseError
|
||||
}
|
||||
// 归属校验:凭证必须属于该用户,防止越权回收他人凭证。
|
||||
if grant.UserID != user.ID {
|
||||
return errcode.UserGrantNotFound
|
||||
}
|
||||
|
||||
if err := s.quotaGrantDao.QuotaGrantSoftDelete(ctx, grantID, user.ID); err != nil {
|
||||
// 回收使「未用完的部分」失效:Cos 为 Amount-Used,每日额度 Used 恒为 0(口径与业务侧到期失效一致)。
|
||||
revoke := grant.Amount - grant.Used
|
||||
if revoke < 0 {
|
||||
revoke = 0
|
||||
}
|
||||
// 回收流水使用独立 order_no:同一凭证的发放流水同为 change_type=5,
|
||||
// 复用 ref_id 会撞 uk_quota_flow_idem(order_no, quota_type, change_type) 唯一索引。
|
||||
if err := s.quotaGrantDao.QuotaGrantRevokeWithFlow(ctx, mysqldao.QuotaFlowParams{
|
||||
UserID: user.ID,
|
||||
VipExpire: user.VIPExpire,
|
||||
QuotaType: grant.QuotaType,
|
||||
Delta: -revoke,
|
||||
FreeTotal: defaultQuotaValue(grant.QuotaType),
|
||||
StatDate: todayStatDate(),
|
||||
OrderNo: "revoke_" + genUUID(),
|
||||
RelatedID: grant.ID,
|
||||
Reason: "管理员回收额度凭证",
|
||||
}); err != nil {
|
||||
return errcode.DatabaseError
|
||||
}
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user