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

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,47 @@
package asset_package_batch_order
import (
"context"
"strconv"
"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/middleware"
)
func (s *Service) writeBatchOrderTaskAudit(ctx context.Context, tx *gorm.DB, task *model.AssetPackageBatchOrderTask, before, after map[string]any, result, phase, errorCode, errorSummary string) error {
scopeType, scopeID := constants.AuditScopePlatform, ""
if task.CreatorShopID != 0 {
scopeType, scopeID = constants.AuditScopeShop, strconv.FormatUint(uint64(task.CreatorShopID), 10)
}
return s.auditWriter.WriteTask(ctx, tx, audit.TaskInput{
EventID: audit.TaskEventID(constants.AuditResourceAssetPackageBatchOrderTask, task.ID, phase),
ActionCode: constants.AuditActionAssetPackageBatchOrderTaskCreated,
Summary: "创建资产套餐批量订购任务", TaskID: task.ID, TaskNo: task.TaskNo,
Actor: audit.ActorInput{
Kind: constants.AuditActorAccount, ID: strconv.FormatUint(uint64(middleware.GetUserIDFromContext(ctx)), 10),
Name: middleware.GetUsernameFromContext(ctx),
},
Source: constants.AuditSourceAdminAPI, ScopeType: scopeType, ScopeID: scopeID,
Result: result, ErrorCode: errorCode, ErrorSummary: errorSummary,
IdentitySnapshot: map[string]any{
"id": task.ID, "task_no": task.TaskNo, "file_name": task.FileName,
"package_id": task.PackageID, "package_code": task.PackageCode,
"package_name": task.PackageName, "payment_method": task.PaymentMethod,
},
BeforeData: before, AfterData: after,
})
}
func batchOrderTaskState(task *model.AssetPackageBatchOrderTask) map[string]any {
if task == nil {
return nil
}
return map[string]any{
"status": task.Status, "total_count": task.TotalCount,
"success_count": task.SuccessCount, "fail_count": task.FailCount,
}
}

View File

@@ -4,17 +4,20 @@ package asset_package_batch_order
import (
"context"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/hibiken/asynq"
"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"
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
"github.com/break/junhong_cmp_fiber/pkg/asynctask"
"github.com/break/junhong_cmp_fiber/pkg/auditfailure"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/break/junhong_cmp_fiber/pkg/middleware"
@@ -26,11 +29,16 @@ type Service struct {
taskStore *postgres.AssetPackageBatchOrderTaskStore
packageStore *postgres.PackageStore
queueClient *queue.Client
auditWriter *audit.Writer
}
// New 创建资产套餐批量订购任务服务。
func New(taskStore *postgres.AssetPackageBatchOrderTaskStore, packageStore *postgres.PackageStore, queueClient *queue.Client) *Service {
return &Service{taskStore: taskStore, packageStore: packageStore, queueClient: queueClient}
func New(taskStore *postgres.AssetPackageBatchOrderTaskStore, packageStore *postgres.PackageStore, queueClient *queue.Client, auditWriters ...*audit.Writer) *Service {
service := &Service{taskStore: taskStore, packageStore: packageStore, queueClient: queueClient}
if len(auditWriters) > 0 {
service.auditWriter = auditWriters[0]
}
return service
}
// TaskPayload 批量订购 Worker 结构化载荷。
@@ -69,7 +77,15 @@ func (s *Service) Create(ctx context.Context, req *dto.CreateAssetPackageBatchOr
CreatorUserType: middleware.GetUserTypeFromContext(ctx), CreatorShopID: middleware.GetShopIDFromContext(ctx),
CreatorName: middleware.GetUsernameFromContext(ctx),
}
if err := s.taskStore.Create(ctx, task); err != nil {
if s.auditWriter == nil {
return nil, errors.New(errors.CodeInvalidStatus, "资产套餐批量订购统一审计接缝未配置")
}
if err := s.taskStore.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := s.taskStore.WithTx(tx).Create(ctx, task); err != nil {
return err
}
return s.writeBatchOrderTaskAudit(ctx, tx, task, nil, batchOrderTaskState(task), constants.AuditResultSuccess, "created", "", "")
}); err != nil {
return nil, errors.Wrap(errors.CodeDatabaseError, err, "创建批量订购任务失败")
}
var enqueueErr error
@@ -82,10 +98,19 @@ func (s *Service) Create(ctx context.Context, req *dto.CreateAssetPackageBatchOr
}
if enqueueErr != nil {
message := "批量订购任务入队失败"
_ = s.taskStore.MarkFailed(ctx, task.ID, message)
task.Status, task.ErrorMessage = asynctask.StatusFailed, message
now := time.Now()
task.CompletedAt = &now
secondaryErr := s.taskStore.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
before := batchOrderTaskState(task)
if err := s.taskStore.WithTx(tx).MarkFailed(ctx, task.ID, message); err != nil {
return err
}
task.Status, task.ErrorMessage = asynctask.StatusFailed, message
now := time.Now()
task.CompletedAt = &now
return s.writeBatchOrderTaskAudit(ctx, tx, task, before, batchOrderTaskState(task), constants.AuditResultFailed, "enqueue_failed", strconv.Itoa(errors.CodeTaskQueueError), message)
})
if secondaryErr != nil {
auditfailure.RecordSecondaryWriteFailure(constants.AuditActionAssetPackageBatchOrderTaskCreated, task.TaskNo, "", task.TaskNo, strconv.Itoa(errors.CodeTaskQueueError), secondaryErr)
}
}
return toResponse(task), nil
}