固化七月迭代审计治理进展以隔离线上热修

Constraint: 切换 main 前必须保存当前七月分支全部项目进展,套餐生效提案仅属于 Iteration/7-11。

Rejected: 将七月套餐修复直接移植到 main | 两个分支的可靠投递架构不同。

Confidence: medium

Scope-risk: broad

Directive: 不得将本提交整体 cherry-pick 到 main;main 套餐热修必须基于其纯 Asynq 代码独立实施。

Tested: git diff --check;openspec validate fix-package-activation-starvation --strict。

Not-tested: 按用户要求未运行自动化测试;go build ./... 因当前审计改造中的 Enterprise 模型字面量和 role.recordFailure 参数类型错误未通过。
This commit is contained in:
2026-08-03 09:47:22 +08:00
parent cf2ff0ac1c
commit b3499adfca
114 changed files with 16961 additions and 2782 deletions

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"time"
accessauditapp "github.com/break/junhong_cmp_fiber/internal/application/accessaudit"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
"github.com/break/junhong_cmp_fiber/pkg/constants"
@@ -12,26 +13,33 @@ import (
"github.com/break/junhong_cmp_fiber/pkg/middleware"
"go.uber.org/zap"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type AuthorizationService struct {
db *gorm.DB
enterpriseStore *postgres.EnterpriseStore
iotCardStore *postgres.IotCardStore
authorizationStore *postgres.EnterpriseCardAuthorizationStore
logger *zap.Logger
accessAudit accessauditapp.Writer
}
func NewAuthorizationService(
db *gorm.DB,
enterpriseStore *postgres.EnterpriseStore,
iotCardStore *postgres.IotCardStore,
authorizationStore *postgres.EnterpriseCardAuthorizationStore,
logger *zap.Logger,
accessAudit accessauditapp.Writer,
) *AuthorizationService {
return &AuthorizationService{
db: db,
enterpriseStore: enterpriseStore,
iotCardStore: iotCardStore,
authorizationStore: authorizationStore,
logger: logger,
accessAudit: accessAudit,
}
}
@@ -405,6 +413,9 @@ func (s *AuthorizationService) UpdateRecordRemark(ctx context.Context, id uint,
if userID == 0 {
return nil, errors.New(errors.CodeUnauthorized, "用户信息无效")
}
if s.db == nil || s.accessAudit == nil {
return nil, errors.New(errors.CodeInvalidStatus, "企业卡授权审计接缝未配置")
}
record, err := s.authorizationStore.GetByIDWithJoin(ctx, id)
if err != nil {
@@ -420,23 +431,97 @@ func (s *AuthorizationService) UpdateRecordRemark(ctx context.Context, id uint,
case constants.UserTypeAgent:
// 代理用户: 只能修改自己创建的授权记录
if record.AuthorizedBy != userID {
return nil, errors.New(errors.CodeForbidden, "只能修改自己创建的授权记录备注")
err := errors.New(errors.CodeForbidden, "只能修改自己创建的授权记录备注")
s.recordRemarkFailure(ctx, record, err)
return nil, err
}
case constants.UserTypeEnterprise:
// 企业用户: 禁止修改授权记录备注
return nil, errors.New(errors.CodeForbidden, "企业用户不允许修改授权记录备注")
err := errors.New(errors.CodeForbidden, "企业用户不允许修改授权记录备注")
s.recordRemarkFailure(ctx, record, err)
return nil, err
default:
return nil, errors.New(errors.CodeForbidden, "无权限修改授权记录备注")
}
if err := s.authorizationStore.UpdateRemarkWithConstraint(ctx, id, remark, record.AuthorizedBy); err != nil {
if err == gorm.ErrRecordNotFound {
return nil, errors.New(errors.CodeNotFound, "授权记录不存在")
}
err := errors.New(errors.CodeForbidden, "无权限修改授权记录备注")
s.recordRemarkFailure(ctx, record, err)
return nil, err
}
return s.GetRecordDetail(ctx, id)
var enterprise model.Enterprise
var card model.IotCard
var auth model.EnterpriseCardAuthorization
var ownerShop *model.Shop
err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&auth, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return errors.New(errors.CodeNotFound, "授权记录不存在")
}
return errors.Wrap(errors.CodeDatabaseError, err, "查询授权记录失败")
}
if userType == constants.UserTypeAgent && auth.AuthorizedBy != userID {
return errors.New(errors.CodeForbidden, "只能修改自己创建的授权记录备注")
}
if err := tx.First(&enterprise, auth.EnterpriseID).Error; err != nil {
return errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
}
if err := tx.First(&card, auth.CardID).Error; err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "查询授权卡失败")
}
if enterprise.OwnerShopID != nil {
var shop model.Shop
if err := tx.Unscoped().First(&shop, *enterprise.OwnerShopID).Error; err == nil {
ownerShop = &shop
}
}
beforeRemark := auth.Remark
if beforeRemark == remark {
return nil
}
if err := tx.Model(&model.EnterpriseCardAuthorization{}).Where("id = ?", auth.ID).Update("remark", remark).Error; err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "更新授权备注失败")
}
auth.Remark = remark
return s.accessAudit.WriteAccessChange(ctx, tx, accessauditapp.ChangeAudit{
ActionCode: constants.AuditActionEnterpriseCardRemarkUpdated, Summary: "更新企业卡授权备注",
OperatorID: userID, Enterprise: &enterprise, Shop: ownerShop,
Cards: []accessauditapp.IotCardChange{{
Card: &card, Relation: constants.AuditResourceRelationReference,
SubjectVisibility: constants.AuditSubjectInternalOnly,
}},
CardAuthorizations: []accessauditapp.EnterpriseCardAuthorizationChange{{
Authorization: &auth,
BeforeData: map[string]any{"remark": beforeRemark}, AfterData: map[string]any{"remark": remark},
}},
})
})
if err != nil {
s.recordRemarkFailure(ctx, record, err)
return nil, err
}
result, err := s.GetRecordDetail(ctx, id)
if err != nil {
return nil, err
}
return result, nil
}
func (s *AuthorizationService) recordRemarkFailure(ctx context.Context, record *postgres.AuthorizationWithJoin, originalErr error) {
if record == nil {
return
}
enterprise, _ := s.enterpriseStore.GetByID(ctx, record.EnterpriseID)
card, _ := s.iotCardStore.GetByID(ctx, record.CardID)
auth := &model.EnterpriseCardAuthorization{
ID: record.ID, EnterpriseID: record.EnterpriseID, CardID: record.CardID,
AuthorizedBy: record.AuthorizedBy, AuthorizerType: record.AuthorizerType,
AuthorizedAt: record.AuthorizedAt, RevokedBy: record.RevokedBy, RevokedAt: record.RevokedAt, Remark: record.Remark,
}
accessauditapp.RecordFailure(ctx, s.db, s.accessAudit, accessauditapp.ChangeAudit{
ActionCode: constants.AuditActionEnterpriseCardRemarkUpdated, Summary: "更新企业卡授权备注失败",
Result: enterpriseCardFailureResult(originalErr), OperatorID: middleware.GetUserIDFromContext(ctx),
Enterprise: enterprise, Cards: []accessauditapp.IotCardChange{{Card: card}},
CardAuthorizations: []accessauditapp.EnterpriseCardAuthorizationChange{{Authorization: auth}},
}, originalErr)
}
func parseDate(dateStr string) (time.Time, error) {