This commit is contained in:
Mrx
2026-03-19 13:23:48 +08:00
parent faf4b7f6a7
commit d837624c0a
12 changed files with 222 additions and 171 deletions

View File

@@ -41,10 +41,12 @@ type CertificationApplicationService interface {
AdminListSubmitRecords(ctx context.Context, query *queries.AdminListSubmitRecordsQuery) (*responses.AdminSubmitRecordsListResponse, error)
// AdminGetSubmitRecordByID 管理端获取单条提交记录详情
AdminGetSubmitRecordByID(ctx context.Context, recordID string) (*responses.AdminSubmitRecordDetail, error)
// AdminApproveSubmitRecord 管理端审核通过
// AdminApproveSubmitRecord 管理端审核通过(按提交记录 ID
AdminApproveSubmitRecord(ctx context.Context, recordID, adminID, remark string) error
// AdminRejectSubmitRecord 管理端审核拒绝
// AdminRejectSubmitRecord 管理端审核拒绝(按提交记录 ID
AdminRejectSubmitRecord(ctx context.Context, recordID, adminID, remark string) error
// AdminTransitionCertificationStatus 管理端按用户变更认证状态以状态机为准info_submitted=通过 / info_rejected=拒绝)
AdminTransitionCertificationStatus(ctx context.Context, cmd *commands.AdminTransitionCertificationStatusCommand) error
// ================ e签宝回调处理 ================

View File

@@ -109,10 +109,13 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
s.logger.Info("开始提交企业信息",
zap.String("user_id", cmd.UserID))
// 0. 若该用户已有待审核的提交记录,则不允许重复提交
// 0. 若该用户已有待审核(认证状态仍在待审核),则不允许重复提交
latestRecord, err := s.enterpriseInfoSubmitRecordRepo.FindLatestByUserID(ctx, cmd.UserID)
if err == nil && latestRecord != nil && latestRecord.ManualReviewStatus == "pending" {
return nil, fmt.Errorf("您已有待审核的提交,请等待管理员审核后再操作")
if err == nil && latestRecord != nil {
cert, loadErr := s.aggregateService.LoadCertificationByUserID(ctx, cmd.UserID)
if loadErr == nil && cert != nil && cert.Status == enums.StatusInfoPendingReview {
return nil, fmt.Errorf("您已有待审核的提交,请等待管理员审核后再操作")
}
}
// 1.5 插入企业信息提交记录(包含扩展字段)
@@ -259,16 +262,7 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
return fmt.Errorf("加载认证信息失败: %s", err.Error())
}
// 已是「已提交企业信息」:说明已通过人工审核,直接返回当前认证数据,前端可刷新到企业认证步骤
if cert.Status == enums.StatusInfoSubmitted {
response = s.convertToResponse(cert)
response.Metadata = map[string]interface{}{
"next_action": "您已通过审核,请完成企业认证步骤",
}
return nil
}
// 4. 提交企业信息进入人工审核(不调用 e签宝不生成认证链接
// 4. 提交企业信息进入人工审核(不在此处推断审核是否通过)
err = cert.SubmitEnterpriseInfoForReview(enterpriseInfo)
if err != nil {
return fmt.Errorf("提交企业信息失败: %s", err.Error())
@@ -303,6 +297,25 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
return response, nil
}
// 审核状态检查(步骤二)
// 规则企业信息提交成功后进入待审核审核通过后才允许进行企业认证确认ConfirmAuth
func (s *CertificationApplicationServiceImpl) checkAuditStatus(ctx context.Context, cert *entities.Certification) error {
switch cert.Status {
case enums.StatusInfoSubmitted,
enums.StatusEnterpriseVerified,
enums.StatusContractApplied,
enums.StatusContractSigned,
enums.StatusCompleted:
return nil
case enums.StatusInfoPendingReview:
return fmt.Errorf("企业信息已提交,正在审核中")
case enums.StatusInfoRejected:
return fmt.Errorf("企业信息审核未通过")
default:
return fmt.Errorf("认证状态不正确,当前状态: %s", enums.GetStatusName(cert.Status))
}
}
// ConfirmAuth 确认认证状态
func (s *CertificationApplicationServiceImpl) ConfirmAuth(
ctx context.Context,
@@ -314,9 +327,9 @@ func (s *CertificationApplicationServiceImpl) ConfirmAuth(
return nil, fmt.Errorf("加载认证信息失败: %s", err.Error())
}
// 企业认证
if cert.Status != enums.StatusInfoSubmitted {
return nil, fmt.Errorf("认证状态不正确,当前状态: %s", enums.GetStatusName(cert.Status))
// 步骤二:审核状态检查(审核通过后才能进入企业认证确认)
if err := s.checkAuditStatus(ctx, cert); err != nil {
return nil, err
}
record, err := s.enterpriseInfoSubmitRecordRepo.FindLatestByUserID(ctx, cert.UserID)
if err != nil {
@@ -730,12 +743,12 @@ func (s *CertificationApplicationServiceImpl) AdminListSubmitRecords(
query.Page = 1
}
filter := repositories.ListSubmitRecordsFilter{
Page: query.Page,
PageSize: query.PageSize,
ManualReviewStatus: query.ManualReviewStatus,
CompanyName: query.CompanyName,
LegalPersonPhone: query.LegalPersonPhone,
LegalPersonName: query.LegalPersonName,
Page: query.Page,
PageSize: query.PageSize,
CertificationStatus: query.CertificationStatus,
CompanyName: query.CompanyName,
LegalPersonPhone: query.LegalPersonPhone,
LegalPersonName: query.LegalPersonName,
}
result, err := s.enterpriseInfoSubmitRecordRepo.List(ctx, filter)
if err != nil {
@@ -755,8 +768,6 @@ func (s *CertificationApplicationServiceImpl) AdminListSubmitRecords(
LegalPersonName: r.LegalPersonName,
SubmitAt: r.SubmitAt,
Status: r.Status,
ManualReviewStatus: r.ManualReviewStatus,
ManualReviewedAt: r.ManualReviewedAt,
CertificationStatus: certStatus,
})
}
@@ -805,10 +816,6 @@ func (s *CertificationApplicationServiceImpl) AdminGetSubmitRecordByID(ctx conte
VerifiedAt: record.VerifiedAt,
FailedAt: record.FailedAt,
FailureReason: record.FailureReason,
ManualReviewStatus: record.ManualReviewStatus,
ManualReviewRemark: record.ManualReviewRemark,
ManualReviewedAt: record.ManualReviewedAt,
ManualReviewerID: record.ManualReviewerID,
CertificationStatus: certStatus,
CreatedAt: record.CreatedAt,
UpdatedAt: record.UpdatedAt,
@@ -821,17 +828,12 @@ func (s *CertificationApplicationServiceImpl) AdminApproveSubmitRecord(ctx conte
if err != nil {
return fmt.Errorf("获取提交记录失败: %w", err)
}
if record.ManualReviewStatus != "pending" {
return fmt.Errorf("该记录已审核,当前状态: %s", record.ManualReviewStatus)
}
cert, err := s.aggregateService.LoadCertificationByUserID(ctx, record.UserID)
if err != nil {
return fmt.Errorf("加载认证信息失败: %w", err)
}
// 兼容线上脏数据:认证已进入「已提交企业信息」或更后续状态,但提交记录仍是 pending
// 此时无需再走「待审核->通过」的状态机,只需要把提交记录补齐为 approved避免管理端无法操作。
// 说明:后续状态(如已企业认证/合同状态/完成)都意味着企业信息审核已经通过。
// 幂等:认证已进入「已提交企业信息」或更后续状态,说明已通过审核,无需重复操作
switch cert.Status {
case enums.StatusInfoSubmitted,
enums.StatusEnterpriseVerified,
@@ -840,31 +842,11 @@ func (s *CertificationApplicationServiceImpl) AdminApproveSubmitRecord(ctx conte
enums.StatusCompleted,
enums.StatusContractRejected,
enums.StatusContractExpired:
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),
zap.String("user_id", record.UserID),
zap.String("certification_status", string(cert.Status)),
)
return nil
}
// 兼容线上脏数据:提交记录已落库但当时事务失败导致认证仍为「待认证」,先同步为待审核再执行通过
if cert.Status != enums.StatusInfoPendingReview {
if err := s.syncCertToPendingReviewIfRecordPending(ctx, cert, record); err != nil {
return err
}
cert, err = s.aggregateService.LoadCertificationByUserID(ctx, record.UserID)
if err != nil {
return fmt.Errorf("加载认证信息失败: %w", err)
}
if cert.Status != enums.StatusInfoPendingReview {
return fmt.Errorf("认证状态不是待审核,当前: %s", enums.GetStatusName(cert.Status))
}
return fmt.Errorf("认证状态不是待审核,当前: %s", enums.GetStatusName(cert.Status))
}
enterpriseInfo := &certification_value_objects.EnterpriseInfo{
CompanyName: record.CompanyName,
@@ -886,10 +868,6 @@ func (s *CertificationApplicationServiceImpl) AdminApproveSubmitRecord(ctx conte
if 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)
}
if err := cert.ApproveEnterpriseInfoReview(authURL.AuthShortURL, authURL.AuthFlowID, adminID); err != nil {
return fmt.Errorf("更新认证状态失败: %w", err)
}
@@ -909,29 +887,24 @@ func (s *CertificationApplicationServiceImpl) AdminRejectSubmitRecord(ctx contex
if err != nil {
return fmt.Errorf("获取提交记录失败: %w", err)
}
if record.ManualReviewStatus != "pending" {
return fmt.Errorf("该记录已审核,当前状态: %s", record.ManualReviewStatus)
}
cert, err := s.aggregateService.LoadCertificationByUserID(ctx, record.UserID)
if err != nil {
return fmt.Errorf("加载认证信息失败: %w", err)
}
// 兼容线上脏数据:提交记录已落库但当时事务失败导致认证仍为「待认证」,先同步为待审核再执行拒绝
if cert.Status != enums.StatusInfoPendingReview {
if err := s.syncCertToPendingReviewIfRecordPending(ctx, cert, record); err != nil {
return err
}
cert, err = s.aggregateService.LoadCertificationByUserID(ctx, record.UserID)
if err != nil {
return fmt.Errorf("加载认证信息失败: %w", err)
}
if cert.Status != enums.StatusInfoPendingReview {
return fmt.Errorf("认证状态不是待审核,当前: %s", enums.GetStatusName(cert.Status))
}
// 幂等:认证已处于拒绝或后续状态,无需重复拒绝
switch cert.Status {
case enums.StatusInfoRejected,
enums.StatusEnterpriseVerified,
enums.StatusContractApplied,
enums.StatusContractSigned,
enums.StatusCompleted,
enums.StatusContractRejected,
enums.StatusContractExpired:
return nil
}
record.MarkManualRejected(adminID, remark)
if err := s.enterpriseInfoSubmitRecordService.Save(ctx, record); err != nil {
return fmt.Errorf("保存提交记录失败: %w", err)
if cert.Status != enums.StatusInfoPendingReview {
return fmt.Errorf("认证状态不是待审核,当前: %s", enums.GetStatusName(cert.Status))
}
if err := cert.RejectEnterpriseInfoReview(adminID, remark); err != nil {
return fmt.Errorf("更新认证状态失败: %w", err)
@@ -943,35 +916,75 @@ func (s *CertificationApplicationServiceImpl) AdminRejectSubmitRecord(ctx contex
return nil
}
// ================ 辅助方法 ================
// syncCertToPendingReviewIfRecordPending 兼容历史脏数据:当认证为「待认证」或「已拒绝」且存在待审核提交记录时,
// 用该记录的企业信息把认证同步为「待审核」,便于管理员直接审核通过/拒绝。
func (s *CertificationApplicationServiceImpl) syncCertToPendingReviewIfRecordPending(ctx context.Context, cert *entities.Certification, record *entities.EnterpriseInfoSubmitRecord) error {
if record.ManualReviewStatus != "pending" {
// AdminTransitionCertificationStatus 管理端按用户变更认证状态(以状态机为准)
func (s *CertificationApplicationServiceImpl) AdminTransitionCertificationStatus(ctx context.Context, cmd *commands.AdminTransitionCertificationStatusCommand) error {
cert, err := s.aggregateService.LoadCertificationByUserID(ctx, cmd.UserID)
if err != nil {
return fmt.Errorf("加载认证信息失败: %w", err)
}
record, err := s.enterpriseInfoSubmitRecordRepo.FindLatestByUserID(ctx, cmd.UserID)
if err != nil {
return fmt.Errorf("查找企业信息提交记录失败: %w", err)
}
if record == nil {
return fmt.Errorf("未找到该用户的企业信息提交记录")
}
switch cmd.TargetStatus {
case string(enums.StatusInfoSubmitted):
// 审核通过:与 AdminApproveSubmitRecord 一致,推状态并生成企业认证链接
switch cert.Status {
case enums.StatusInfoSubmitted, enums.StatusEnterpriseVerified, enums.StatusContractApplied,
enums.StatusContractSigned, enums.StatusCompleted, enums.StatusContractRejected, enums.StatusContractExpired:
return nil
}
if cert.Status != enums.StatusInfoPendingReview {
return fmt.Errorf("认证状态不是待审核,当前: %s", enums.GetStatusName(cert.Status))
}
enterpriseInfo := &certification_value_objects.EnterpriseInfo{
CompanyName: record.CompanyName, UnifiedSocialCode: record.UnifiedSocialCode,
LegalPersonName: record.LegalPersonName, LegalPersonID: record.LegalPersonID,
LegalPersonPhone: record.LegalPersonPhone, EnterpriseAddress: record.EnterpriseAddress,
}
authURL, err := s.esignClient.GenerateEnterpriseAuth(&esign.EnterpriseAuthRequest{
CompanyName: enterpriseInfo.CompanyName, UnifiedSocialCode: enterpriseInfo.UnifiedSocialCode,
LegalPersonName: enterpriseInfo.LegalPersonName, LegalPersonID: enterpriseInfo.LegalPersonID,
TransactorName: enterpriseInfo.LegalPersonName, TransactorMobile: enterpriseInfo.LegalPersonPhone, TransactorID: enterpriseInfo.LegalPersonID,
})
if err != nil {
return fmt.Errorf("生成企业认证链接失败: %w", err)
}
if err := cert.ApproveEnterpriseInfoReview(authURL.AuthShortURL, authURL.AuthFlowID, cmd.AdminID); err != nil {
return fmt.Errorf("更新认证状态失败: %w", err)
}
if err := s.aggregateService.SaveCertification(ctx, cert); 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):
// 审核拒绝
if cert.Status == enums.StatusInfoRejected || cert.Status == enums.StatusEnterpriseVerified ||
cert.Status == enums.StatusContractApplied || cert.Status == enums.StatusContractSigned || cert.Status == enums.StatusCompleted {
return nil
}
if cert.Status != enums.StatusInfoPendingReview {
return fmt.Errorf("认证状态不是待审核,当前: %s", enums.GetStatusName(cert.Status))
}
if err := cert.RejectEnterpriseInfoReview(cmd.AdminID, cmd.Remark); err != nil {
return fmt.Errorf("更新认证状态失败: %w", err)
}
if err := s.aggregateService.SaveCertification(ctx, cert); 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:
return fmt.Errorf("不支持的目标状态: %s", cmd.TargetStatus)
}
if cert.Status != enums.StatusPending && cert.Status != enums.StatusInfoRejected {
return fmt.Errorf("认证状态不是待审核,当前: %s", enums.GetStatusName(cert.Status))
}
enterpriseInfo := &certification_value_objects.EnterpriseInfo{
CompanyName: record.CompanyName,
UnifiedSocialCode: record.UnifiedSocialCode,
LegalPersonName: record.LegalPersonName,
LegalPersonID: record.LegalPersonID,
LegalPersonPhone: record.LegalPersonPhone,
EnterpriseAddress: record.EnterpriseAddress,
}
if err := cert.SubmitEnterpriseInfoForReview(enterpriseInfo); err != nil {
return fmt.Errorf("同步认证为待审核失败: %w", err)
}
if err := s.aggregateService.SaveCertification(ctx, cert); err != nil {
return fmt.Errorf("保存认证信息失败: %w", err)
}
s.logger.Info("已同步认证为待审核(兼容历史脏数据)", zap.String("user_id", cert.UserID), zap.String("record_id", record.ID))
return nil
}
// ================ 辅助方法 ================
// convertToResponse 转换实体为响应DTO
func (s *CertificationApplicationServiceImpl) convertToResponse(cert *entities.Certification) *responses.CertificationResponse {
response := &responses.CertificationResponse{

View File

@@ -94,6 +94,14 @@ type ForceTransitionStatusCommand struct {
Force bool `json:"force,omitempty"` // 是否强制执行,跳过业务规则验证
}
// AdminTransitionCertificationStatusCommand 管理端变更认证状态(以状态机为准,用于审核通过/拒绝等)
type AdminTransitionCertificationStatusCommand struct {
AdminID string `json:"-"`
UserID string `json:"user_id" validate:"required"`
TargetStatus string `json:"target_status" validate:"required,oneof=info_submitted info_rejected"` // 审核通过 -> info_submitted审核拒绝 -> info_rejected
Remark string `json:"remark"`
}
// SubmitEnterpriseInfoCommand 提交企业信息命令
type SubmitEnterpriseInfoCommand struct {
UserID string `json:"-" comment:"用户唯一标识从JWT token获取不在JSON中暴露"`

View File

@@ -193,11 +193,11 @@ func (q *GetSystemMonitoringQuery) ShouldIncludeMetric(metric string) bool {
return false
}
// AdminListSubmitRecordsQuery 管理端企业信息提交记录列表查询
// AdminListSubmitRecordsQuery 管理端企业信息提交记录列表查询(以状态机 certification_status 为准,不做审核状态筛选)
type AdminListSubmitRecordsQuery struct {
Page int `json:"page" form:"page"`
PageSize int `json:"page_size" form:"page_size"`
ManualReviewStatus string `json:"manual_review_status" form:"manual_review_status"` // pending, approved, rejected空为全部
CertificationStatus string `json:"certification_status" form:"certification_status"` // 按认证状态筛选,如 info_pending_review / info_submitted / info_rejected空为全部
CompanyName string `json:"company_name" form:"company_name"` // 企业名称(模糊搜索)
LegalPersonPhone string `json:"legal_person_phone" form:"legal_person_phone"` // 法人手机号
LegalPersonName string `json:"legal_person_name" form:"legal_person_name"` // 法人姓名(模糊搜索)

View File

@@ -119,9 +119,7 @@ type AdminSubmitRecordItem struct {
LegalPersonName string `json:"legal_person_name"`
SubmitAt time.Time `json:"submit_at"`
Status string `json:"status"`
ManualReviewStatus string `json:"manual_review_status"`
ManualReviewedAt *time.Time `json:"manual_reviewed_at,omitempty"`
CertificationStatus string `json:"certification_status,omitempty"` // 该用户当前认证状态,用于前端判断是否已完成企业认证并显示「已审核」
CertificationStatus string `json:"certification_status,omitempty"` // 以状态机为准info_pending_review/info_submitted/info_rejected 等
}
// AdminSubmitRecordDetail 管理端提交记录详情(含完整信息与图片 URL
@@ -147,11 +145,7 @@ type AdminSubmitRecordDetail struct {
VerifiedAt *time.Time `json:"verified_at,omitempty"`
FailedAt *time.Time `json:"failed_at,omitempty"`
FailureReason string `json:"failure_reason,omitempty"`
ManualReviewStatus string `json:"manual_review_status"`
ManualReviewRemark string `json:"manual_review_remark,omitempty"`
ManualReviewedAt *time.Time `json:"manual_reviewed_at,omitempty"`
ManualReviewerID string `json:"manual_reviewer_id,omitempty"`
CertificationStatus string `json:"certification_status,omitempty"` // 该用户当前认证状态,用于前端显示「已审核」
CertificationStatus string `json:"certification_status,omitempty"` // 以状态机为准
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}

View File

@@ -5,18 +5,18 @@ type CertificationStatus string
const (
// === 主流程状态 ===
StatusPending CertificationStatus = "pending" // 待认证
StatusInfoPendingReview CertificationStatus = "info_pending_review" // 企业信息待人工审核
StatusInfoSubmitted CertificationStatus = "info_submitted" // 已提交企业信息(审核通过)
StatusEnterpriseVerified CertificationStatus = "enterprise_verified" // 已企业认证
StatusContractApplied CertificationStatus = "contract_applied" // 已申请签署合同
StatusContractSigned CertificationStatus = "contract_signed" // 已签署合同
StatusCompleted CertificationStatus = "completed" // 认证完成
StatusPending CertificationStatus = "pending" // 待认证
StatusInfoPendingReview CertificationStatus = "info_pending_review" // 企业信息待人工审核
StatusInfoSubmitted CertificationStatus = "info_submitted" // 已提交企业信息(审核通过)
StatusEnterpriseVerified CertificationStatus = "enterprise_verified" // 已企业认证
StatusContractApplied CertificationStatus = "contract_applied" // 已申请签署合同
StatusContractSigned CertificationStatus = "contract_signed" // 已签署合同
StatusCompleted CertificationStatus = "completed" // 认证完成
// === 失败状态 ===
StatusInfoRejected CertificationStatus = "info_rejected" // 企业信息被拒绝
StatusContractRejected CertificationStatus = "contract_rejected" // 合同被拒签
StatusContractExpired CertificationStatus = "contract_expired" // 合同签署超时
StatusContractExpired CertificationStatus = "contract_expired" // 合同签署超时
)
// AllStatuses 所有有效状态列表
@@ -145,15 +145,15 @@ func GetStatusPriority(status CertificationStatus) int {
func GetProgressPercentage(status CertificationStatus) int {
progressMap := map[CertificationStatus]int{
StatusPending: 0,
StatusInfoPendingReview: 15,
StatusInfoSubmitted: 25,
StatusEnterpriseVerified: 50,
StatusContractApplied: 75,
StatusContractSigned: 100,
StatusInfoPendingReview: 15,
StatusInfoSubmitted: 25,
StatusEnterpriseVerified: 50,
StatusContractApplied: 75,
StatusContractSigned: 100,
StatusCompleted: 100,
StatusInfoRejected: 25,
StatusContractRejected: 75,
StatusContractExpired: 75,
StatusInfoRejected: 25,
StatusContractRejected: 75,
StatusContractExpired: 75,
}
if progress, exists := progressMap[status]; exists {
@@ -166,7 +166,7 @@ func GetProgressPercentage(status CertificationStatus) int {
func IsUserActionRequired(status CertificationStatus) bool {
userActionRequired := map[CertificationStatus]bool{
StatusPending: true, // 需要提交企业信息
StatusInfoPendingReview: false, // 等待人工审核
StatusInfoPendingReview: false, // 等待人工审核
StatusInfoSubmitted: false, // 等待完成企业认证
StatusEnterpriseVerified: true, // 需要申请合同
StatusContractApplied: true, // 需要签署合同
@@ -277,18 +277,18 @@ func CanTransitionTo(currentStatus, targetStatus CertificationStatus) bool {
// GetTransitionReason 获取状态转换的原因描述
func GetTransitionReason(from, to CertificationStatus) string {
transitionReasons := map[string]string{
string(StatusPending) + "->" + string(StatusInfoPendingReview): "用户提交企业信息,等待人工审核",
string(StatusInfoPendingReview) + "->" + string(StatusInfoSubmitted): "管理员审核通过",
string(StatusInfoPendingReview) + "->" + string(StatusInfoRejected): "管理员审核拒绝",
string(StatusPending) + "->" + string(StatusInfoSubmitted): "用户提交企业信息",
string(StatusInfoSubmitted) + "->" + string(StatusEnterpriseVerified): "e签宝企业认证成功",
string(StatusInfoSubmitted) + "->" + string(StatusInfoRejected): "e签宝企业认证失败",
string(StatusEnterpriseVerified) + "->" + string(StatusContractApplied): "用户申请签署合同",
string(StatusContractApplied) + "->" + string(StatusContractSigned): "e签宝合同签署成功",
string(StatusContractSigned) + "->" + string(StatusCompleted): "系统处理完成,认证成功",
string(StatusContractApplied) + "->" + string(StatusContractRejected): "用户拒绝签署合同",
string(StatusContractApplied) + "->" + string(StatusContractExpired): "合同签署超时",
string(StatusInfoRejected) + "->" + string(StatusInfoSubmitted): "用户重新提交企业信息",
string(StatusPending) + "->" + string(StatusInfoPendingReview): "用户提交企业信息,等待人工审核",
string(StatusInfoPendingReview) + "->" + string(StatusInfoSubmitted): "管理员审核通过",
string(StatusInfoPendingReview) + "->" + string(StatusInfoRejected): "管理员审核拒绝",
string(StatusPending) + "->" + string(StatusInfoSubmitted): "用户提交企业信息",
string(StatusInfoSubmitted) + "->" + string(StatusEnterpriseVerified): "e签宝企业认证成功",
string(StatusInfoSubmitted) + "->" + string(StatusInfoRejected): "e签宝企业认证失败",
string(StatusEnterpriseVerified) + "->" + string(StatusContractApplied): "用户申请签署合同",
string(StatusContractApplied) + "->" + string(StatusContractSigned): "e签宝合同签署成功",
string(StatusContractSigned) + "->" + string(StatusCompleted): "系统处理完成,认证成功",
string(StatusContractApplied) + "->" + string(StatusContractRejected): "用户拒绝签署合同",
string(StatusContractApplied) + "->" + string(StatusContractExpired): "合同签署超时",
string(StatusInfoRejected) + "->" + string(StatusInfoSubmitted): "用户重新提交企业信息",
string(StatusContractRejected) + "->" + string(StatusEnterpriseVerified): "重置状态,准备重新申请",
string(StatusContractExpired) + "->" + string(StatusEnterpriseVerified): "重置状态,准备重新申请",
}

View File

@@ -5,9 +5,9 @@ import (
"tyapi-server/internal/domains/certification/entities"
)
// ListSubmitRecordsFilter 提交记录列表筛选
// ListSubmitRecordsFilter 提交记录列表筛选(以状态机 certification 状态为准)
type ListSubmitRecordsFilter struct {
ManualReviewStatus string // pending, approved, rejected表示全部
CertificationStatus string // 认证状态筛选,如 info_pending_review / info_submitted / info_rejected全部
CompanyName string // 企业名称(模糊搜索)
LegalPersonPhone string // 法人手机号
LegalPersonName string // 法人姓名(模糊搜索)

View File

@@ -82,12 +82,12 @@ func (s *EnterpriseInfoSubmitRecordService) ValidateWithWestdex(ctx context.Cont
}
// 开发环境下跳过外部验证
// if s.appConfig.IsDevelopment() {
// s.logger.Info("开发环境:跳过企业信息外部验证",
// zap.String("company_name", info.CompanyName),
// zap.String("legal_person", info.LegalPersonName))
// return nil
// }
if s.appConfig.IsDevelopment() {
s.logger.Info("开发环境:跳过企业信息外部验证",
zap.String("company_name", info.CompanyName),
zap.String("legal_person", info.LegalPersonName))
return nil
}
// 构建QYGL5CMP请求参数
reqDto := dto.QYGL5CMPReq{

View File

@@ -91,21 +91,22 @@ func (r *GormEnterpriseInfoSubmitRecordRepository) ExistsByUnifiedSocialCodeExcl
}
func (r *GormEnterpriseInfoSubmitRecordRepository) List(ctx context.Context, filter repositories.ListSubmitRecordsFilter) (*repositories.ListSubmitRecordsResult, error) {
db := r.GetDB(ctx).Model(&entities.EnterpriseInfoSubmitRecord{})
if filter.ManualReviewStatus != "" {
db = db.Where("manual_review_status = ?", filter.ManualReviewStatus)
base := r.GetDB(ctx).Model(&entities.EnterpriseInfoSubmitRecord{})
if filter.CertificationStatus != "" {
base = base.Joins("JOIN certifications ON certifications.user_id = enterprise_info_submit_records.user_id AND certifications.deleted_at IS NULL").
Where("certifications.status = ?", filter.CertificationStatus)
}
if filter.CompanyName != "" {
db = db.Where("company_name LIKE ?", "%"+filter.CompanyName+"%")
base = base.Where("enterprise_info_submit_records.company_name LIKE ?", "%"+filter.CompanyName+"%")
}
if filter.LegalPersonPhone != "" {
db = db.Where("legal_person_phone = ?", filter.LegalPersonPhone)
base = base.Where("enterprise_info_submit_records.legal_person_phone = ?", filter.LegalPersonPhone)
}
if filter.LegalPersonName != "" {
db = db.Where("legal_person_name LIKE ?", "%"+filter.LegalPersonName+"%")
base = base.Where("enterprise_info_submit_records.legal_person_name LIKE ?", "%"+filter.LegalPersonName+"%")
}
var total int64
if err := db.Count(&total).Error; err != nil {
if err := base.Count(&total).Error; err != nil {
return nil, err
}
if filter.PageSize <= 0 {
@@ -116,20 +117,21 @@ func (r *GormEnterpriseInfoSubmitRecordRepository) List(ctx context.Context, fil
}
offset := (filter.Page - 1) * filter.PageSize
var records []*entities.EnterpriseInfoSubmitRecord
q := r.GetDB(ctx)
if filter.ManualReviewStatus != "" {
q = q.Where("manual_review_status = ?", filter.ManualReviewStatus)
q := r.GetDB(ctx).Model(&entities.EnterpriseInfoSubmitRecord{})
if filter.CertificationStatus != "" {
q = q.Joins("JOIN certifications ON certifications.user_id = enterprise_info_submit_records.user_id AND certifications.deleted_at IS NULL").
Where("certifications.status = ?", filter.CertificationStatus)
}
if filter.CompanyName != "" {
q = q.Where("company_name LIKE ?", "%"+filter.CompanyName+"%")
q = q.Where("enterprise_info_submit_records.company_name LIKE ?", "%"+filter.CompanyName+"%")
}
if filter.LegalPersonPhone != "" {
q = q.Where("legal_person_phone = ?", filter.LegalPersonPhone)
q = q.Where("enterprise_info_submit_records.legal_person_phone = ?", filter.LegalPersonPhone)
}
if filter.LegalPersonName != "" {
q = q.Where("legal_person_name LIKE ?", "%"+filter.LegalPersonName+"%")
q = q.Where("enterprise_info_submit_records.legal_person_name LIKE ?", "%"+filter.LegalPersonName+"%")
}
err := q.Order("submit_at DESC").Offset(offset).Limit(filter.PageSize).Find(&records).Error
err := q.Order("enterprise_info_submit_records.submit_at DESC").Offset(offset).Limit(filter.PageSize).Find(&records).Error
if err != nil {
return nil, err
}

View File

@@ -458,7 +458,7 @@ func (h *CertificationHandler) AdminCompleteCertificationWithoutContract(c *gin.
// @Security Bearer
// @Param page query int false "页码"
// @Param page_size query int false "每页条数"
// @Param manual_review_status query string false "审核状态 pending/approved/rejected"
// @Param certification_status query string false "按状态机筛选info_pending_review/info_submitted/info_rejected,空为全部"
// @Success 200 {object} responses.AdminSubmitRecordsListResponse
// @Router /api/v1/certifications/admin/submit-records [get]
func (h *CertificationHandler) AdminListSubmitRecords(c *gin.Context) {
@@ -564,6 +564,34 @@ func (h *CertificationHandler) AdminRejectSubmitRecord(c *gin.Context) {
h.response.Success(c, nil, "已拒绝")
}
// AdminTransitionCertificationStatus 管理端按用户变更认证状态(以状态机为准)
// @Summary 管理端变更认证状态
// @Tags 认证管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body commands.AdminTransitionCertificationStatusCommand true "user_id, target_status(info_submitted/info_rejected), remark"
// @Success 200 {object} map[string]interface{}
// @Router /api/v1/certifications/admin/transition-status [post]
func (h *CertificationHandler) AdminTransitionCertificationStatus(c *gin.Context) {
adminID := h.getCurrentUserID(c)
if adminID == "" {
h.response.Unauthorized(c, "未登录")
return
}
var cmd commands.AdminTransitionCertificationStatusCommand
if err := c.ShouldBindJSON(&cmd); err != nil {
h.response.BadRequest(c, "参数错误")
return
}
cmd.AdminID = adminID
if err := h.appService.AdminTransitionCertificationStatus(c.Request.Context(), &cmd); err != nil {
h.response.BadRequest(c, err.Error())
return
}
h.response.Success(c, nil, "状态已更新")
}
// ================ 回调处理 ================
// HandleEsignCallback 处理e签宝回调

View File

@@ -77,10 +77,14 @@ func (r *CertificationRoutes) Register(router *http.GinRouter) {
}
// 管理端企业审核(需管理员权限)
adminCertGroup := certificationGroup.Group("/admin/submit-records")
adminCertGroup.Use(r.auth.Handle())
adminCertGroup.Use(r.admin.Handle())
// 管理端企业审核(需管理员权限,以状态机状态为准
adminGroup := certificationGroup.Group("/admin")
adminGroup.Use(r.auth.Handle())
adminGroup.Use(r.admin.Handle())
{
adminGroup.POST("/transition-status", r.handler.AdminTransitionCertificationStatus)
}
adminCertGroup := adminGroup.Group("/submit-records")
{
adminCertGroup.GET("", r.handler.AdminListSubmitRecords)
adminCertGroup.GET("/:id", r.handler.AdminGetSubmitRecordByID)

View File

@@ -240,7 +240,7 @@ func (m *ComprehensiveLoggerMiddleware) logErrors(c *gin.Context, requestID, tra
zap.String("user_id", userID),
zap.String("method", c.Request.Method),
zap.String("path", c.Request.URL.Path),
zap.String("error_type", string(ginErr.Type)),
zap.Uint64("error_type", uint64(ginErr.Type)),
zap.Error(ginErr.Err),
zap.Time("timestamp", time.Now()),
}