收口审计治理与套餐任务进展
Constraint: 在线热修前必须保存当前迭代分支全部有效代码进展 Confidence: medium Scope-risk: broad Directive: 后续修改需保持审计事件与业务事务边界一致 Tested: git diff --cached --check Not-tested: 未运行全量测试,提交用于切换分支前保存既有工作
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/infrastructure/audit"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"github.com/redis/go-redis/v9"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type ResetService struct {
|
||||
@@ -19,6 +21,12 @@ type ResetService struct {
|
||||
packageUsageStore *postgres.PackageUsageStore
|
||||
logger *zap.Logger
|
||||
resumeCallback ResumeCallback
|
||||
auditWriter *audit.Writer
|
||||
}
|
||||
|
||||
// SetLifecycleAudit 注入套餐权益流量重置统一审计 Writer。
|
||||
func (s *ResetService) SetLifecycleAudit(writer *audit.Writer) {
|
||||
s.auditWriter = writer
|
||||
}
|
||||
|
||||
func NewResetService(
|
||||
@@ -55,6 +63,7 @@ func (s *ResetService) resetDailyUsageWithDB(ctx context.Context, db *gorm.DB) e
|
||||
err := tx.Where("data_reset_cycle = ?", constants.PackageDataResetDaily).
|
||||
Where("next_reset_at <= ?", now).
|
||||
Where("status IN ?", []int{constants.PackageUsageStatusActive, constants.PackageUsageStatusDepleted}).
|
||||
Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||
Find(&packages).Error
|
||||
|
||||
if err != nil {
|
||||
@@ -80,21 +89,40 @@ func (s *ResetService) resetDailyUsageWithDB(ctx context.Context, db *gorm.DB) e
|
||||
"status": constants.PackageUsageStatusActive,
|
||||
}
|
||||
|
||||
if err := tx.Model(&model.PackageUsage{}).
|
||||
Where("id IN ?", packageIDs).
|
||||
Updates(updates).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "批量重置日流量失败")
|
||||
result := tx.Model(&model.PackageUsage{}).
|
||||
Where("id IN ? AND next_reset_at <= ? AND status IN ?", packageIDs, now, []int{constants.PackageUsageStatusActive, constants.PackageUsageStatusDepleted}).
|
||||
Updates(updates)
|
||||
if result.Error != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, result.Error, "批量重置日流量失败")
|
||||
}
|
||||
if result.RowsAffected != int64(len(packages)) {
|
||||
return errors.New(errors.CodeConflict, "日流量重置目标状态已变化")
|
||||
}
|
||||
changes := make([]packageUsageAuditChange, 0, len(packages))
|
||||
for _, usage := range packages {
|
||||
beforeData := packageUsageStateData(usage)
|
||||
usage.DataUsageMB = 0
|
||||
usage.LastResetAt = &now
|
||||
usage.NextResetAt = &nextReset
|
||||
usage.Status = constants.PackageUsageStatusActive
|
||||
changes = append(changes, packageUsageAuditChange{Usage: usage, BeforeData: beforeData, AfterData: packageUsageStateData(usage)})
|
||||
}
|
||||
resetPackages = packages
|
||||
if err := appendPackageUsageAudit(ctx, tx, s.auditWriter, constants.AuditActionPackageUsageTrafficReset, "重置套餐权益日流量", changes, nil, map[string]any{"reset_cycle": constants.PackageDataResetDaily}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.logger.Info("日流量重置完成",
|
||||
zap.Int("count", len(packages)),
|
||||
zap.Time("next_reset_at", nextReset))
|
||||
|
||||
resetPackages = packages
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if len(resetPackages) > 0 {
|
||||
recordPackageUsageFailure(ctx, s.db, s.auditWriter, constants.AuditActionPackageUsageTrafficReset, "重置套餐权益日流量失败", resetPackages[0], err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -117,6 +145,7 @@ func (s *ResetService) resetMonthlyUsageWithDB(ctx context.Context, db *gorm.DB)
|
||||
err := tx.Where("data_reset_cycle = ?", constants.PackageDataResetMonthly).
|
||||
Where("next_reset_at <= ?", now).
|
||||
Where("status IN ?", []int{constants.PackageUsageStatusActive, constants.PackageUsageStatusDepleted}).
|
||||
Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||
Find(&packages).Error
|
||||
|
||||
if err != nil {
|
||||
@@ -128,6 +157,7 @@ func (s *ResetService) resetMonthlyUsageWithDB(ctx context.Context, db *gorm.DB)
|
||||
return nil
|
||||
}
|
||||
|
||||
changes := make([]packageUsageAuditChange, 0, len(packages))
|
||||
for _, usage := range packages {
|
||||
var pkg model.Package
|
||||
if err := tx.First(&pkg, usage.PackageID).Error; err != nil {
|
||||
@@ -156,9 +186,22 @@ func (s *ResetService) resetMonthlyUsageWithDB(ctx context.Context, db *gorm.DB)
|
||||
"status": constants.PackageUsageStatusActive,
|
||||
}
|
||||
|
||||
if err := tx.Model(usage).Updates(updates).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "重置月流量失败")
|
||||
beforeData := packageUsageStateData(usage)
|
||||
result := tx.Model(usage).
|
||||
Where("next_reset_at <= ? AND status IN ?", now, []int{constants.PackageUsageStatusActive, constants.PackageUsageStatusDepleted}).
|
||||
Updates(updates)
|
||||
if result.Error != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, result.Error, "重置月流量失败")
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
continue
|
||||
}
|
||||
usage.DataUsageMB = 0
|
||||
usage.LastResetAt = &now
|
||||
usage.NextResetAt = nextResetAt
|
||||
usage.Status = constants.PackageUsageStatusActive
|
||||
changes = append(changes, packageUsageAuditChange{Usage: usage, BeforeData: beforeData, AfterData: packageUsageStateData(usage)})
|
||||
resetPackages = append(resetPackages, usage)
|
||||
|
||||
s.logger.Info("月流量已重置",
|
||||
zap.Uint("usage_id", usage.ID),
|
||||
@@ -166,11 +209,18 @@ func (s *ResetService) resetMonthlyUsageWithDB(ctx context.Context, db *gorm.DB)
|
||||
zap.Time("next_reset_at", *nextResetAt))
|
||||
}
|
||||
|
||||
resetPackages = packages
|
||||
if len(changes) > 0 {
|
||||
if err := appendPackageUsageAudit(ctx, tx, s.auditWriter, constants.AuditActionPackageUsageTrafficReset, "重置套餐权益月流量", changes, nil, map[string]any{"reset_cycle": constants.PackageDataResetMonthly}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if len(resetPackages) > 0 {
|
||||
recordPackageUsageFailure(ctx, s.db, s.auditWriter, constants.AuditActionPackageUsageTrafficReset, "重置套餐权益月流量失败", resetPackages[0], err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -193,6 +243,7 @@ func (s *ResetService) resetYearlyUsageWithDB(ctx context.Context, db *gorm.DB)
|
||||
err := tx.Where("data_reset_cycle = ?", constants.PackageDataResetYearly).
|
||||
Where("next_reset_at <= ?", now).
|
||||
Where("status IN ?", []int{constants.PackageUsageStatusActive, constants.PackageUsageStatusDepleted}).
|
||||
Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||
Find(&packages).Error
|
||||
|
||||
if err != nil {
|
||||
@@ -218,21 +269,40 @@ func (s *ResetService) resetYearlyUsageWithDB(ctx context.Context, db *gorm.DB)
|
||||
"status": constants.PackageUsageStatusActive,
|
||||
}
|
||||
|
||||
if err := tx.Model(&model.PackageUsage{}).
|
||||
Where("id IN ?", packageIDs).
|
||||
Updates(updates).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "批量重置年流量失败")
|
||||
result := tx.Model(&model.PackageUsage{}).
|
||||
Where("id IN ? AND next_reset_at <= ? AND status IN ?", packageIDs, now, []int{constants.PackageUsageStatusActive, constants.PackageUsageStatusDepleted}).
|
||||
Updates(updates)
|
||||
if result.Error != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, result.Error, "批量重置年流量失败")
|
||||
}
|
||||
if result.RowsAffected != int64(len(packages)) {
|
||||
return errors.New(errors.CodeConflict, "年流量重置目标状态已变化")
|
||||
}
|
||||
changes := make([]packageUsageAuditChange, 0, len(packages))
|
||||
for _, usage := range packages {
|
||||
beforeData := packageUsageStateData(usage)
|
||||
usage.DataUsageMB = 0
|
||||
usage.LastResetAt = &now
|
||||
usage.NextResetAt = &nextReset
|
||||
usage.Status = constants.PackageUsageStatusActive
|
||||
changes = append(changes, packageUsageAuditChange{Usage: usage, BeforeData: beforeData, AfterData: packageUsageStateData(usage)})
|
||||
}
|
||||
resetPackages = packages
|
||||
if err := appendPackageUsageAudit(ctx, tx, s.auditWriter, constants.AuditActionPackageUsageTrafficReset, "重置套餐权益年流量", changes, nil, map[string]any{"reset_cycle": constants.PackageDataResetYearly}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.logger.Info("年流量重置完成",
|
||||
zap.Int("count", len(packages)),
|
||||
zap.Time("next_reset_at", nextReset))
|
||||
|
||||
resetPackages = packages
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if len(resetPackages) > 0 {
|
||||
recordPackageUsageFailure(ctx, s.db, s.auditWriter, constants.AuditActionPackageUsageTrafficReset, "重置套餐权益年流量失败", resetPackages[0], err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user