Files
junhong_cmp_fiber/internal/service/device/realname_policy_batch.go
break 5e552d99bc 收口审计治理与套餐任务进展
Constraint: 在线热修前必须保存当前迭代分支全部有效代码进展
Confidence: medium
Scope-risk: broad
Directive: 后续修改需保持审计事件与业务事务边界一致
Tested: git diff --cached --check
Not-tested: 未运行全量测试,提交用于切换分支前保存既有工作
2026-08-05 14:30:54 +08:00

85 lines
3.3 KiB
Go

package device
import (
"context"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/break/junhong_cmp_fiber/pkg/middleware"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// BatchUpdateRealnamePolicy 批量更新设备实名认证策略,整批校验后在单事务内全成全败。
func (s *Service) BatchUpdateRealnamePolicy(ctx context.Context, req *dto.BatchUpdateAssetRealnamePolicyRequest) (*dto.BatchUpdateAssetRealnamePolicyResponse, error) {
if middleware.GetUserTypeFromContext(ctx) == constants.UserTypeEnterprise {
return nil, errors.New(errors.CodeForbidden, "企业账号无权修改设备实名认证策略")
}
ids, err := validateBatchRealnamePolicyRequest(req)
if err != nil {
return nil, err
}
var devices []*model.Device
err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
query := middleware.ApplyShopFilter(ctx, tx.Model(&model.Device{})).Clauses(clause.Locking{Strength: "UPDATE"})
if err := query.Where("id IN ?", ids).Find(&devices).Error; err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "查询批量设备资产失败")
}
if len(devices) != len(ids) {
return errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
}
changedIDs := make([]uint, 0, len(devices))
for _, device := range devices {
if device != nil && device.RealnamePolicy != req.RealnamePolicy {
changedIDs = append(changedIDs, device.ID)
}
}
if len(changedIDs) > 0 {
result := tx.Model(&model.Device{}).Where("id IN ?", changedIDs).Update("realname_policy", req.RealnamePolicy)
if result.Error != nil {
return errors.Wrap(errors.CodeDatabaseError, result.Error, "批量更新设备实名认证策略失败")
}
if result.RowsAffected != int64(len(changedIDs)) {
return errors.New(errors.CodeConflict, "设备资产状态已变化,请刷新后重试")
}
}
return s.appendDeviceRealnamePolicyBatchAudit(ctx, tx, devices, req.RealnamePolicy)
})
if err != nil {
result := constants.AuditResultFailed
if appErr, ok := err.(*errors.AppError); ok && appErr.Code == errors.CodeForbidden {
result = constants.AuditResultDenied
}
s.recordDeviceRealnamePolicyBatchFailure(ctx, devices, req.RealnamePolicy, result, err)
return nil, err
}
return &dto.BatchUpdateAssetRealnamePolicyResponse{SuccessCount: len(ids), RealnamePolicy: req.RealnamePolicy}, nil
}
func validateBatchRealnamePolicyRequest(req *dto.BatchUpdateAssetRealnamePolicyRequest) ([]uint, error) {
if req == nil || len(req.AssetIDs) == 0 || len(req.AssetIDs) > 500 || !isValidRealnamePolicy(req.RealnamePolicy) {
return nil, errors.New(errors.CodeInvalidParam)
}
seen := make(map[uint]struct{}, len(req.AssetIDs))
ids := make([]uint, 0, len(req.AssetIDs))
for _, id := range req.AssetIDs {
if id == 0 {
return nil, errors.New(errors.CodeInvalidParam)
}
if _, exists := seen[id]; exists {
return nil, errors.New(errors.CodeInvalidParam, "资产ID不能重复")
}
seen[id] = struct{}{}
ids = append(ids, id)
}
return ids, nil
}
func isValidRealnamePolicy(policy string) bool {
return policy == constants.RealnamePolicyNone ||
policy == constants.RealnamePolicyBeforeOrder ||
policy == constants.RealnamePolicyAfterOrder
}