This commit is contained in:
2026-07-26 22:24:57 +08:00
parent e51056e0eb
commit 6631f8d382
6 changed files with 144 additions and 0 deletions

View File

@@ -45,6 +45,9 @@ type CertificationApplicationService interface {
// AdminCompleteCertificationWithoutContract 管理员代用户完成认证(暂不关联合同) // AdminCompleteCertificationWithoutContract 管理员代用户完成认证(暂不关联合同)
AdminCompleteCertificationWithoutContract(ctx context.Context, cmd *commands.AdminCompleteCertificationCommand) (*responses.CertificationResponse, error) AdminCompleteCertificationWithoutContract(ctx context.Context, cmd *commands.AdminCompleteCertificationCommand) (*responses.CertificationResponse, error)
// AdminResetForResignContract 管理员触发重新认证(回退到提交企业信息,保留钱包/API Key/历史合同,不改 is_certified
AdminResetForResignContract(ctx context.Context, cmd *commands.AdminResetForResignContractCommand) (*responses.CertificationResponse, error)
// AdminListSubmitRecords 管理端分页查询企业信息提交记录 // AdminListSubmitRecords 管理端分页查询企业信息提交记录
AdminListSubmitRecords(ctx context.Context, query *queries.AdminListSubmitRecordsQuery) (*responses.AdminSubmitRecordsListResponse, error) AdminListSubmitRecords(ctx context.Context, query *queries.AdminListSubmitRecordsQuery) (*responses.AdminSubmitRecordsListResponse, error)
// AdminGetSubmitRecordByID 管理端获取单条提交记录详情 // AdminGetSubmitRecordByID 管理端获取单条提交记录详情

View File

@@ -1003,6 +1003,48 @@ func (s *CertificationApplicationServiceImpl) AdminCompleteCertificationWithoutC
return response, nil return response, nil
} }
// AdminResetForResignContract 管理员触发重新认证(回退到提交企业信息)
// 回退到 pending用户可重新填写企业信息并走完整流程不改 is_certified不动钱包/API Key/历史合同
func (s *CertificationApplicationServiceImpl) AdminResetForResignContract(
ctx context.Context,
cmd *commands.AdminResetForResignContractCommand,
) (*responses.CertificationResponse, error) {
s.logger.Info("管理员触发重新认证",
zap.String("admin_id", cmd.AdminID),
zap.String("user_id", cmd.UserID),
zap.String("reason", cmd.Reason),
)
if strings.TrimSpace(cmd.UserID) == "" {
return nil, fmt.Errorf("用户ID不能为空")
}
if strings.TrimSpace(cmd.Reason) == "" {
return nil, fmt.Errorf("请填写操作原因")
}
cert, err := s.aggregateService.LoadCertificationByUserID(ctx, cmd.UserID)
if err != nil {
return nil, fmt.Errorf("加载认证信息失败: %s", err.Error())
}
if err := cert.ResetForResignContract(cmd.AdminID, fmt.Sprintf("管理员触发重新认证:%s", cmd.Reason)); err != nil {
return nil, err
}
if err := s.aggregateService.SaveCertification(ctx, cert); err != nil {
return nil, fmt.Errorf("保存认证信息失败: %s", err.Error())
}
response := s.convertToResponse(cert)
s.logger.Info("管理员触发重新认证成功",
zap.String("admin_id", cmd.AdminID),
zap.String("user_id", cmd.UserID),
zap.String("certification_id", cert.ID),
zap.String("status", string(cert.Status)),
)
return response, nil
}
// AdminListSubmitRecords 管理端分页查询企业信息提交记录 // AdminListSubmitRecords 管理端分页查询企业信息提交记录
func (s *CertificationApplicationServiceImpl) AdminListSubmitRecords( func (s *CertificationApplicationServiceImpl) AdminListSubmitRecords(
ctx context.Context, ctx context.Context,

View File

@@ -85,6 +85,13 @@ type AdminCompleteCertificationCommand struct {
// 备注信息,用于记录后台操作原因 // 备注信息,用于记录后台操作原因
Reason string `json:"reason" validate:"required"` Reason string `json:"reason" validate:"required"`
} }
// AdminResetForResignContractCommand 管理员触发用户重新认证(回退到提交企业信息,保留钱包/API Key/历史记录)
type AdminResetForResignContractCommand struct {
AdminID string `json:"-"`
UserID string `json:"user_id" validate:"required"`
Reason string `json:"reason" validate:"required,min=2,max=500"`
}
// ForceTransitionStatusCommand 强制状态转换命令(管理员) // ForceTransitionStatusCommand 强制状态转换命令(管理员)
type ForceTransitionStatusCommand struct { type ForceTransitionStatusCommand struct {
CertificationID string `json:"certification_id" validate:"required"` CertificationID string `json:"certification_id" validate:"required"`

View File

@@ -552,6 +552,57 @@ func (c *Certification) ResetAfterSignTaskCreateFailed() error {
return nil return nil
} }
// ResetForResignContract 管理员触发重新认证:回退到 pending用户可重新填写企业信息并走完整入驻流程
// 不改动用户域认证标记、钱包、API Key、历史合同记录
func (c *Certification) ResetForResignContract(actorID, reason string) error {
switch c.Status {
case enums.StatusCompleted, enums.StatusContractSigned, enums.StatusContractApplied,
enums.StatusContractRejected, enums.StatusContractExpired,
enums.StatusEnterpriseVerified, enums.StatusInfoSubmitted, enums.StatusInfoPendingReview:
// 允许从已进入流程的各状态重置,供用户重新填写企业信息
default:
if c.Status == enums.StatusPending {
return fmt.Errorf("当前已是待提交企业信息状态,无需重置")
}
return fmt.Errorf("当前状态 %s 不允许重新认证", enums.GetStatusName(c.Status))
}
oldStatus := c.Status
now := time.Now()
c.Status = enums.StatusPending
c.SignPlatform = ""
c.AuthFlowID = ""
c.AuthURL = ""
c.ContractCode = ""
c.ContractFileID = ""
c.EsignFlowID = ""
c.ContractURL = ""
c.ContractSignURL = ""
c.InfoSubmittedAt = nil
c.EnterpriseVerifiedAt = nil
c.ContractAppliedAt = nil
c.ContractSignedAt = nil
c.CompletedAt = nil
c.ContractFileCreatedAt = nil
c.RetryCount = 0
c.clearFailureInfo()
c.updateTransitionAudit(enums.ActorTypeAdmin, actorID)
c.UpdatedAt = now
c.addDomainEvent(&CertificationStatusChangedEvent{
CertificationID: c.ID,
UserID: c.UserID,
FromStatus: oldStatus,
ToStatus: enums.StatusPending,
Actor: enums.ActorTypeAdmin,
ActorID: actorID,
Reason: reason,
TransitionedAt: now,
})
return nil
}
// SetSignPlatform 设置签署平台(选定后锁定) // SetSignPlatform 设置签署平台(选定后锁定)
func (c *Certification) SetSignPlatform(platform enums.SignPlatform) error { func (c *Certification) SetSignPlatform(platform enums.SignPlatform) error {
if !enums.IsValidSignPlatform(platform) { if !enums.IsValidSignPlatform(platform) {

View File

@@ -559,6 +559,46 @@ func (h *CertificationHandler) AdminCompleteCertificationWithoutContract(c *gin.
h.response.Success(c, result, "代用户完成认证成功") h.response.Success(c, result, "代用户完成认证成功")
} }
// AdminResetForResignContract 管理员触发重新认证
// @Summary 管理员触发重新认证
// @Description 将认证回退到 pending用户可重新填写企业信息并走完整入驻流程保留钱包、API Key、历史合同与 is_certified
// @Tags 认证管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body commands.AdminResetForResignContractCommand true "管理员重新认证请求"
// @Success 200 {object} responses.CertificationResponse "操作成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 403 {object} map[string]interface{} "权限不足"
// @Router /api/v1/certifications/admin/reset-for-resign-contract [post]
func (h *CertificationHandler) AdminResetForResignContract(c *gin.Context) {
adminID := h.getCurrentUserID(c)
if adminID == "" {
h.response.Unauthorized(c, "用户未登录")
return
}
var cmd commands.AdminResetForResignContractCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
cmd.AdminID = adminID
result, err := h.appService.AdminResetForResignContract(c.Request.Context(), &cmd)
if err != nil {
h.logger.Error("管理员触发重新认证失败",
zap.Error(err),
zap.String("admin_id", adminID),
zap.String("user_id", cmd.UserID),
)
h.response.BadRequest(c, err.Error())
return
}
h.response.Success(c, result, "已重置为重新认证,请通知用户前往企业入驻重新填写企业信息")
}
// AdminListSubmitRecords 管理端分页查询企业信息提交记录 // AdminListSubmitRecords 管理端分页查询企业信息提交记录
// @Summary 管理端企业审核列表 // @Summary 管理端企业审核列表
// @Tags 认证管理 // @Tags 认证管理

View File

@@ -90,6 +90,7 @@ func (r *CertificationRoutes) Register(router *http.GinRouter) {
adminGroup.Use(r.admin.Handle()) adminGroup.Use(r.admin.Handle())
{ {
adminGroup.POST("/transition-status", r.handler.AdminTransitionCertificationStatus) adminGroup.POST("/transition-status", r.handler.AdminTransitionCertificationStatus)
adminGroup.POST("/reset-for-resign-contract", r.handler.AdminResetForResignContract)
} }
adminCertGroup := adminGroup.Group("/submit-records") adminCertGroup := adminGroup.Group("/submit-records")
{ {