暂存
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m32s

This commit is contained in:
2026-08-06 09:35:00 +08:00
parent 8659dfc658
commit 88cc5e96ec
75 changed files with 6520 additions and 513 deletions

View File

@@ -83,10 +83,6 @@ func (s *Service) Get(ctx context.Context, id uint) (*dto.CarrierResponse, error
}
return nil, errors.Wrap(errors.CodeInternalError, err, "获取运营商失败")
}
if s.audit == nil {
return nil, errors.New(errors.CodeInvalidStatus, "运营商配置审计接缝未配置")
}
before := *carrier
return s.toResponse(carrier), nil
}
@@ -103,6 +99,7 @@ func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateCarrierReq
}
return nil, errors.Wrap(errors.CodeInternalError, err, "获取运营商失败")
}
before := *carrier
if req.CarrierName != nil {
carrier.CarrierName = *req.CarrierName

View File

@@ -65,12 +65,12 @@ func (s *Service) AllocateDevices(ctx context.Context, enterpriseID uint, req *d
return nil, errors.New(errors.CodeInvalidStatus, "企业设备授权审计接缝未配置")
}
if err := validateEnterpriseDeviceActor(ctx); err != nil {
s.recordDeviceFailure(ctx, constants.AuditActionEnterpriseDevicesAllocated, "向企业授权设备被拒绝", &model.Enterprise{ID: enterpriseID}, nil, nil, err)
s.recordDeviceFailure(ctx, constants.AuditActionEnterpriseDevicesAllocated, "向企业授权设备被拒绝", &model.Enterprise{Model: gorm.Model{ID: enterpriseID}}, nil, nil, err)
return nil, err
}
if err := middleware.CanManageEnterprise(ctx, enterpriseID, s.enterpriseStore); err != nil {
permissionErr := errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
s.recordDeviceFailure(ctx, constants.AuditActionEnterpriseDevicesAllocated, "向企业授权设备被拒绝", &model.Enterprise{ID: enterpriseID}, nil, nil, permissionErr)
s.recordDeviceFailure(ctx, constants.AuditActionEnterpriseDevicesAllocated, "向企业授权设备被拒绝", &model.Enterprise{Model: gorm.Model{ID: enterpriseID}}, nil, nil, permissionErr)
return nil, permissionErr
}
enterprise, err := s.enterpriseStore.GetByID(ctx, enterpriseID)
@@ -417,12 +417,12 @@ func (s *Service) RecallDevices(ctx context.Context, enterpriseID uint, req *dto
return nil, errors.New(errors.CodeInvalidStatus, "企业设备授权审计接缝未配置")
}
if err := validateEnterpriseDeviceActor(ctx); err != nil {
s.recordDeviceFailure(ctx, constants.AuditActionEnterpriseDevicesRecalled, "回收企业设备授权被拒绝", &model.Enterprise{ID: enterpriseID}, nil, nil, err)
s.recordDeviceFailure(ctx, constants.AuditActionEnterpriseDevicesRecalled, "回收企业设备授权被拒绝", &model.Enterprise{Model: gorm.Model{ID: enterpriseID}}, nil, nil, err)
return nil, err
}
if err := middleware.CanManageEnterprise(ctx, enterpriseID, s.enterpriseStore); err != nil {
permissionErr := errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
s.recordDeviceFailure(ctx, constants.AuditActionEnterpriseDevicesRecalled, "回收企业设备授权被拒绝", &model.Enterprise{ID: enterpriseID}, nil, nil, permissionErr)
s.recordDeviceFailure(ctx, constants.AuditActionEnterpriseDevicesRecalled, "回收企业设备授权被拒绝", &model.Enterprise{Model: gorm.Model{ID: enterpriseID}}, nil, nil, permissionErr)
return nil, permissionErr
}
enterprise, err := s.enterpriseStore.GetByID(ctx, enterpriseID)

View File

@@ -23,38 +23,46 @@ func (s *Service) SetAccessAudit(writer *audit.Writer) {
s.auditWriter = writer
}
// WriteCardStateAudit 将卡观测事务中的人工状态操作写入统一 Audit Event。
// WriteCardStateAudit 将卡观测事务中的人工、回调或 Worker 状态变化写入统一 Audit Event。
func (s *Service) WriteCardStateAudit(ctx context.Context, tx *gorm.DB, input cardapp.StateAudit) error {
extraResources := make([]audit.ResourceInput, 0, 1)
if input.IntegrationID != "" {
extraResources = append(extraResources, callbackIntegrationAuditResource(ctx, input.IntegrationID))
extraResources = append(extraResources, cardStateIntegrationAuditResource(ctx, input.IntegrationID))
}
return s.appendCardLifecycleAudit(ctx, tx, input.ActionCode, input.Summary, constants.AuditResultSuccess,
input.Card, input.BeforeData, input.AfterData, nil, extraResources...)
}
// WriteCardStateFailure 使用独立短事务记录已解析卡资源后的回调失败。
// WriteCardStateFailure 使用独立短事务记录已解析卡资源后的回调或 Worker 失败。
func (s *Service) WriteCardStateFailure(ctx context.Context, input cardapp.StateAudit, businessErr error) {
extraResources := make([]audit.ResourceInput, 0, 1)
if input.IntegrationID != "" {
extraResources = append(extraResources, callbackIntegrationAuditResource(ctx, input.IntegrationID))
extraResources = append(extraResources, cardStateIntegrationAuditResource(ctx, input.IntegrationID))
}
s.recordCardLifecycleFailure(ctx, input.ActionCode, input.Summary, constants.AuditResultFailed,
input.Card, input.Card.ID, businessErr, extraResources...)
}
func callbackIntegrationAuditResource(ctx context.Context, integrationID string) audit.ResourceInput {
func cardStateIntegrationAuditResource(ctx context.Context, integrationID string) audit.ResourceInput {
linkage := auditcontext.From(ctx)
correlationID := linkage.CorrelationID
if correlationID == "" {
correlationID = linkage.RequestID
}
direction := constants.IntegrationDirectionInbound
role := constants.AuditResourceRoleCallbackIntegration
provider := linkage.ActorID
if linkage.Source == constants.AuditSourceWorker {
direction = constants.IntegrationDirectionOutbound
role = constants.AuditResourceRoleWorkerIntegration
provider = constants.IntegrationProviderGateway
}
return audit.ResourceInput{
Type: constants.AuditResourceIntegrationLog, Key: integrationID, DisplayName: integrationID,
Relation: constants.AuditResourceRelationReference, Role: constants.AuditResourceRoleCallbackIntegration,
Relation: constants.AuditResourceRelationReference, Role: role,
IdentitySnapshot: map[string]any{
"integration_id": integrationID, "provider": linkage.ActorID,
"direction": constants.IntegrationDirectionInbound, "correlation_id": correlationID,
"integration_id": integrationID, "provider": provider,
"direction": direction, "correlation_id": correlationID,
},
SubjectVisibility: constants.AuditSubjectInternalOnly,
}

View File

@@ -2709,8 +2709,12 @@ func (s *Service) enqueueCommissionCalculation(ctx context.Context, orderID uint
return
}
linkage := auditcontext.From(ctx)
// 直接传 map由 EnqueueTask 内部统一序列化一次(传 []byte 会导致 sonic.Marshal 二次 base64 编码)
if err := s.queueClient.EnqueueTask(ctx, constants.TaskTypeCommission, map[string]any{"order_id": orderID}); err != nil {
if err := s.queueClient.EnqueueTask(ctx, constants.TaskTypeCommission, map[string]any{
"order_id": orderID, "request_id": linkage.RequestID, "correlation_id": linkage.CorrelationID,
"parent_event_id": linkage.ParentEventID,
}); err != nil {
s.logger.Error("佣金计算任务入队失败",
zap.Uint("order_id", orderID),
zap.Error(err),

View File

@@ -14,6 +14,7 @@ import (
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// ResumeCallback 复机回调接口

View File

@@ -9,6 +9,7 @@ import (
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
"github.com/break/junhong_cmp_fiber/internal/task"
"github.com/break/junhong_cmp_fiber/pkg/auditcontext"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/break/junhong_cmp_fiber/pkg/queue"
@@ -227,7 +228,11 @@ func (s *Service) HandlePaymentCallback(ctx context.Context, paymentNo string, p
linkedIDs := rechargeOrder.LinkedPackageIDs
if len(linkedIDs) > 0 {
taskPayload := task.AutoPurchasePayload{RechargeOrderID: rechargeOrder.ID}
linkage := auditcontext.From(ctx)
taskPayload := task.AutoPurchasePayload{
RechargeOrderID: rechargeOrder.ID, RequestID: linkage.RequestID,
CorrelationID: linkage.CorrelationID, ParentEventID: linkage.ParentEventID,
}
if err := s.queueClient.EnqueueTask(ctx, constants.TaskTypeAutoPurchaseAfterRecharge, taskPayload,
asynq.MaxRetry(3),
asynq.Queue(constants.QueueForTaskType(constants.TaskTypeAutoPurchaseAfterRecharge)),

View File

@@ -132,12 +132,12 @@ func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateRoleReques
if req.RoleName != nil && *req.RoleName != role.RoleName {
exists, err := s.roleStore.ExistsByName(ctx, *req.RoleName, id)
if err != nil {
s.recordFailure(ctx, constants.AuditActionRoleUpdated, "更新角色失败", constants.AuditResultFailed, role, beforeData, nil, err)
s.recordFailure(ctx, constants.AuditActionRoleUpdated, "更新角色失败", constants.AuditResultFailed, role, nil, beforeData, err)
return nil, errors.Wrap(errors.CodeInternalError, err, "检查角色名失败")
}
if exists {
appErr := errors.New(errors.CodeRoleNameExists)
s.recordFailure(ctx, constants.AuditActionRoleUpdated, "拒绝更新重复角色名", constants.AuditResultDenied, role, beforeData, nil, appErr)
s.recordFailure(ctx, constants.AuditActionRoleUpdated, "拒绝更新重复角色名", constants.AuditResultDenied, role, nil, beforeData, appErr)
return nil, appErr
}
role.RoleName = *req.RoleName
@@ -162,7 +162,7 @@ func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateRoleReques
OperatorID: currentUserID, Role: role, BeforeData: beforeData, AfterData: roleAuditData(role),
})
}); err != nil {
s.recordFailure(ctx, constants.AuditActionRoleUpdated, "更新角色失败", constants.AuditResultFailed, role, beforeData, nil, err)
s.recordFailure(ctx, constants.AuditActionRoleUpdated, "更新角色失败", constants.AuditResultFailed, role, nil, beforeData, err)
return nil, errors.Wrap(errors.CodeInternalError, err, "更新角色失败")
}
@@ -181,19 +181,19 @@ func (s *Service) Delete(ctx context.Context, id uint) error {
accountCount, err := s.accountRoleStore.CountByRoleID(ctx, id)
if err != nil {
s.recordFailure(ctx, constants.AuditActionRoleDeleted, "删除角色失败", constants.AuditResultFailed, role, roleAuditData(role), nil, err)
s.recordFailure(ctx, constants.AuditActionRoleDeleted, "删除角色失败", constants.AuditResultFailed, role, nil, roleAuditData(role), err)
return errors.Wrap(errors.CodeInternalError, err, "检查角色分配情况失败")
}
shopCount, err := s.shopRoleStore.CountByRoleID(ctx, id)
if err != nil {
s.recordFailure(ctx, constants.AuditActionRoleDeleted, "删除角色失败", constants.AuditResultFailed, role, roleAuditData(role), nil, err)
s.recordFailure(ctx, constants.AuditActionRoleDeleted, "删除角色失败", constants.AuditResultFailed, role, nil, roleAuditData(role), err)
return errors.Wrap(errors.CodeInternalError, err, "检查角色分配情况失败")
}
if accountCount > 0 || shopCount > 0 {
appErr := errors.New(errors.CodeRoleInUse, fmt.Sprintf("该角色已分配给 %d 个账号、%d 个店铺,请先移除相关分配后再删除", accountCount, shopCount))
s.recordFailure(ctx, constants.AuditActionRoleDeleted, "拒绝删除使用中的角色", constants.AuditResultDenied, role, roleAuditData(role), nil, appErr)
s.recordFailure(ctx, constants.AuditActionRoleDeleted, "拒绝删除使用中的角色", constants.AuditResultDenied, role, nil, roleAuditData(role), appErr)
return appErr
}
@@ -208,7 +208,7 @@ func (s *Service) Delete(ctx context.Context, id uint) error {
OperatorID: operatorID, Role: role, BeforeData: beforeData, AfterData: map[string]any{"deleted": true},
})
}); err != nil {
s.recordFailure(ctx, constants.AuditActionRoleDeleted, "删除角色失败", constants.AuditResultFailed, role, beforeData, nil, err)
s.recordFailure(ctx, constants.AuditActionRoleDeleted, "删除角色失败", constants.AuditResultFailed, role, nil, beforeData, err)
return errors.Wrap(errors.CodeInternalError, err, "删除角色失败")
}
@@ -510,19 +510,19 @@ func (s *Service) UpdateStatus(ctx context.Context, id uint, status int) error {
if status == constants.StatusDisabled {
accountCount, err := s.accountRoleStore.CountByRoleID(ctx, id)
if err != nil {
s.recordFailure(ctx, constants.AuditActionRoleStatusUpdated, "更新角色状态失败", constants.AuditResultFailed, role, roleAuditData(role), nil, err)
s.recordFailure(ctx, constants.AuditActionRoleStatusUpdated, "更新角色状态失败", constants.AuditResultFailed, role, nil, roleAuditData(role), err)
return errors.Wrap(errors.CodeInternalError, err, "检查角色分配情况失败")
}
shopCount, err := s.shopRoleStore.CountByRoleID(ctx, id)
if err != nil {
s.recordFailure(ctx, constants.AuditActionRoleStatusUpdated, "更新角色状态失败", constants.AuditResultFailed, role, roleAuditData(role), nil, err)
s.recordFailure(ctx, constants.AuditActionRoleStatusUpdated, "更新角色状态失败", constants.AuditResultFailed, role, nil, roleAuditData(role), err)
return errors.Wrap(errors.CodeInternalError, err, "检查角色分配情况失败")
}
if accountCount > 0 || shopCount > 0 {
appErr := errors.New(errors.CodeRoleInUse, fmt.Sprintf("该角色已分配给 %d 个账号、%d 个店铺,请先移除相关分配后再禁用", accountCount, shopCount))
s.recordFailure(ctx, constants.AuditActionRoleStatusUpdated, "拒绝禁用使用中的角色", constants.AuditResultDenied, role, roleAuditData(role), nil, appErr)
s.recordFailure(ctx, constants.AuditActionRoleStatusUpdated, "拒绝禁用使用中的角色", constants.AuditResultDenied, role, nil, roleAuditData(role), appErr)
return appErr
}
}
@@ -540,7 +540,7 @@ func (s *Service) UpdateStatus(ctx context.Context, id uint, status int) error {
OperatorID: currentUserID, Role: role, BeforeData: beforeData, AfterData: roleAuditData(role),
})
}); err != nil {
s.recordFailure(ctx, constants.AuditActionRoleStatusUpdated, "更新角色状态失败", constants.AuditResultFailed, role, beforeData, nil, err)
s.recordFailure(ctx, constants.AuditActionRoleStatusUpdated, "更新角色状态失败", constants.AuditResultFailed, role, nil, beforeData, err)
return errors.Wrap(errors.CodeInternalError, err, "更新角色状态失败")
}