新增代理注册申请管理
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m41s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m41s
This commit is contained in:
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/middleware"
|
||||
)
|
||||
|
||||
// RegistrationResult 返回已落库的待审批注册记录与审批实例引用。
|
||||
@@ -33,24 +34,86 @@ type RegistrationService struct {
|
||||
db *gorm.DB
|
||||
verifier VerificationCodeVerifier
|
||||
approval approvalapp.Port
|
||||
recovery approvalapp.RecoveryPort
|
||||
audit AuditWriter
|
||||
}
|
||||
|
||||
// NewRegistrationService 创建公开扫码注册用例。
|
||||
func NewRegistrationService(
|
||||
db *gorm.DB,
|
||||
verifier VerificationCodeVerifier,
|
||||
approval approvalapp.Port,
|
||||
audit AuditWriter,
|
||||
) *RegistrationService {
|
||||
func NewRegistrationService(db *gorm.DB, verifier VerificationCodeVerifier, approval approvalapp.Port, audit AuditWriter) *RegistrationService {
|
||||
return &RegistrationService{db: db, verifier: verifier, approval: approval, audit: audit}
|
||||
}
|
||||
|
||||
// Register 创建待审批注册记录。
|
||||
// 分销码无效、上级店铺停用、上级店铺缺少启用的主账号、短信验证码无效分别返回各自的错误码与提示。
|
||||
// 短信验证码只在注册记录与审批实例落库成功后消费:落库前的任何失败都不消费验证码,重试无需重新获取。
|
||||
// 手机号、用户名或店铺编号与既有账号/店铺冲突时返回对应已存在错误码;
|
||||
// 与其它待审批注册记录冲突时返回资源冲突错误并指明冲突字段。
|
||||
// SetRecoveryPort 注入通用审批原实例恢复接缝。
|
||||
func (s *RegistrationService) SetRecoveryPort(recovery approvalapp.RecoveryPort) {
|
||||
s.recovery = recovery
|
||||
}
|
||||
|
||||
// RecoverApproval 恢复代理注册原审批提交事实,不创建新审批实例或业务实体。
|
||||
func (s *RegistrationService) RecoverApproval(ctx context.Context, id uint) error {
|
||||
userType := middleware.GetUserTypeFromContext(ctx)
|
||||
if userType == constants.UserTypeEnterprise || (userType != constants.UserTypeSuperAdmin && userType != constants.UserTypePlatform && userType != constants.UserTypeAgent) {
|
||||
return errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
|
||||
}
|
||||
if middleware.GetUserIDFromContext(ctx) == 0 {
|
||||
return errors.New(errors.CodeUnauthorized)
|
||||
}
|
||||
if s == nil || s.db == nil || s.recovery == nil {
|
||||
return errors.New(errors.CodeServiceUnavailable, "代理注册审批恢复能力尚未配置")
|
||||
}
|
||||
return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
registration, err := lockRegistrationForUpdate(ctx, tx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if registration.Status != constants.AgentDistributionRegistrationStatusPending {
|
||||
return errors.New(errors.CodeConflict, "终态申请不可恢复")
|
||||
}
|
||||
var parent model.Shop
|
||||
if err := tx.Where("id = ?", registration.ParentShopID).First(&parent).Error; err != nil || parent.Status != constants.StatusEnabled {
|
||||
return errors.New(errors.CodeConflict, "上级店铺已停用,不能恢复审批")
|
||||
}
|
||||
if userType == constants.UserTypeAgent && (middleware.GetShopIDFromContext(ctx) == 0 || middleware.GetShopIDFromContext(ctx) != registration.ParentShopID) {
|
||||
return errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
|
||||
}
|
||||
if registration.ApprovalInstanceID == nil || *registration.ApprovalInstanceID == 0 {
|
||||
return errors.New(errors.CodeConflict, "注册申请未关联审批实例")
|
||||
}
|
||||
instanceID := *registration.ApprovalInstanceID
|
||||
var instance model.ApprovalInstance
|
||||
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ? AND business_type = ? AND business_id = ?", instanceID, constants.ApprovalBusinessTypeAgentDistribution, registration.ID).First(&instance).Error; err != nil {
|
||||
return errors.New(errors.CodeConflict, "审批实例业务关联不一致")
|
||||
}
|
||||
var wc model.WeComApprovalContext
|
||||
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("approval_instance_id = ? AND business_type = ?", instanceID, constants.ApprovalBusinessTypeAgentDistribution).First(&wc).Error; err != nil {
|
||||
return errors.New(errors.CodeConflict, "企业微信审批上下文不存在")
|
||||
}
|
||||
branch := "active"
|
||||
var recoveryErr error
|
||||
if wc.SPNo != "" {
|
||||
branch = "sp_no"
|
||||
recoveryErr = s.recovery.EnqueueSubmittedSync(ctx, tx, instanceID)
|
||||
} else if wc.SubmissionStatus == constants.WeComSubmissionStatusUnknown || instance.Status == constants.ApprovalStatusSubmissionUnknown {
|
||||
branch = "unknown"
|
||||
recoveryErr = s.recovery.EnqueueUnknownConfirm(ctx, tx, instanceID)
|
||||
} else if instance.Status == constants.ApprovalStatusSubmissionFailed && wc.SubmissionStatus == constants.WeComSubmissionStatusFailed {
|
||||
branch = "replay"
|
||||
_, recoveryErr = s.recovery.RecoverSubmissionEvent(ctx, tx, instanceID)
|
||||
} else if instance.Status == constants.ApprovalStatusSubmitting || instance.Status == constants.ApprovalStatusPending || wc.SubmissionStatus == constants.WeComSubmissionStatusReady || wc.SubmissionStatus == constants.WeComSubmissionStatusSending {
|
||||
} else {
|
||||
return errors.New(errors.CodeConflict, "当前审批提交状态不允许恢复")
|
||||
}
|
||||
if recoveryErr != nil {
|
||||
return errors.Wrap(errors.CodeConflict, recoveryErr, "恢复审批提交失败")
|
||||
}
|
||||
if s.audit != nil {
|
||||
if err := s.audit.WriteDistributionWithdrawal(ctx, tx, AuditChange{EventID: "agent-distribution:" + uintText(id) + ":recover:" + branch, ActionCode: constants.AuditActionApprovalSubmissionRecovered, Summary: "人工恢复代理注册审批提交", Registration: registration, ParentShop: &parent, BeforeData: map[string]any{"submission_status": wc.SubmissionStatus, "branch": branch}, AfterData: map[string]any{"result": "accepted"}}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// 已通过或已驳回的终态记录不阻塞重新注册;同一关键字段的并发提交由事务级 advisory lock 串行裁决。
|
||||
func (s *RegistrationService) Register(
|
||||
ctx context.Context,
|
||||
|
||||
Reference in New Issue
Block a user