收口审计治理与套餐任务进展

Constraint: 在线热修前必须保存当前迭代分支全部有效代码进展
Confidence: medium
Scope-risk: broad
Directive: 后续修改需保持审计事件与业务事务边界一致
Tested: git diff --cached --check
Not-tested: 未运行全量测试,提交用于切换分支前保存既有工作
This commit is contained in:
2026-08-05 14:30:54 +08:00
parent b3499adfca
commit 5e552d99bc
178 changed files with 16797 additions and 5674 deletions

View File

@@ -0,0 +1,54 @@
package package_series
import (
"context"
"gorm.io/gorm"
"github.com/break/junhong_cmp_fiber/internal/infrastructure/audit"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
)
// SetAccessAudit 注入套餐系列统一审计 Writer。
func (s *Service) SetAccessAudit(db *gorm.DB, writer *audit.Writer) {
s.db = db
s.auditWriter = writer
}
func (s *Service) appendAudit(ctx context.Context, tx *gorm.DB, actionCode, summary string, series *model.PackageSeries, beforeData, afterData map[string]any) error {
if s.auditWriter == nil || s.db == nil {
return errors.New(errors.CodeInvalidStatus, "套餐系列统一审计接缝未配置")
}
return s.auditWriter.Append(ctx, tx, audit.AppendInput{
ActionCode: actionCode, Summary: summary, ScopeType: constants.AuditScopePlatform,
Result: constants.AuditResultSuccess,
Resources: []audit.ResourceInput{audit.PackageSeriesResource(
series, constants.AuditResourceRelationPrimary, constants.AuditResourceRolePackageSeriesTarget, beforeData, afterData,
)},
})
}
func (s *Service) recordAuditFailure(ctx context.Context, actionCode, summary string, series *model.PackageSeries, beforeData map[string]any, businessErr error) {
if series == nil {
return
}
s.auditWriter.RecordFailure(ctx, s.db, audit.AppendInput{
ActionCode: actionCode, Summary: summary, ScopeType: constants.AuditScopePlatform,
Resources: []audit.ResourceInput{audit.PackageSeriesResource(
series, constants.AuditResourceRelationPrimary, constants.AuditResourceRolePackageSeriesTarget, beforeData, nil,
)},
}, businessErr)
}
func packageSeriesData(series *model.PackageSeries) map[string]any {
if series == nil {
return nil
}
return map[string]any{
"series_name": series.SeriesName, "description": series.Description, "status": series.Status,
"enable_one_time_commission": series.EnableOneTimeCommission,
"one_time_commission_config": series.OneTimeCommissionConfigJSON,
}
}

View File

