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

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

@@ -3,8 +3,10 @@ package iot_card_import
import (
"context"
"path/filepath"
"strconv"
"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/model/dto"
"github.com/break/junhong_cmp_fiber/internal/store"
@@ -23,6 +25,7 @@ type Service struct {
carrierStore carrierGetter
queueClient *queue.Client
assetAudit AssetAuditService
auditWriter *audit.Writer
}
type carrierGetter interface {
@@ -50,14 +53,19 @@ func New(
importTaskStore *postgres.IotCardImportTaskStore,
queueClient *queue.Client,
assetAudit AssetAuditService,
auditWriters ...*audit.Writer,
) *Service {
return &Service{
service := &Service{
db: db,
importTaskStore: importTaskStore,
carrierStore: NewCarrierStore(db),
queueClient: queueClient,
assetAudit: assetAudit,
}
if len(auditWriters) > 0 {
service.auditWriter = auditWriters[0]
}
return service
}
type IotCardImportPayload struct {
@@ -67,16 +75,12 @@ type IotCardImportPayload struct {
func (s *Service) CreateImportTask(ctx context.Context, req *dto.ImportIotCardRequest) (*dto.ImportIotCardResponse, error) {
userID := middleware.GetUserIDFromContext(ctx)
if userID == 0 {
appErr := errors.New(errors.CodeUnauthorized, "未授权访问")
s.logIotCardImportAudit(ctx, newIotCardImportAuditParams(0, "", req, constants.AssetAuditResultDenied, appErr))
return nil, appErr
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
}
carrier, err := s.carrierStore.GetByID(ctx, req.CarrierID)
if err != nil {
appErr := errors.New(errors.CodeInvalidParam, "运营商不存在")
s.logIotCardImportAudit(ctx, newIotCardImportAuditParams(0, "", req, constants.AssetAuditResultDenied, appErr))
return nil, appErr
return nil, errors.New(errors.CodeInvalidParam, "运营商不存在")
}
taskNo := s.importTaskStore.GenerateTaskNo(ctx)
@@ -103,9 +107,17 @@ func (s *Service) CreateImportTask(ctx context.Context, req *dto.ImportIotCardRe
task.Creator = userID
task.Updater = userID
if err := s.importTaskStore.Create(ctx, task); err != nil {
if s.auditWriter == nil {
return nil, errors.New(errors.CodeInvalidStatus, "IoT 卡导入任务统一审计接缝未配置")
}
if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.WithContext(ctx).Create(task).Error; err != nil {
return err
}
return s.writeImportTaskAudit(ctx, tx, task, nil, importTaskState(task), constants.AuditResultSuccess, "created", "", "")
}); err != nil {
appErr := errors.Wrap(errors.CodeInternalError, err, "创建导入任务失败")
s.logIotCardImportAudit(ctx, newIotCardImportAuditParams(0, taskNo, req, constants.AssetAuditResultFailed, appErr))
s.recordImportTaskAudit(ctx, task, nil, importTaskState(task), constants.AuditResultFailed, "create_failed", errors.CodeDatabaseError, "创建 IoT 卡导入任务失败")
return nil, appErr
}
@@ -117,14 +129,23 @@ func (s *Service) CreateImportTask(ctx context.Context, req *dto.ImportIotCardRe
asynq.Queue(constants.QueueForTaskType(constants.TaskTypeIotCardImport)),
)
if err != nil {
s.importTaskStore.UpdateStatus(ctx, task.ID, model.ImportTaskStatusFailed, "任务入队失败: "+err.Error())
secondaryErr := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
before := importTaskState(task)
if updateErr := tx.WithContext(ctx).Model(&model.IotCardImportTask{}).Where("id = ?", task.ID).Updates(map[string]any{
"status": model.ImportTaskStatusFailed, "error_message": "任务入队失败", "completed_at": time.Now(), "updated_at": time.Now(),
}).Error; updateErr != nil {
return updateErr
}
task.Status, task.ErrorMessage = model.ImportTaskStatusFailed, "任务入队失败"
return s.writeImportTaskAudit(ctx, tx, task, before, importTaskState(task), constants.AuditResultFailed, "enqueue_failed", strconv.Itoa(errors.CodeTaskQueueError), "IoT 卡导入任务入队失败")
})
if secondaryErr != nil {
s.recordImportTaskAudit(ctx, task, nil, importTaskState(task), constants.AuditResultFailed, "enqueue_audit_failed", errors.CodeTaskQueueError, "IoT 卡导入任务入队失败")
}
appErr := errors.Wrap(errors.CodeInternalError, err, "任务入队失败")
s.logIotCardImportAudit(ctx, newIotCardImportAuditParams(task.ID, taskNo, req, constants.AssetAuditResultFailed, appErr))
return nil, appErr
}
s.logIotCardImportAudit(ctx, newIotCardImportAuditParams(task.ID, taskNo, req, constants.AssetAuditResultSuccess, nil))
return &dto.ImportIotCardResponse{
TaskID: task.ID,
TaskNo: taskNo,