feat: 实现一次性佣金功能
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m41s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m41s
- 新增佣金计算服务,支持一次性佣金和返佣计算 - 新增 ShopSeriesOneTimeCommissionTier 模型和存储层 - 新增两个数据库迁移:一次性佣金表和订单佣金字段 - 更新 Commission 模型,新增佣金来源和关联字段 - 更新 CommissionRecord 存储层,支持一次性佣金查询 - 更新 MyCommission 服务,集成一次性佣金计算逻辑 - 更新 ShopCommission 服务,支持一次性佣金统计 - 新增佣金计算异步任务处理器 - 更新 API 路由,新增一次性佣金相关端点 - 归档 OpenSpec 变更文档,同步规范到主规范库
This commit is contained in:
@@ -329,8 +329,8 @@ func (s *Service) ListMyCommissionRecords(ctx context.Context, req *dto.MyCommis
|
||||
query := s.db.WithContext(ctx).Model(&model.CommissionRecord{}).
|
||||
Where("shop_id = ?", shopID)
|
||||
|
||||
if req.CommissionType != nil {
|
||||
query = query.Where("commission_type = ?", *req.CommissionType)
|
||||
if req.CommissionSource != nil {
|
||||
query = query.Where("commission_source = ?", *req.CommissionSource)
|
||||
}
|
||||
|
||||
var total int64
|
||||
@@ -347,14 +347,14 @@ func (s *Service) ListMyCommissionRecords(ctx context.Context, req *dto.MyCommis
|
||||
items := make([]dto.MyCommissionRecordItem, 0, len(records))
|
||||
for _, r := range records {
|
||||
items = append(items, dto.MyCommissionRecordItem{
|
||||
ID: r.ID,
|
||||
ShopID: r.ShopID,
|
||||
OrderID: r.OrderID,
|
||||
CommissionType: r.CommissionType,
|
||||
Amount: r.Amount,
|
||||
Status: r.Status,
|
||||
StatusName: getCommissionStatusName(r.Status),
|
||||
CreatedAt: r.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
ID: r.ID,
|
||||
ShopID: r.ShopID,
|
||||
OrderID: r.OrderID,
|
||||
CommissionSource: r.CommissionSource,
|
||||
Amount: r.Amount,
|
||||
Status: r.Status,
|
||||
StatusName: getCommissionStatusName(r.Status),
|
||||
CreatedAt: r.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -366,6 +366,97 @@ func (s *Service) ListMyCommissionRecords(ctx context.Context, req *dto.MyCommis
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetStats(ctx context.Context, req *dto.CommissionStatsRequest) (*dto.CommissionStatsResponse, error) {
|
||||
shopID, err := s.getShopIDFromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filters := &postgres.CommissionRecordListFilters{
|
||||
ShopID: shopID,
|
||||
StartTime: req.StartTime,
|
||||
EndTime: req.EndTime,
|
||||
}
|
||||
|
||||
stats, err := s.commissionRecordStore.GetStats(ctx, filters)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取佣金统计失败: %w", err)
|
||||
}
|
||||
|
||||
if stats == nil {
|
||||
return &dto.CommissionStatsResponse{}, nil
|
||||
}
|
||||
|
||||
var costDiffPercent, oneTimePercent, tierBonusPercent int64
|
||||
if stats.TotalAmount > 0 {
|
||||
costDiffPercent = stats.CostDiffAmount * 1000 / stats.TotalAmount
|
||||
oneTimePercent = stats.OneTimeAmount * 1000 / stats.TotalAmount
|
||||
tierBonusPercent = stats.TierBonusAmount * 1000 / stats.TotalAmount
|
||||
}
|
||||
|
||||
return &dto.CommissionStatsResponse{
|
||||
TotalAmount: stats.TotalAmount,
|
||||
CostDiffAmount: stats.CostDiffAmount,
|
||||
OneTimeAmount: stats.OneTimeAmount,
|
||||
TierBonusAmount: stats.TierBonusAmount,
|
||||
CostDiffPercent: costDiffPercent,
|
||||
OneTimePercent: oneTimePercent,
|
||||
TierBonusPercent: tierBonusPercent,
|
||||
TotalCount: stats.TotalCount,
|
||||
CostDiffCount: stats.CostDiffCount,
|
||||
OneTimeCount: stats.OneTimeCount,
|
||||
TierBonusCount: stats.TierBonusCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetDailyStats(ctx context.Context, req *dto.DailyCommissionStatsRequest) ([]*dto.DailyCommissionStatsResponse, error) {
|
||||
shopID, err := s.getShopIDFromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
days := 30
|
||||
if req.Days != nil && *req.Days > 0 {
|
||||
days = *req.Days
|
||||
}
|
||||
|
||||
filters := &postgres.CommissionRecordListFilters{
|
||||
ShopID: shopID,
|
||||
StartTime: req.StartDate,
|
||||
EndTime: req.EndDate,
|
||||
}
|
||||
|
||||
dailyStats, err := s.commissionRecordStore.GetDailyStats(ctx, filters, days)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取每日佣金统计失败: %w", err)
|
||||
}
|
||||
|
||||
result := make([]*dto.DailyCommissionStatsResponse, 0, len(dailyStats))
|
||||
for _, stat := range dailyStats {
|
||||
result = append(result, &dto.DailyCommissionStatsResponse{
|
||||
Date: stat.Date,
|
||||
TotalAmount: stat.TotalAmount,
|
||||
TotalCount: stat.TotalCount,
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *Service) getShopIDFromContext(ctx context.Context) (uint, error) {
|
||||
userType := middleware.GetUserTypeFromContext(ctx)
|
||||
if userType != constants.UserTypeAgent {
|
||||
return 0, errors.New(errors.CodeForbidden, "仅代理商用户可访问")
|
||||
}
|
||||
|
||||
shopID := middleware.GetShopIDFromContext(ctx)
|
||||
if shopID == 0 {
|
||||
return 0, errors.New(errors.CodeForbidden, "无法获取店铺信息")
|
||||
}
|
||||
|
||||
return shopID, nil
|
||||
}
|
||||
|
||||
// generateWithdrawalNo 生成提现单号
|
||||
func generateWithdrawalNo() string {
|
||||
now := time.Now()
|
||||
|
||||
Reference in New Issue
Block a user