@@ -7,6 +7,7 @@ import (
"gorm.io/gorm"
"github.com/break/junhong_cmp_fiber/internal/infrastructure/audit"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
"github.com/break/junhong_cmp_fiber/internal/store"
@@ -18,9 +19,11 @@ import (
// Service 套餐系列业务服务
type Service struct {
db *gorm.DB
packageSeriesStore *postgres.PackageSeriesStore
shopSeriesAllocationStore *postgres.ShopSeriesAllocationStore
packageStore *postgres.PackageStore
auditWriter *audit.Writer
}
// New 创建套餐系列服务实例
@@ -32,7 +35,7 @@ func New(packageSeriesStore *postgres.PackageSeriesStore, shopSeriesAllocationSt
}
}
func (s *Service) Create(ctx context.Context, req *dto.CreatePackageSeriesRequest) (*dto.PackageSeriesResponse, error) {
func (s *Service) Create(ctx context.Context, req *dto.CreatePackageSeriesRequest) (_ *dto.PackageSeriesResponse, retErr error) {
currentUserID := middleware.GetUserIDFromContext(ctx)
if currentUserID == 0 {
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
@@ -50,6 +53,13 @@ func (s *Service) Create(ctx context.Context, req *dto.CreatePackageSeriesReques
Status: constants.StatusEnabled,
OneTimeCommissionConfigJSON: "{}",
}
defer func() {
if retErr != nil {
failedSeries := *series
failedSeries.ID = 0
s.recordAuditFailure(ctx, constants.AuditActionPackageSeriesCreated, "创建套餐系列失败 "+series.SeriesCode, &failedSeries, nil, retErr)
}
}()
series.Creator = currentUserID
if req.EnableOneTimeCommission != nil {
@@ -69,8 +79,13 @@ func (s *Service) Create(ctx context.Context, req *dto.CreatePackageSeriesReques
}
}
if err := s.packageSeriesStore.Create(ctx, series); err != nil {
return nil, errors.Wrap(errors.CodeInternalError, err, "创建套餐系列失败")
if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := postgres.NewPackageSeriesStore(tx).Create(ctx, series); err != nil {
return errors.Wrap(errors.CodeInternalError, err, "创建套餐系列失败")
}
return s.appendAudit(ctx, tx, constants.AuditActionPackageSeriesCreated, "创建套餐系列 "+series.SeriesCode, series, nil, packageSeriesData(series))
}); err != nil {
return nil, err
}
return s.toResponse(series), nil
@@ -87,7 +102,7 @@ func (s *Service) Get(ctx context.Context, id uint) (*dto.PackageSeriesResponse,
return s.toResponse(series), nil
}
func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdatePackageSeriesRequest) (*dto.PackageSeriesResponse, error) {
func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdatePackageSeriesRequest) (_ *dto.PackageSeriesResponse, retErr error) {
currentUserID := middleware.GetUserIDFromContext(ctx)
if currentUserID == 0 {
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
@@ -100,6 +115,12 @@ func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdatePackageSer
}
return nil, errors.Wrap(errors.CodeInternalError, err, "获取套餐系列失败")
}
before := *series
defer func() {
if retErr != nil {
s.recordAuditFailure(ctx, constants.AuditActionPackageSeriesUpdated, "更新套餐系列失败 "+before.SeriesCode, &before, packageSeriesData(&before), retErr)
}
}()
if req.SeriesName != nil {
series.SeriesName = *req.SeriesName
@@ -128,21 +149,31 @@ func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdatePackageSer
}
series.Updater = currentUserID
if err := s.packageSeriesStore.Update(ctx, series); err != nil {
return nil, errors.Wrap(errors.CodeInternalError, err, "更新套餐系列失败")
if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := postgres.NewPackageSeriesStore(tx).Update(ctx, series); err != nil {
return errors.Wrap(errors.CodeInternalError, err, "更新套餐系列失败")
}
return s.appendAudit(ctx, tx, constants.AuditActionPackageSeriesUpdated, "更新套餐系列 "+series.SeriesCode, series, packageSeriesData(&before), packageSeriesData(series))
}); err != nil {
return nil, err
}
return s.toResponse(series), nil
}
func (s *Service) Delete(ctx context.Context, id uint) error {
_, err := s.packageSeriesStore.GetByID(ctx, id)
func (s *Service) Delete(ctx context.Context, id uint) (retErr error) {
series, err := s.packageSeriesStore.GetByID(ctx, id)
if err != nil {
if err == gorm.ErrRecordNotFound {
return errors.New(errors.CodeNotFound, "套餐系列不存在")
}
return errors.Wrap(errors.CodeInternalError, err, "获取套餐系列失败")
}
defer func() {
if retErr != nil {
s.recordAuditFailure(ctx, constants.AuditActionPackageSeriesDeleted, "删除套餐系列失败 "+series.SeriesCode, series, packageSeriesData(series), retErr)
}
}()
count, err := s.packageStore.CountBySeriesID(ctx, id)
if err != nil {
@@ -152,11 +183,12 @@ func (s *Service) Delete(ctx context.Context, id uint) error {
return errors.New(errors.CodeInvalidParam, fmt.Sprintf("该系列下有 %d 个关联套餐,请先处理后再删除", count))
}
if err := s.packageSeriesStore.Delete(ctx, id); err != nil {
return errors.Wrap(errors.CodeInternalError, err, "删除套餐系列失败")
}
return nil
return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := postgres.NewPackageSeriesStore(tx).Delete(ctx, id); err != nil {
return errors.Wrap(errors.CodeInternalError, err, "删除套餐系列失败")
}
return s.appendAudit(ctx, tx, constants.AuditActionPackageSeriesDeleted, "删除套餐系列 "+series.SeriesCode, series, packageSeriesData(series), map[string]any{"deleted": true})
})
}
func (s *Service) List(ctx context.Context, req *dto.PackageSeriesListRequest) ([]*dto.PackageSeriesResponse, int64, error) {
@@ -223,7 +255,7 @@ func (s *Service) List(ctx context.Context, req *dto.PackageSeriesListRequest) (
return responses, total, nil
}
func (s *Service) UpdateStatus(ctx context.Context, id uint, status int) error {
func (s *Service) UpdateStatus(ctx context.Context, id uint, status int) (retErr error) {
currentUserID := middleware.GetUserIDFromContext(ctx)
if currentUserID == 0 {
return errors.New(errors.CodeUnauthorized, "未授权访问")
@@ -236,15 +268,22 @@ func (s *Service) UpdateStatus(ctx context.Context, id uint, status int) error {
}
return errors.Wrap(errors.CodeInternalError, err, "获取套餐系列失败")
}
before := *series
defer func() {
if retErr != nil {
s.recordAuditFailure(ctx, constants.AuditActionPackageSeriesStatusUpdated, "更新套餐系列状态失败 "+before.SeriesCode, &before, map[string]any{"status": before.Status}, retErr)
}
}()
series.Status = status
series.Updater = currentUserID
if err := s.packageSeriesStore.Update(ctx, series); err != nil {
return errors.Wrap(errors.CodeInternalError, err, "更新套餐系列状态失败")
}
return nil
return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := postgres.NewPackageSeriesStore(tx).Update(ctx, series); err != nil {
return errors.Wrap(errors.CodeInternalError, err, "更新套餐系列状态失败")
}
return s.appendAudit(ctx, tx, constants.AuditActionPackageSeriesStatusUpdated, "更新套餐系列状态 "+series.SeriesCode, series, map[string]any{"status": before.Status}, map[string]any{"status": series.Status})
})
}
func (s *Service) toResponse(series *model.PackageSeries) *dto.PackageSeriesResponse {