f
This commit is contained in:
@@ -127,6 +127,35 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
}
|
||||
}
|
||||
|
||||
// 0.5 已通过人工审核或已进入后续流程:幂等返回当前认证数据(不调 e签宝、不新建提交记录)
|
||||
existsCertEarly, err := s.aggregateService.ExistsByUserID(ctx, cmd.UserID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("检查认证记录失败: %w", err)
|
||||
}
|
||||
if existsCertEarly {
|
||||
certEarly, loadErr := s.aggregateService.LoadCertificationByUserID(ctx, cmd.UserID)
|
||||
if loadErr != nil {
|
||||
return nil, fmt.Errorf("加载认证信息失败: %w", loadErr)
|
||||
}
|
||||
switch certEarly.Status {
|
||||
case enums.StatusInfoSubmitted, enums.StatusEnterpriseVerified, enums.StatusContractApplied,
|
||||
enums.StatusContractSigned, enums.StatusCompleted, enums.StatusContractRejected, enums.StatusContractExpired:
|
||||
meta, metaErr := s.AddStatusMetadata(ctx, certEarly)
|
||||
if metaErr != nil {
|
||||
return nil, metaErr
|
||||
}
|
||||
resp := s.convertToResponse(certEarly)
|
||||
if meta != nil {
|
||||
resp.Metadata = meta
|
||||
} else {
|
||||
resp.Metadata = map[string]interface{}{}
|
||||
}
|
||||
resp.Metadata["next_action"] = enums.GetUserActionHint(certEarly.Status)
|
||||
s.logger.Info("企业信息提交幂等返回", zap.String("user_id", cmd.UserID), zap.String("status", string(certEarly.Status)))
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 1.5 插入企业信息提交记录(包含扩展字段)
|
||||
record := entities.NewEnterpriseInfoSubmitRecord(
|
||||
cmd.UserID,
|
||||
@@ -301,50 +330,12 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
return fmt.Errorf("加载认证信息失败: %s", err.Error())
|
||||
}
|
||||
|
||||
// 4. 提交企业信息:暂时跳过人工审核,直接进入「已提交」状态(第三步企业认证)
|
||||
// 恢复人工审核时改为 cert.SubmitEnterpriseInfoForReview(enterpriseInfo),并将 next_action 改为「请等待管理员审核企业信息」
|
||||
|
||||
authReq := &esign.EnterpriseAuthRequest{
|
||||
CompanyName: enterpriseInfo.CompanyName,
|
||||
UnifiedSocialCode: enterpriseInfo.UnifiedSocialCode,
|
||||
LegalPersonName: enterpriseInfo.LegalPersonName,
|
||||
LegalPersonID: enterpriseInfo.LegalPersonID,
|
||||
TransactorName: enterpriseInfo.LegalPersonName,
|
||||
TransactorMobile: enterpriseInfo.LegalPersonPhone,
|
||||
TransactorID: enterpriseInfo.LegalPersonID,
|
||||
// 4. 提交企业信息:进入人工审核(三真/企业信息审核);e签宝链接仅在管理员审核通过后生成(见 AdminApproveSubmitRecord)
|
||||
if err := cert.SubmitEnterpriseInfoForReview(enterpriseInfo); err != nil {
|
||||
return fmt.Errorf("提交企业信息失败: %s", err.Error())
|
||||
}
|
||||
authURL, alreadyVerified, err := s.generateEnterpriseAuthOrDetectVerified(txCtx, authReq)
|
||||
if err != nil {
|
||||
s.logger.Error("步骤5.2-生成企业认证链接失败",
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.Error(err))
|
||||
return fmt.Errorf("生成企业认证链接失败: %w", err)
|
||||
}
|
||||
if alreadyVerified {
|
||||
s.logger.Info("步骤5.2-检测到企业已实名,进入自动完成认证流程",
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.String("company_name", enterpriseInfo.CompanyName))
|
||||
// 三方侧已实名:先进入 info_submitted,再在同事务内自动推进到 enterprise_verified。
|
||||
if err = cert.SubmitEnterpriseInfo(enterpriseInfo, "", ""); err != nil {
|
||||
return fmt.Errorf("提交企业信息失败: %s", err.Error())
|
||||
}
|
||||
if err = s.completeEnterpriseVerification(txCtx, cert, cert.UserID, enterpriseInfo.CompanyName, enterpriseInfo.LegalPersonName); err != nil {
|
||||
return fmt.Errorf("自动完成企业认证失败: %w", err)
|
||||
}
|
||||
s.logger.Info("步骤5.2-自动完成企业认证成功", zap.String("user_id", cmd.UserID))
|
||||
} else {
|
||||
s.logger.Info("步骤5.2-企业未实名,写入认证链接并等待用户操作",
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.String("auth_flow_id", authURL.AuthFlowID))
|
||||
err = cert.SubmitEnterpriseInfo(enterpriseInfo, authURL.AuthShortURL, authURL.AuthFlowID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("提交企业信息失败: %s", err.Error())
|
||||
}
|
||||
err = s.aggregateService.SaveCertification(txCtx, cert)
|
||||
if err != nil {
|
||||
return fmt.Errorf("保存认证信息失败: %s", err.Error())
|
||||
}
|
||||
s.logger.Info("步骤5.2-认证信息保存成功(待用户完成企业认证)", zap.String("user_id", cmd.UserID))
|
||||
if err := s.aggregateService.SaveCertification(txCtx, cert); err != nil {
|
||||
return fmt.Errorf("保存认证信息失败: %s", err.Error())
|
||||
}
|
||||
|
||||
// 5. 提交记录与认证状态在同一事务内保存
|
||||
@@ -355,20 +346,24 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.String("record_id", record.ID))
|
||||
|
||||
var enterpriseInfoMeta map[string]interface{}
|
||||
if raw, mErr := json.Marshal(enterpriseInfo); mErr == nil {
|
||||
_ = json.Unmarshal(raw, &enterpriseInfoMeta)
|
||||
}
|
||||
if enterpriseInfoMeta == nil {
|
||||
enterpriseInfoMeta = map[string]interface{}{}
|
||||
}
|
||||
enterpriseInfoMeta["submit_at"] = record.SubmitAt.Format(time.RFC3339)
|
||||
|
||||
respMeta := map[string]interface{}{
|
||||
"enterprise_info": enterpriseInfo,
|
||||
"enterprise_info": enterpriseInfoMeta,
|
||||
"polling": map[string]interface{}{
|
||||
"enabled": !alreadyVerified,
|
||||
"enabled": false,
|
||||
"endpoint": "/api/v1/certifications/confirm-auth",
|
||||
"interval_seconds": 3,
|
||||
},
|
||||
}
|
||||
if alreadyVerified {
|
||||
respMeta["next_action"] = "企业已实名,已自动完成认证,可进入合同签署"
|
||||
respMeta["target_view"] = "contract_sign"
|
||||
} else {
|
||||
respMeta["next_action"] = "请完成企业认证"
|
||||
respMeta["target_view"] = "enterprise_auth"
|
||||
"next_action": "请等待管理员审核企业信息",
|
||||
"target_view": "manual_review",
|
||||
}
|
||||
// 6. 转换为响应 DTO
|
||||
response = s.convertToResponse(cert)
|
||||
@@ -379,13 +374,30 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 提醒管理员处理待审核申请(配置企业微信 Webhook 时生效)
|
||||
if s.wechatWorkService != nil {
|
||||
contactPhone := cmd.LegalPersonPhone
|
||||
if strings.TrimSpace(cmd.AuthorizedRepPhone) != "" {
|
||||
contactPhone = fmt.Sprintf("法人 %s;授权代表 %s", cmd.LegalPersonPhone, cmd.AuthorizedRepPhone)
|
||||
} else {
|
||||
contactPhone = fmt.Sprintf("%s(法人)", cmd.LegalPersonPhone)
|
||||
}
|
||||
_ = s.wechatWorkService.SendCertificationNotification(ctx, "pending_manual_review", map[string]interface{}{
|
||||
"company_name": cmd.CompanyName,
|
||||
"legal_person_name": cmd.LegalPersonName,
|
||||
"authorized_rep_name": cmd.AuthorizedRepName,
|
||||
"contact_phone": contactPhone,
|
||||
"api_usage": cmd.APIUsage,
|
||||
"submit_at": record.SubmitAt.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
|
||||
s.logger.Info("企业信息提交成功", zap.String("user_id", cmd.UserID))
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// 审核状态检查(步骤二)
|
||||
// 规则:企业信息提交成功后进入待审核;审核通过后才允许进行企业认证确认(ConfirmAuth)。
|
||||
// 当前暂时跳过人工审核(待审核状态视为通过);启用审核时恢复对 StatusInfoPendingReview 返回错误。
|
||||
func (s *CertificationApplicationServiceImpl) checkAuditStatus(ctx context.Context, cert *entities.Certification) error {
|
||||
switch cert.Status {
|
||||
case enums.StatusInfoSubmitted,
|
||||
@@ -395,9 +407,7 @@ func (s *CertificationApplicationServiceImpl) checkAuditStatus(ctx context.Conte
|
||||
enums.StatusCompleted:
|
||||
return nil
|
||||
case enums.StatusInfoPendingReview:
|
||||
// 暂时跳过人工审核:待审核状态视为通过,后续启用审核时还原为 return fmt.Errorf("企业信息已提交,正在审核中")
|
||||
s.logger.Info("跳过人工审核状态检查", zap.String("user_id", cert.UserID))
|
||||
return nil
|
||||
return fmt.Errorf("企业信息已提交,正在审核中")
|
||||
case enums.StatusInfoRejected:
|
||||
return fmt.Errorf("企业信息审核未通过")
|
||||
default:
|
||||
@@ -979,7 +989,15 @@ func (s *CertificationApplicationServiceImpl) AdminApproveSubmitRecord(ctx conte
|
||||
if err := cert.ApproveEnterpriseInfoReview("", "", adminID); err != nil {
|
||||
return fmt.Errorf("更新认证状态失败: %w", err)
|
||||
}
|
||||
return s.completeEnterpriseVerification(ctx, cert, cert.UserID, record.CompanyName, record.LegalPersonName)
|
||||
if err := s.completeEnterpriseVerification(ctx, cert, cert.UserID, record.CompanyName, record.LegalPersonName); err != nil {
|
||||
return err
|
||||
}
|
||||
record.MarkManualApproved(adminID, remark)
|
||||
if err := s.enterpriseInfoSubmitRecordService.Save(ctx, record); err != nil {
|
||||
return fmt.Errorf("保存企业信息提交记录失败: %w", err)
|
||||
}
|
||||
s.logger.Info("管理员审核通过企业信息", zap.String("record_id", recordID), zap.String("admin_id", adminID))
|
||||
return nil
|
||||
}
|
||||
if err := cert.ApproveEnterpriseInfoReview(authURL.AuthShortURL, authURL.AuthFlowID, adminID); err != nil {
|
||||
return fmt.Errorf("更新认证状态失败: %w", err)
|
||||
@@ -987,6 +1005,10 @@ func (s *CertificationApplicationServiceImpl) AdminApproveSubmitRecord(ctx conte
|
||||
if err := s.aggregateService.SaveCertification(ctx, cert); err != nil {
|
||||
return fmt.Errorf("保存认证信息失败: %w", err)
|
||||
}
|
||||
record.MarkManualApproved(adminID, remark)
|
||||
if err := s.enterpriseInfoSubmitRecordService.Save(ctx, record); err != nil {
|
||||
return fmt.Errorf("保存企业信息提交记录失败: %w", err)
|
||||
}
|
||||
s.logger.Info("管理员审核通过企业信息", zap.String("record_id", recordID), zap.String("admin_id", adminID))
|
||||
return nil
|
||||
}
|
||||
@@ -1025,6 +1047,10 @@ func (s *CertificationApplicationServiceImpl) AdminRejectSubmitRecord(ctx contex
|
||||
if err := s.aggregateService.SaveCertification(ctx, cert); err != nil {
|
||||
return fmt.Errorf("保存认证信息失败: %w", err)
|
||||
}
|
||||
record.MarkManualRejected(adminID, remark)
|
||||
if err := s.enterpriseInfoSubmitRecordService.Save(ctx, record); err != nil {
|
||||
return fmt.Errorf("保存企业信息提交记录失败: %w", err)
|
||||
}
|
||||
s.logger.Info("管理员审核拒绝企业信息", zap.String("record_id", recordID), zap.String("admin_id", adminID))
|
||||
return nil
|
||||
}
|
||||
@@ -1071,7 +1097,15 @@ func (s *CertificationApplicationServiceImpl) AdminTransitionCertificationStatus
|
||||
if err := cert.ApproveEnterpriseInfoReview("", "", cmd.AdminID); err != nil {
|
||||
return fmt.Errorf("更新认证状态失败: %w", err)
|
||||
}
|
||||
return s.completeEnterpriseVerification(ctx, cert, cert.UserID, record.CompanyName, record.LegalPersonName)
|
||||
if err := s.completeEnterpriseVerification(ctx, cert, cert.UserID, record.CompanyName, record.LegalPersonName); err != nil {
|
||||
return err
|
||||
}
|
||||
record.MarkManualApproved(cmd.AdminID, cmd.Remark)
|
||||
if err := s.enterpriseInfoSubmitRecordService.Save(ctx, record); err != nil {
|
||||
return fmt.Errorf("保存企业信息提交记录失败: %w", err)
|
||||
}
|
||||
s.logger.Info("管理端变更认证状态为通过", zap.String("user_id", cmd.UserID), zap.String("admin_id", cmd.AdminID))
|
||||
return nil
|
||||
}
|
||||
if err := cert.ApproveEnterpriseInfoReview(authURL.AuthShortURL, authURL.AuthFlowID, cmd.AdminID); err != nil {
|
||||
return fmt.Errorf("更新认证状态失败: %w", err)
|
||||
@@ -1079,6 +1113,10 @@ func (s *CertificationApplicationServiceImpl) AdminTransitionCertificationStatus
|
||||
if err := s.aggregateService.SaveCertification(ctx, cert); err != nil {
|
||||
return fmt.Errorf("保存认证信息失败: %w", err)
|
||||
}
|
||||
record.MarkManualApproved(cmd.AdminID, cmd.Remark)
|
||||
if err := s.enterpriseInfoSubmitRecordService.Save(ctx, record); err != nil {
|
||||
return fmt.Errorf("保存企业信息提交记录失败: %w", err)
|
||||
}
|
||||
s.logger.Info("管理端变更认证状态为通过", zap.String("user_id", cmd.UserID), zap.String("admin_id", cmd.AdminID))
|
||||
return nil
|
||||
case string(enums.StatusInfoRejected):
|
||||
@@ -1096,6 +1134,10 @@ func (s *CertificationApplicationServiceImpl) AdminTransitionCertificationStatus
|
||||
if err := s.aggregateService.SaveCertification(ctx, cert); err != nil {
|
||||
return fmt.Errorf("保存认证信息失败: %w", err)
|
||||
}
|
||||
record.MarkManualRejected(cmd.AdminID, cmd.Remark)
|
||||
if err := s.enterpriseInfoSubmitRecordService.Save(ctx, record); err != nil {
|
||||
return fmt.Errorf("保存企业信息提交记录失败: %w", err)
|
||||
}
|
||||
s.logger.Info("管理端变更认证状态为拒绝", zap.String("user_id", cmd.UserID), zap.String("admin_id", cmd.AdminID))
|
||||
return nil
|
||||
default:
|
||||
@@ -1557,7 +1599,7 @@ func (s *CertificationApplicationServiceImpl) AddStatusMetadata(ctx context.Cont
|
||||
metadata := make(map[string]interface{})
|
||||
metadata = cert.GetDataByStatus()
|
||||
switch cert.Status {
|
||||
case enums.StatusPending, enums.StatusInfoSubmitted, enums.StatusEnterpriseVerified:
|
||||
case enums.StatusPending, enums.StatusInfoPendingReview, enums.StatusInfoRejected, enums.StatusInfoSubmitted, enums.StatusEnterpriseVerified:
|
||||
record, err := s.enterpriseInfoSubmitRecordRepo.FindLatestByUserID(ctx, cert.UserID)
|
||||
if err == nil && record != nil {
|
||||
enterpriseInfo := map[string]interface{}{
|
||||
@@ -1567,6 +1609,7 @@ func (s *CertificationApplicationServiceImpl) AddStatusMetadata(ctx context.Cont
|
||||
"enterprise_address": record.EnterpriseAddress,
|
||||
"legal_person_phone": record.LegalPersonPhone,
|
||||
"legal_person_id": record.LegalPersonID,
|
||||
"submit_at": record.SubmitAt.Format(time.RFC3339),
|
||||
}
|
||||
metadata["enterprise_info"] = enterpriseInfo
|
||||
}
|
||||
|
||||
@@ -134,6 +134,8 @@ func (s *WeChatWorkService) SendCertificationNotification(ctx context.Context, n
|
||||
switch notificationType {
|
||||
case "new_application":
|
||||
return s.sendNewApplicationNotification(ctx, data)
|
||||
case "pending_manual_review":
|
||||
return s.sendPendingManualReviewNotification(ctx, data)
|
||||
case "ocr_success":
|
||||
return s.sendOCRSuccessNotification(ctx, data)
|
||||
case "ocr_failed":
|
||||
@@ -177,6 +179,45 @@ func (s *WeChatWorkService) sendNewApplicationNotification(ctx context.Context,
|
||||
return s.SendMarkdownMessage(ctx, content)
|
||||
}
|
||||
|
||||
// sendPendingManualReviewNotification 用户已提交企业信息,待管理员人工审核(三真审核前序步骤)
|
||||
func (s *WeChatWorkService) sendPendingManualReviewNotification(ctx context.Context, data map[string]interface{}) error {
|
||||
companyName := fmt.Sprint(data["company_name"])
|
||||
legalPersonName := fmt.Sprint(data["legal_person_name"])
|
||||
authorizedRepName := fmt.Sprint(data["authorized_rep_name"])
|
||||
contactPhone := fmt.Sprint(data["contact_phone"])
|
||||
apiUsage := fmt.Sprint(data["api_usage"])
|
||||
submitAt := fmt.Sprint(data["submit_at"])
|
||||
|
||||
if authorizedRepName == "" || authorizedRepName == "<nil>" {
|
||||
authorizedRepName = "—"
|
||||
}
|
||||
if apiUsage == "" || apiUsage == "<nil>" {
|
||||
apiUsage = "—"
|
||||
}
|
||||
if contactPhone == "" || contactPhone == "<nil>" {
|
||||
contactPhone = "—"
|
||||
}
|
||||
|
||||
content := fmt.Sprintf(`## 【天远API】📋 企业信息待人工审核
|
||||
|
||||
**企业名称**: %s
|
||||
**法人**: %s
|
||||
**授权申请人**: %s
|
||||
**联系电话**: %s
|
||||
**应用场景说明**: %s
|
||||
**提交时间**: %s
|
||||
|
||||
> 请管理员登录后台 **企业审核** 通过审核后,用户方可进行 e签宝企业认证。`,
|
||||
companyName,
|
||||
legalPersonName,
|
||||
authorizedRepName,
|
||||
contactPhone,
|
||||
apiUsage,
|
||||
submitAt)
|
||||
|
||||
return s.SendMarkdownMessage(ctx, content)
|
||||
}
|
||||
|
||||
// sendOCRSuccessNotification 发送OCR识别成功通知
|
||||
func (s *WeChatWorkService) sendOCRSuccessNotification(ctx context.Context, data map[string]interface{}) error {
|
||||
companyName := data["company_name"].(string)
|
||||
@@ -391,14 +432,13 @@ func (s *WeChatWorkService) sendMessage(ctx context.Context, message map[string]
|
||||
isTimeout = true
|
||||
} else if netErr, ok := err.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
||||
isTimeout = true
|
||||
} else if errStr := err.Error();
|
||||
errStr == "context deadline exceeded" ||
|
||||
errStr == "timeout" ||
|
||||
errStr == "Client.Timeout exceeded" ||
|
||||
errStr == "net/http: request canceled" {
|
||||
} else if errStr := err.Error(); errStr == "context deadline exceeded" ||
|
||||
errStr == "timeout" ||
|
||||
errStr == "Client.Timeout exceeded" ||
|
||||
errStr == "net/http: request canceled" {
|
||||
isTimeout = true
|
||||
}
|
||||
|
||||
|
||||
errorMsg := "发送请求失败"
|
||||
if isTimeout {
|
||||
errorMsg = "发送请求超时"
|
||||
|
||||
@@ -113,6 +113,12 @@ func (m *DailyRateLimitMiddleware) Handle() gin.HandlerFunc {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
// 开发环境debug模式下跳过
|
||||
if m.config.Development.Debug {
|
||||
m.logger.Info("开发环境debug模式下跳过每日限流", zap.String("path", c.Request.URL.Path))
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否在排除域名中
|
||||
host := c.Request.Host
|
||||
|
||||
Reference in New Issue
Block a user