收口审计治理与套餐任务进展
Constraint: 在线热修前必须保存当前迭代分支全部有效代码进展 Confidence: medium Scope-risk: broad Directive: 后续修改需保持审计事件与业务事务边界一致 Tested: git diff --cached --check Not-tested: 未运行全量测试,提交用于切换分支前保存既有工作
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
||||
systemconfigapp "github.com/break/junhong_cmp_fiber/internal/application/systemconfig"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||
"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"
|
||||
@@ -90,7 +91,7 @@ func NewConnectionService(db *gorm.DB, repo ApplicationRepository, tokens Access
|
||||
|
||||
// Save 创建或更新企业微信应用配置。
|
||||
func (s *ConnectionService) Save(ctx context.Context, request dto.SaveWeComApplicationRequest) (*dto.WeComApplicationResponse, error) {
|
||||
if s == nil || s.db == nil || s.repo == nil {
|
||||
if s == nil || s.db == nil || s.repo == nil || s.audit == nil {
|
||||
return nil, errors.New(errors.CodeServiceUnavailable, "企业微信连接服务未配置")
|
||||
}
|
||||
if middleware.GetUserTypeFromContext(ctx) != constants.UserTypeSuperAdmin {
|
||||
@@ -140,10 +141,12 @@ func (s *ConnectionService) Save(ctx context.Context, request dto.SaveWeComAppli
|
||||
if value := middleware.GetRequestIDFromContext(ctx); value != nil {
|
||||
requestID = *value
|
||||
}
|
||||
resourceID := fmt.Sprintf("%d", existing.ID)
|
||||
if err := s.audit.WriteConfigChange(ctx, tx, systemconfigapp.ChangeAudit{
|
||||
OperatorID: operatorID, OperationType: "wecom_application_save", Description: "保存企业微信应用安全配置",
|
||||
OperatorID: operatorID, OperationType: constants.AuditOperationWeComApplicationSave, Description: "保存企业微信应用安全配置",
|
||||
ConfigKey: fmt.Sprintf("wecom.application.%d", existing.ID), BeforeData: before,
|
||||
AfterData: applicationAuditSnapshot(existing), RequestID: requestID, CorrelationID: requestID,
|
||||
ResourceID: &resourceID, DisplayName: existing.Name, Identity: applicationAuditIdentity(existing),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -152,6 +155,14 @@ func (s *ConnectionService) Save(ctx context.Context, request dto.SaveWeComAppli
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
recordConfigFailure(ctx, s.db, s.audit, systemconfigapp.ChangeAudit{
|
||||
OperatorID: operatorID, OperationType: constants.AuditOperationWeComApplicationSave,
|
||||
Description: "保存企业微信应用配置失败", ConfigKey: fmt.Sprintf("wecom.application.%s.%d", request.CorpID, request.AgentID),
|
||||
DisplayName: request.Name, Identity: map[string]any{
|
||||
"corp_id": request.CorpID, "agent_id": request.AgentID, "name": request.Name, "status": request.Status,
|
||||
"credentials_configured": request.Secret != "" && request.CallbackToken != "" && request.EncodingAESKey != "",
|
||||
}, Result: constants.AuditResultFailed, ErrorCode: fmt.Sprintf("%d", errors.CodeDatabaseError), ErrorSummary: "企业微信应用配置事务已回滚",
|
||||
})
|
||||
var appErr *errors.AppError
|
||||
if stdErrors.As(err, &appErr) {
|
||||
return nil, appErr
|
||||
@@ -240,7 +251,7 @@ func (s *ConnectionService) Test(ctx context.Context, applicationID uint) error
|
||||
|
||||
// SaveDefaultCreator 从应用当前可见成员中保存代理等账号使用的默认审批发起人。
|
||||
func (s *ConnectionService) SaveDefaultCreator(ctx context.Context, applicationID uint, request dto.SaveWeComDefaultCreatorRequest) (*dto.WeComApplicationResponse, error) {
|
||||
if s == nil || s.db == nil || s.repo == nil || s.members == nil {
|
||||
if s == nil || s.db == nil || s.repo == nil || s.members == nil || s.audit == nil {
|
||||
return nil, errors.New(errors.CodeServiceUnavailable, "企业微信默认审批发起人服务未配置")
|
||||
}
|
||||
if middleware.GetUserTypeFromContext(ctx) != constants.UserTypeSuperAdmin {
|
||||
@@ -256,6 +267,7 @@ func (s *ConnectionService) SaveDefaultCreator(ctx context.Context, applicationI
|
||||
}
|
||||
member, err := s.members.GetVisible(ctx, applicationID, request.UserID)
|
||||
if err != nil {
|
||||
recordApplicationFailure(ctx, s.db, s.audit, constants.AuditOperationWeComDefaultCreatorSave, "拒绝保存不可用的企业微信默认审批发起人", application, constants.AuditResultDenied, errors.CodeInvalidParam)
|
||||
return nil, err
|
||||
}
|
||||
now := s.now().UTC()
|
||||
@@ -273,15 +285,18 @@ func (s *ConnectionService) SaveDefaultCreator(ctx context.Context, applicationI
|
||||
if value := middleware.GetRequestIDFromContext(ctx); value != nil {
|
||||
requestID = *value
|
||||
}
|
||||
resourceID := fmt.Sprintf("%d", applicationID)
|
||||
return s.audit.WriteConfigChange(ctx, tx, systemconfigapp.ChangeAudit{
|
||||
OperatorID: operatorID, OperationType: "wecom_default_creator_save", Description: "保存企业微信默认审批发起人",
|
||||
OperatorID: operatorID, OperationType: constants.AuditOperationWeComDefaultCreatorSave, Description: "保存企业微信默认审批发起人",
|
||||
ConfigKey: fmt.Sprintf("wecom.application.%d.default_creator", applicationID), BeforeData: before,
|
||||
AfterData: applicationAuditSnapshot(application), RequestID: requestID, CorrelationID: requestID,
|
||||
ResourceID: &resourceID, DisplayName: application.Name, Identity: applicationAuditIdentity(application),
|
||||
})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
recordApplicationFailure(ctx, s.db, s.audit, constants.AuditOperationWeComDefaultCreatorSave, "保存企业微信默认审批发起人失败", application, constants.AuditResultFailed, errors.CodeDatabaseError)
|
||||
var appErr *errors.AppError
|
||||
if stdErrors.As(err, &appErr) {
|
||||
return nil, appErr
|
||||
@@ -295,12 +310,52 @@ func (s *ConnectionService) SaveDefaultCreator(ctx context.Context, applicationI
|
||||
func applicationAuditSnapshot(application *model.WeComApplication) map[string]any {
|
||||
return map[string]any{
|
||||
"id": application.ID, "corp_id": application.CorpID, "agent_id": application.AgentID,
|
||||
"name": application.Name, "status": application.Status, "credentials_configured": true,
|
||||
"name": application.Name, "status": application.Status,
|
||||
"credentials_configured": application.Secret != "" && application.CallbackToken != "" && application.EncodingAESKey != "",
|
||||
"default_creator_userid": application.DefaultCreatorUserID,
|
||||
"default_creator_name": application.DefaultCreatorName,
|
||||
}
|
||||
}
|
||||
|
||||
func applicationAuditIdentity(application *model.WeComApplication) map[string]any {
|
||||
if application == nil {
|
||||
return nil
|
||||
}
|
||||
return map[string]any{
|
||||
"id": application.ID, "corp_id": application.CorpID, "agent_id": application.AgentID,
|
||||
"name": application.Name, "status": application.Status,
|
||||
"credentials_configured": application.Secret != "" && application.CallbackToken != "" && application.EncodingAESKey != "",
|
||||
}
|
||||
}
|
||||
|
||||
func recordApplicationFailure(ctx context.Context, db *gorm.DB, audit systemconfigapp.AuditWriter, operation, description string, application *model.WeComApplication, result string, code int) {
|
||||
if application == nil {
|
||||
return
|
||||
}
|
||||
resourceID := fmt.Sprintf("%d", application.ID)
|
||||
recordConfigFailure(ctx, db, audit, systemconfigapp.ChangeAudit{
|
||||
OperatorID: middleware.GetUserIDFromContext(ctx), OperationType: operation, Description: description,
|
||||
ConfigKey: fmt.Sprintf("wecom.application.%d", application.ID), ResourceID: &resourceID,
|
||||
DisplayName: application.Name, Identity: applicationAuditIdentity(application), BeforeData: applicationAuditSnapshot(application),
|
||||
Result: result, ErrorCode: fmt.Sprintf("%d", code), ErrorSummary: description,
|
||||
})
|
||||
}
|
||||
|
||||
func recordConfigFailure(ctx context.Context, db *gorm.DB, audit systemconfigapp.AuditWriter, change systemconfigapp.ChangeAudit) {
|
||||
if db == nil || audit == nil || change.OperatorID == 0 || change.ConfigKey == "" {
|
||||
return
|
||||
}
|
||||
if value := middleware.GetRequestIDFromContext(ctx); value != nil {
|
||||
change.RequestID = *value
|
||||
change.CorrelationID = *value
|
||||
}
|
||||
if err := db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
return audit.WriteConfigChange(ctx, tx, change)
|
||||
}); err != nil {
|
||||
auditfailure.RecordSecondaryWriteFailure(change.OperationType, change.ConfigKey, change.RequestID, change.CorrelationID, change.ErrorCode, err)
|
||||
}
|
||||
}
|
||||
|
||||
func toApplicationResponse(application model.WeComApplication) dto.WeComApplicationResponse {
|
||||
statusName := "禁用"
|
||||
if application.Status == constants.StatusEnabled {
|
||||
|
||||
Reference in New Issue
Block a user