From 830d9ae9d91a01b75d9ec5a359ab3bfee96d4cd4 Mon Sep 17 00:00:00 2001 From: liangzai <2440983361@qq.com> Date: Fri, 9 May 2025 21:45:15 +0800 Subject: [PATCH] fix --- .../admin_promotion/recordlinkclicklogic.go | 49 +---- .../api/internal/queue/paySuccessNotify.go | 29 ++- .../service/adminPromotionLinkStatsService.go | 206 ++++++++++++++++++ app/main/api/internal/svc/servicecontext.go | 82 +++---- 4 files changed, 274 insertions(+), 92 deletions(-) create mode 100644 app/main/api/internal/service/adminPromotionLinkStatsService.go diff --git a/app/main/api/internal/logic/admin_promotion/recordlinkclicklogic.go b/app/main/api/internal/logic/admin_promotion/recordlinkclicklogic.go index c156c1c..c9bb5cf 100644 --- a/app/main/api/internal/logic/admin_promotion/recordlinkclicklogic.go +++ b/app/main/api/internal/logic/admin_promotion/recordlinkclicklogic.go @@ -3,16 +3,13 @@ package admin_promotion import ( "context" "fmt" - "time" "tyc-server/app/main/api/internal/svc" "tyc-server/app/main/api/internal/types" - "tyc-server/app/main/model" "tyc-server/common/xerr" "github.com/pkg/errors" "github.com/zeromicro/go-zero/core/logx" - "github.com/zeromicro/go-zero/core/stores/sqlx" ) type RecordLinkClickLogic struct { @@ -48,50 +45,12 @@ func (l *RecordLinkClickLogic) RecordLinkClick(req *types.RecordLinkClickReq) (r return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "无效的推广链接路径") } - err = l.svcCtx.AdminPromotionLinkModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error { - // 更新总统计 - totalStats, err := l.svcCtx.AdminPromotionLinkStatsTotalModel.FindOneByLinkId(l.ctx, link.Id) - if err != nil { - return err - } - totalStats.ClickCount = totalStats.ClickCount + 1 - err = l.svcCtx.AdminPromotionLinkStatsTotalModel.UpdateWithVersion(l.ctx, session, totalStats) - if err != nil { - return fmt.Errorf("更新总统计失败%+v", err) - } - // 更新历史统计 - today := time.Now().Truncate(24 * time.Hour) - builder := l.svcCtx.AdminPromotionLinkStatsHistoryModel.SelectBuilder() - builder = builder.Where("link_id = ? AND DATE(stats_date) = DATE(?)", link.Id, today) - historyStats, err := l.svcCtx.AdminPromotionLinkStatsHistoryModel.FindAll(l.ctx, builder, "") - if err != nil { - return fmt.Errorf("查询今日统计记录失败%+v", err) - } - - if len(historyStats) == 0 { - // 创建今天的记录 - newStats := &model.AdminPromotionLinkStatsHistory{ - LinkId: link.Id, - StatsDate: today, - ClickCount: 1, - } - _, err = l.svcCtx.AdminPromotionLinkStatsHistoryModel.Insert(l.ctx, session, newStats) - if err != nil { - return fmt.Errorf("创建今日统计记录失败%+v", err) - } - } else { - // 更新今日记录 - historyStats[0].ClickCount++ - err = l.svcCtx.AdminPromotionLinkStatsHistoryModel.UpdateWithVersion(l.ctx, session, historyStats[0]) - if err != nil { - return fmt.Errorf("更新历史统计失败%+v", err) - } - } - return nil - }) + // 使用 statsService 更新点击统计 + err = l.svcCtx.AdminPromotionLinkStatsService.UpdateLinkStats(l.ctx, link.Id) if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新总统计失败,%+v", err) + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新点击统计失败: %+v", err) } + return &types.RecordLinkClickResp{ Success: true, }, nil diff --git a/app/main/api/internal/queue/paySuccessNotify.go b/app/main/api/internal/queue/paySuccessNotify.go index b2fea18..4319538 100644 --- a/app/main/api/internal/queue/paySuccessNotify.go +++ b/app/main/api/internal/queue/paySuccessNotify.go @@ -4,8 +4,8 @@ import ( "context" "encoding/hex" "encoding/json" + "errors" "fmt" - "regexp" "strings" "tyc-server/app/main/api/internal/svc" "tyc-server/app/main/api/internal/types" @@ -130,7 +130,10 @@ func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq. updateQueryErr = fmt.Errorf("修改查询状态失败: %v", updateQueryErr) return l.handleError(ctx, updateQueryErr, order, query) } - + err = l.promotionOrderStats(ctx, order) + if err != nil { + logx.Errorf("处理推广订单统计失败,订单ID: %d, 错误: %v", order.Id, err) + } _, delErr := l.svcCtx.Redis.DelCtx(ctx, redisKey) if delErr != nil { logx.Errorf("删除Redis缓存失败,但任务已成功处理,订单ID: %d, 错误: %v", order.Id, delErr) @@ -191,6 +194,21 @@ func (l *PaySuccessNotifyUserHandler) handleError(ctx context.Context, err error return asynq.SkipRetry } +// 处理推广订单统计 +func (l *PaySuccessNotifyUserHandler) promotionOrderStats(ctx context.Context, order *model.Order) error { + promotionOrder, err := l.svcCtx.AdminPromotionOrderModel.FindOneByOrderId(ctx, order.Id) + if err != nil && !errors.Is(err, model.ErrNotFound) { + return fmt.Errorf("获取推广订单失败: %+v", err) + } + if promotionOrder != nil { + err = l.svcCtx.AdminPromotionLinkStatsService.UpdatePaymentStats(ctx, promotionOrder.LinkId, float64(order.Amount)) + if err != nil { + return fmt.Errorf("更新推广链接支付统计失败: %+v", err) + } + } + return nil +} + // desensitizeParams 对敏感数据进行脱敏处理 func (l *PaySuccessNotifyUserHandler) desensitizeParams(data []byte) ([]byte, error) { // 解析JSON数据到map @@ -260,13 +278,6 @@ func isPhoneField(key string) bool { strings.Contains(key, "手机") || strings.Contains(key, "电话") } -// 判断是否包含敏感数据模式 -func containsSensitivePattern(value string) bool { - // 检查是否包含连续的数字或字母模式 - numPattern := regexp.MustCompile(`\d{6,}`) - return numPattern.MatchString(value) -} - // 姓名脱敏 func maskName(name string) string { // 将字符串转换为rune切片以正确处理中文字符 diff --git a/app/main/api/internal/service/adminPromotionLinkStatsService.go b/app/main/api/internal/service/adminPromotionLinkStatsService.go new file mode 100644 index 0000000..9949d36 --- /dev/null +++ b/app/main/api/internal/service/adminPromotionLinkStatsService.go @@ -0,0 +1,206 @@ +package service + +import ( + "context" + "database/sql" + "time" + + "tyc-server/app/main/model" + "tyc-server/common/xerr" + + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/core/stores/sqlx" +) + +type AdminPromotionLinkStatsService struct { + logx.Logger + svcCtx *ServiceContext +} + +type ServiceContext struct { + AdminPromotionLinkModel model.AdminPromotionLinkModel + AdminPromotionLinkStatsTotalModel model.AdminPromotionLinkStatsTotalModel + AdminPromotionLinkStatsHistoryModel model.AdminPromotionLinkStatsHistoryModel +} + +func NewAdminPromotionLinkStatsService(svcCtx *ServiceContext) *AdminPromotionLinkStatsService { + return &AdminPromotionLinkStatsService{ + Logger: logx.WithContext(context.Background()), + svcCtx: svcCtx, + } +} + +// ensureTotalStats 确保总统计记录存在,如果不存在则创建 +func (s *AdminPromotionLinkStatsService) ensureTotalStats(ctx context.Context, session sqlx.Session, linkId int64) (*model.AdminPromotionLinkStatsTotal, error) { + totalStats, err := s.svcCtx.AdminPromotionLinkStatsTotalModel.FindOneByLinkId(ctx, linkId) + if err != nil { + if err == model.ErrNotFound { + // 如果记录不存在,创建新记录 + totalStats = &model.AdminPromotionLinkStatsTotal{ + LinkId: linkId, + ClickCount: 0, + PayCount: 0, + PayAmount: 0, + } + _, err = s.svcCtx.AdminPromotionLinkStatsTotalModel.Insert(ctx, session, totalStats) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建总统计记录失败: %+v", err) + } + // 重新获取创建后的记录 + totalStats, err = s.svcCtx.AdminPromotionLinkStatsTotalModel.FindOneByLinkId(ctx, linkId) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取新创建的总统计记录失败: %+v", err) + } + } else { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询总统计失败: %+v", err) + } + } + return totalStats, nil +} + +// ensureHistoryStats 确保历史统计记录存在,如果不存在则创建 +func (s *AdminPromotionLinkStatsService) ensureHistoryStats(ctx context.Context, session sqlx.Session, linkId int64, today time.Time) (*model.AdminPromotionLinkStatsHistory, error) { + historyStats, err := s.svcCtx.AdminPromotionLinkStatsHistoryModel.FindOneByLinkIdStatsDate(ctx, linkId, today) + if err != nil { + if err == model.ErrNotFound { + // 如果记录不存在,创建新记录 + historyStats = &model.AdminPromotionLinkStatsHistory{ + LinkId: linkId, + StatsDate: today, + ClickCount: 0, + PayCount: 0, + PayAmount: 0, + } + _, err = s.svcCtx.AdminPromotionLinkStatsHistoryModel.Insert(ctx, session, historyStats) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建今日统计记录失败: %+v", err) + } + // 重新获取创建后的记录 + historyStats, err = s.svcCtx.AdminPromotionLinkStatsHistoryModel.FindOneByLinkIdStatsDate(ctx, linkId, today) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取新创建的今日统计记录失败: %+v", err) + } + } else { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询今日统计记录失败: %+v", err) + } + } + return historyStats, nil +} + +// UpdateLinkStats 更新推广链接统计 +func (s *AdminPromotionLinkStatsService) UpdateLinkStats(ctx context.Context, linkId int64) error { + return s.svcCtx.AdminPromotionLinkStatsTotalModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error { + // 确保总统计记录存在 + totalStats, err := s.ensureTotalStats(ctx, session, linkId) + if err != nil { + return err + } + + // 更新总统计 + totalStats.ClickCount++ + totalStats.LastClickTime = sql.NullTime{Time: time.Now(), Valid: true} + err = s.svcCtx.AdminPromotionLinkStatsTotalModel.UpdateWithVersion(ctx, session, totalStats) + if err != nil { + return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新总统计失败: %+v", err) + } + + // 确保历史统计记录存在 + today := time.Now().Truncate(24 * time.Hour) + historyStats, err := s.ensureHistoryStats(ctx, session, linkId, today) + if err != nil { + return err + } + + // 更新历史统计 + historyStats.ClickCount++ + historyStats.LastClickTime = sql.NullTime{Time: time.Now(), Valid: true} + err = s.svcCtx.AdminPromotionLinkStatsHistoryModel.UpdateWithVersion(ctx, session, historyStats) + if err != nil { + return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新历史统计失败: %+v", err) + } + + return nil + }) +} + +// UpdatePaymentStats 更新付费统计 +func (s *AdminPromotionLinkStatsService) UpdatePaymentStats(ctx context.Context, linkId int64, amount float64) error { + return s.svcCtx.AdminPromotionLinkStatsTotalModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error { + // 确保总统计记录存在 + totalStats, err := s.ensureTotalStats(ctx, session, linkId) + if err != nil { + return err + } + + // 更新总统计 + totalStats.PayCount++ + totalStats.PayAmount += amount + totalStats.LastPayTime = sql.NullTime{Time: time.Now(), Valid: true} + err = s.svcCtx.AdminPromotionLinkStatsTotalModel.UpdateWithVersion(ctx, session, totalStats) + if err != nil { + return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新总统计失败: %+v", err) + } + + // 确保历史统计记录存在 + today := time.Now().Truncate(24 * time.Hour) + historyStats, err := s.ensureHistoryStats(ctx, session, linkId, today) + if err != nil { + return err + } + + // 更新历史统计 + historyStats.PayCount++ + historyStats.PayAmount += amount + historyStats.LastPayTime = sql.NullTime{Time: time.Now(), Valid: true} + err = s.svcCtx.AdminPromotionLinkStatsHistoryModel.UpdateWithVersion(ctx, session, historyStats) + if err != nil { + return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新历史统计失败: %+v", err) + } + + return nil + }) +} + +// CreateLinkStats 创建新的推广链接统计记录 +func (s *AdminPromotionLinkStatsService) CreateLinkStats(ctx context.Context, linkId int64) error { + return s.svcCtx.AdminPromotionLinkStatsTotalModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error { + // 检查总统计记录是否已存在 + _, err := s.svcCtx.AdminPromotionLinkStatsTotalModel.FindOneByLinkId(ctx, linkId) + if err == nil { + // 记录已存在,不需要创建 + return nil + } + if err != model.ErrNotFound { + return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询总统计记录失败: %+v", err) + } + + // 创建总统计记录 + totalStats := &model.AdminPromotionLinkStatsTotal{ + LinkId: linkId, + ClickCount: 0, + PayCount: 0, + PayAmount: 0, + } + _, err = s.svcCtx.AdminPromotionLinkStatsTotalModel.Insert(ctx, session, totalStats) + if err != nil { + return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建总统计记录失败: %+v", err) + } + + // 创建今日历史统计记录 + today := time.Now().Truncate(24 * time.Hour) + historyStats := &model.AdminPromotionLinkStatsHistory{ + LinkId: linkId, + StatsDate: today, + ClickCount: 0, + PayCount: 0, + PayAmount: 0, + } + _, err = s.svcCtx.AdminPromotionLinkStatsHistoryModel.Insert(ctx, session, historyStats) + if err != nil { + return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建历史统计记录失败: %+v", err) + } + + return nil + }) +} diff --git a/app/main/api/internal/svc/servicecontext.go b/app/main/api/internal/svc/servicecontext.go index 69e6de8..fc9eb3c 100644 --- a/app/main/api/internal/svc/servicecontext.go +++ b/app/main/api/internal/svc/servicecontext.go @@ -35,17 +35,18 @@ type ServiceContext struct { ExampleParamsModel model.ExampleParamsModel // 服务 - AlipayService *service.AliPayService - WechatPayService *service.WechatPayService - ApplePayService *service.ApplePayService - WestDexService *service.WestDexService - YushanService *service.YushanService - TianjuService *service.TianjuService - ApiRequestService *service.ApiRequestService - AsynqServer *asynq.Server // 服务端 - AsynqService *service.AsynqService // 客户端 - VerificationService *service.VerificationService - DictService *service.DictService + AlipayService *service.AliPayService + WechatPayService *service.WechatPayService + ApplePayService *service.ApplePayService + WestDexService *service.WestDexService + YushanService *service.YushanService + TianjuService *service.TianjuService + ApiRequestService *service.ApiRequestService + AsynqServer *asynq.Server // 服务端 + AsynqService *service.AsynqService // 客户端 + VerificationService *service.VerificationService + DictService *service.DictService + AdminPromotionLinkStatsService *service.AdminPromotionLinkStatsService // admin AdminApiModel model.AdminApiModel @@ -105,35 +106,40 @@ func NewServiceContext(c config.Config) *ServiceContext { featureModel := model.NewFeatureModel(db, c.CacheRedis) userAuthModel := model.NewUserAuthModel(db, c.CacheRedis) apiRequestService := service.NewApiRequestService(c, westDexService, yushanService, tianjuService, featureModel, productFeatureModel) - + adminPromotionLinkStatsService := service.NewAdminPromotionLinkStatsService(&service.ServiceContext{ + AdminPromotionLinkModel: adminPromotionLinkModel, + AdminPromotionLinkStatsTotalModel: adminPromotionLinkStatsTotalModel, + AdminPromotionLinkStatsHistoryModel: adminPromotionLinkStatsHistoryModel, + }) dictService := service.NewDictService(adminDictTypeModel, adminDictDataModel) return &ServiceContext{ - Config: c, - Redis: redis.MustNewRedis(redisConf), - SourceInterceptor: middleware.NewSourceInterceptorMiddleware().Handle, - AuthInterceptor: middleware.NewAuthInterceptorMiddleware(c).Handle, - AlipayService: service.NewAliPayService(c), - WechatPayService: service.NewWechatPayService(c, userAuthModel), - ApplePayService: service.NewApplePayService(c), - WestDexService: westDexService, - YushanService: yushanService, - TianjuService: tianjuService, - VerificationService: service.NewVerificationService(c, westDexService, apiRequestService), - AsynqServer: asynqServer, - ApiRequestService: apiRequestService, - AsynqService: service.NewAsynqService(c), - DictService: dictService, - UserModel: model.NewUserModel(db, c.CacheRedis), - UserAuthModel: userAuthModel, - ProductModel: model.NewProductModel(db, c.CacheRedis), - ProductRenderModel: model.NewProductRenderModel(db, c.CacheRedis), - OrderModel: model.NewOrderModel(db, c.CacheRedis), - QueryModel: model.NewQueryModel(db, c.CacheRedis), - ExampleModel: model.NewExampleModel(db, c.CacheRedis), - ExampleParamsModel: model.NewExampleParamsModel(db, c.CacheRedis), - GlobalNotificationsModel: model.NewGlobalNotificationsModel(db, c.CacheRedis), - FeatureModel: featureModel, - ProductFeatureModel: productFeatureModel, + Config: c, + Redis: redis.MustNewRedis(redisConf), + SourceInterceptor: middleware.NewSourceInterceptorMiddleware().Handle, + AuthInterceptor: middleware.NewAuthInterceptorMiddleware(c).Handle, + AlipayService: service.NewAliPayService(c), + WechatPayService: service.NewWechatPayService(c, userAuthModel), + ApplePayService: service.NewApplePayService(c), + WestDexService: westDexService, + YushanService: yushanService, + TianjuService: tianjuService, + AdminPromotionLinkStatsService: adminPromotionLinkStatsService, + VerificationService: service.NewVerificationService(c, westDexService, apiRequestService), + AsynqServer: asynqServer, + ApiRequestService: apiRequestService, + AsynqService: service.NewAsynqService(c), + DictService: dictService, + UserModel: model.NewUserModel(db, c.CacheRedis), + UserAuthModel: userAuthModel, + ProductModel: model.NewProductModel(db, c.CacheRedis), + ProductRenderModel: model.NewProductRenderModel(db, c.CacheRedis), + OrderModel: model.NewOrderModel(db, c.CacheRedis), + QueryModel: model.NewQueryModel(db, c.CacheRedis), + ExampleModel: model.NewExampleModel(db, c.CacheRedis), + ExampleParamsModel: model.NewExampleParamsModel(db, c.CacheRedis), + GlobalNotificationsModel: model.NewGlobalNotificationsModel(db, c.CacheRedis), + FeatureModel: featureModel, + ProductFeatureModel: productFeatureModel, AdminApiModel: adminApiModel, AdminMenuModel: adminMenuModel,