Files
tyapi-server/internal/domains/certification/handlers/certification_handler.go
2025-07-11 21:05:58 +08:00

537 lines
15 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handlers
import (
"io"
"path/filepath"
"strconv"
"strings"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"tyapi-server/internal/domains/certification/dto"
"tyapi-server/internal/domains/certification/services"
"tyapi-server/internal/shared/interfaces"
)
// CertificationHandler 认证处理器
type CertificationHandler struct {
certificationService *services.CertificationService
response interfaces.ResponseBuilder
logger *zap.Logger
}
// NewCertificationHandler 创建认证处理器
func NewCertificationHandler(
certificationService *services.CertificationService,
response interfaces.ResponseBuilder,
logger *zap.Logger,
) *CertificationHandler {
return &CertificationHandler{
certificationService: certificationService,
response: response,
logger: logger,
}
}
// CreateCertification 创建认证申请
// @Summary 创建认证申请
// @Description 用户创建企业认证申请
// @Tags 认证
// @Accept json
// @Produce json
// @Success 200 {object} dto.CertificationCreateResponse
// @Failure 400 {object} interfaces.APIResponse
// @Failure 500 {object} interfaces.APIResponse
// @Router /api/v1/certification/create [post]
func (h *CertificationHandler) CreateCertification(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.response.Unauthorized(c, "用户未认证")
return
}
result, err := h.certificationService.CreateCertification(c.Request.Context(), userID)
if err != nil {
h.logger.Error("创建认证申请失败",
zap.String("user_id", userID),
zap.Error(err),
)
h.response.InternalError(c, "创建认证申请失败")
return
}
h.response.Success(c, result, "认证申请创建成功")
}
// UploadLicense 上传营业执照
// @Summary 上传营业执照
// @Description 上传营业执照文件并进行OCR识别
// @Tags 认证
// @Accept multipart/form-data
// @Produce json
// @Param file formData file true "营业执照文件"
// @Success 200 {object} dto.UploadLicenseResponse
// @Failure 400 {object} interfaces.APIResponse
// @Failure 500 {object} interfaces.APIResponse
// @Router /api/v1/certification/upload-license [post]
func (h *CertificationHandler) UploadLicense(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.response.Unauthorized(c, "用户未认证")
return
}
// 获取上传的文件
file, header, err := c.Request.FormFile("file")
if err != nil {
h.logger.Error("获取上传文件失败", zap.Error(err))
h.response.BadRequest(c, "请选择要上传的文件")
return
}
defer file.Close()
// 检查文件类型
fileName := header.Filename
ext := strings.ToLower(filepath.Ext(fileName))
allowedExts := []string{".jpg", ".jpeg", ".png", ".pdf"}
isAllowed := false
for _, allowedExt := range allowedExts {
if ext == allowedExt {
isAllowed = true
break
}
}
if !isAllowed {
h.response.BadRequest(c, "文件格式不支持,仅支持 JPG、PNG、PDF 格式")
return
}
// 检查文件大小限制为10MB
const maxFileSize = 10 * 1024 * 1024 // 10MB
if header.Size > maxFileSize {
h.response.BadRequest(c, "文件大小不能超过10MB")
return
}
// 读取文件内容
fileBytes, err := io.ReadAll(file)
if err != nil {
h.logger.Error("读取文件内容失败", zap.Error(err))
h.response.InternalError(c, "文件读取失败")
return
}
// 调用服务上传文件
result, err := h.certificationService.UploadLicense(c.Request.Context(), userID, fileBytes, fileName)
if err != nil {
h.logger.Error("上传营业执照失败",
zap.String("user_id", userID),
zap.String("file_name", fileName),
zap.Error(err),
)
h.response.InternalError(c, "上传失败,请稍后重试")
return
}
h.response.Success(c, result, "营业执照上传成功")
}
// SubmitEnterpriseInfo 提交企业信息
// @Summary 提交企业信息
// @Description 确认并提交企业四要素信息
// @Tags 认证
// @Accept json
// @Produce json
// @Param id path string true "认证申请ID"
// @Param request body dto.SubmitEnterpriseInfoRequest true "企业信息"
// @Success 200 {object} dto.SubmitEnterpriseInfoResponse
// @Failure 400 {object} interfaces.APIResponse
// @Failure 500 {object} interfaces.APIResponse
// @Router /api/v1/certification/{id}/submit-info [put]
func (h *CertificationHandler) SubmitEnterpriseInfo(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.response.Unauthorized(c, "用户未认证")
return
}
certificationID := c.Param("id")
if certificationID == "" {
h.response.BadRequest(c, "认证申请ID不能为空")
return
}
var req dto.SubmitEnterpriseInfoRequest
if err := c.ShouldBindJSON(&req); err != nil {
h.logger.Error("参数绑定失败", zap.Error(err))
h.response.BadRequest(c, "请求参数格式错误")
return
}
// 验证企业信息
if req.CompanyName == "" {
h.response.BadRequest(c, "企业名称不能为空")
return
}
if req.UnifiedSocialCode == "" {
h.response.BadRequest(c, "统一社会信用代码不能为空")
return
}
if req.LegalPersonName == "" {
h.response.BadRequest(c, "法定代表人姓名不能为空")
return
}
if req.LegalPersonID == "" {
h.response.BadRequest(c, "法定代表人身份证号不能为空")
return
}
if req.LicenseUploadRecordID == "" {
h.response.BadRequest(c, "营业执照上传记录ID不能为空")
return
}
result, err := h.certificationService.SubmitEnterpriseInfo(c.Request.Context(), certificationID, &req)
if err != nil {
h.logger.Error("提交企业信息失败",
zap.String("certification_id", certificationID),
zap.String("user_id", userID),
zap.Error(err),
)
if strings.Contains(err.Error(), "已被使用") || strings.Contains(err.Error(), "不允许") {
h.response.BadRequest(c, err.Error())
} else {
h.response.InternalError(c, "提交失败,请稍后重试")
}
return
}
h.response.Success(c, result, "企业信息提交成功")
}
// InitiateFaceVerify 初始化人脸识别
// @Summary 初始化人脸识别
// @Description 开始人脸识别认证流程
// @Tags 认证
// @Accept json
// @Produce json
// @Param id path string true "认证申请ID"
// @Param request body dto.FaceVerifyRequest true "人脸识别请求"
// @Success 200 {object} dto.FaceVerifyResponse
// @Failure 400 {object} interfaces.APIResponse
// @Failure 500 {object} interfaces.APIResponse
// @Router /api/v1/certification/{id}/face-verify [post]
func (h *CertificationHandler) InitiateFaceVerify(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.response.Unauthorized(c, "用户未认证")
return
}
certificationID := c.Param("id")
if certificationID == "" {
h.response.BadRequest(c, "认证申请ID不能为空")
return
}
var req dto.FaceVerifyRequest
if err := c.ShouldBindJSON(&req); err != nil {
h.logger.Error("参数绑定失败", zap.Error(err))
h.response.BadRequest(c, "请求参数格式错误")
return
}
// 验证请求参数
if req.RealName == "" {
h.response.BadRequest(c, "真实姓名不能为空")
return
}
if req.IDCardNumber == "" {
h.response.BadRequest(c, "身份证号不能为空")
return
}
result, err := h.certificationService.InitiateFaceVerify(c.Request.Context(), certificationID, &req)
if err != nil {
h.logger.Error("初始化人脸识别失败",
zap.String("certification_id", certificationID),
zap.String("user_id", userID),
zap.Error(err),
)
if strings.Contains(err.Error(), "不允许") {
h.response.BadRequest(c, err.Error())
} else {
h.response.InternalError(c, "初始化失败,请稍后重试")
}
return
}
h.response.Success(c, result, "人脸识别初始化成功")
}
// ApplyContract 申请电子合同
// @Summary 申请电子合同
// @Description 申请生成企业认证电子合同
// @Tags 认证
// @Accept json
// @Produce json
// @Param id path string true "认证申请ID"
// @Success 200 {object} dto.ApplyContractResponse
// @Failure 400 {object} interfaces.APIResponse
// @Failure 500 {object} interfaces.APIResponse
// @Router /api/v1/certification/{id}/apply-contract [post]
func (h *CertificationHandler) ApplyContract(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.response.Unauthorized(c, "用户未认证")
return
}
certificationID := c.Param("id")
if certificationID == "" {
h.response.BadRequest(c, "认证申请ID不能为空")
return
}
result, err := h.certificationService.ApplyContract(c.Request.Context(), certificationID)
if err != nil {
h.logger.Error("申请电子合同失败",
zap.String("certification_id", certificationID),
zap.String("user_id", userID),
zap.Error(err),
)
if strings.Contains(err.Error(), "不允许") {
h.response.BadRequest(c, err.Error())
} else {
h.response.InternalError(c, "申请失败,请稍后重试")
}
return
}
h.response.Success(c, result, "合同申请提交成功,请等待管理员审核")
}
// GetCertificationStatus 获取认证状态
// @Summary 获取认证状态
// @Description 查询当前用户的认证申请状态和进度
// @Tags 认证
// @Accept json
// @Produce json
// @Success 200 {object} dto.CertificationStatusResponse
// @Failure 400 {object} interfaces.APIResponse
// @Failure 500 {object} interfaces.APIResponse
// @Router /api/v1/certification/status [get]
func (h *CertificationHandler) GetCertificationStatus(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.response.Unauthorized(c, "用户未认证")
return
}
result, err := h.certificationService.GetCertificationStatus(c.Request.Context(), userID)
if err != nil {
h.logger.Error("获取认证状态失败",
zap.String("user_id", userID),
zap.Error(err),
)
if strings.Contains(err.Error(), "不存在") {
h.response.NotFound(c, "未找到认证申请记录")
} else {
h.response.InternalError(c, "查询失败,请稍后重试")
}
return
}
h.response.Success(c, result, "查询成功")
}
// GetCertificationDetails 获取认证详情
// @Summary 获取认证详情
// @Description 获取指定认证申请的详细信息
// @Tags 认证
// @Accept json
// @Produce json
// @Param id path string true "认证申请ID"
// @Success 200 {object} dto.CertificationStatusResponse
// @Failure 400 {object} interfaces.APIResponse
// @Failure 500 {object} interfaces.APIResponse
// @Router /api/v1/certification/{id} [get]
func (h *CertificationHandler) GetCertificationDetails(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.response.Unauthorized(c, "用户未认证")
return
}
certificationID := c.Param("id")
if certificationID == "" {
h.response.BadRequest(c, "认证申请ID不能为空")
return
}
// 通过用户ID获取状态来确保用户只能查看自己的认证记录
result, err := h.certificationService.GetCertificationStatus(c.Request.Context(), userID)
if err != nil {
h.logger.Error("获取认证详情失败",
zap.String("certification_id", certificationID),
zap.String("user_id", userID),
zap.Error(err),
)
if strings.Contains(err.Error(), "不存在") {
h.response.NotFound(c, "未找到认证申请记录")
} else {
h.response.InternalError(c, "查询失败,请稍后重试")
}
return
}
// 检查是否是用户自己的认证记录
if result.ID != certificationID {
h.response.Forbidden(c, "无权访问此认证记录")
return
}
h.response.Success(c, result, "查询成功")
}
// RetryStep 重试认证步骤
// @Summary 重试认证步骤
// @Description 重试失败的认证步骤(如人脸识别失败、签署失败等)
// @Tags 认证
// @Accept json
// @Produce json
// @Param id path string true "认证申请ID"
// @Param step query string true "重试步骤face_verify, sign_contract"
// @Success 200 {object} interfaces.APIResponse
// @Failure 400 {object} interfaces.APIResponse
// @Failure 500 {object} interfaces.APIResponse
// @Router /api/v1/certification/{id}/retry [post]
func (h *CertificationHandler) RetryStep(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.response.Unauthorized(c, "用户未认证")
return
}
certificationID := c.Param("id")
if certificationID == "" {
h.response.BadRequest(c, "认证申请ID不能为空")
return
}
step := c.Query("step")
if step == "" {
h.response.BadRequest(c, "重试步骤不能为空")
return
}
// TODO: 实现重试逻辑
// 这里需要根据不同的步骤调用状态机进行状态重置
h.logger.Info("重试认证步骤",
zap.String("certification_id", certificationID),
zap.String("user_id", userID),
zap.String("step", step),
)
h.response.Success(c, gin.H{
"certification_id": certificationID,
"step": step,
"message": "重试操作已提交",
}, "重试操作成功")
}
// GetProgressStats 获取进度统计
// @Summary 获取进度统计
// @Description 获取用户认证申请的进度统计信息
// @Tags 认证
// @Accept json
// @Produce json
// @Success 200 {object} map[string]interface{}
// @Failure 400 {object} interfaces.APIResponse
// @Failure 500 {object} interfaces.APIResponse
// @Router /api/v1/certification/progress [get]
func (h *CertificationHandler) GetProgressStats(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.response.Unauthorized(c, "用户未认证")
return
}
// 获取认证状态
status, err := h.certificationService.GetCertificationStatus(c.Request.Context(), userID)
if err != nil {
if strings.Contains(err.Error(), "不存在") {
h.response.Success(c, gin.H{
"has_certification": false,
"progress": 0,
"status": "",
"next_steps": []string{"开始企业认证"},
}, "查询成功")
return
}
h.response.InternalError(c, "查询失败")
return
}
// 构建进度统计
nextSteps := []string{}
if status.IsUserActionRequired {
switch status.Status {
case "pending":
nextSteps = append(nextSteps, "上传营业执照")
case "info_submitted":
nextSteps = append(nextSteps, "进行人脸识别")
case "face_verified":
nextSteps = append(nextSteps, "申请电子合同")
case "contract_approved":
nextSteps = append(nextSteps, "签署电子合同")
case "face_failed":
nextSteps = append(nextSteps, "重新进行人脸识别")
case "sign_failed":
nextSteps = append(nextSteps, "重新签署合同")
}
} else if status.IsAdminActionRequired {
nextSteps = append(nextSteps, "等待管理员审核")
} else {
nextSteps = append(nextSteps, "认证流程已完成")
}
result := gin.H{
"has_certification": true,
"certification_id": status.ID,
"progress": status.Progress,
"status": status.Status,
"status_name": status.StatusName,
"is_user_action_required": status.IsUserActionRequired,
"is_admin_action_required": status.IsAdminActionRequired,
"next_steps": nextSteps,
"created_at": status.CreatedAt,
"updated_at": status.UpdatedAt,
}
h.response.Success(c, result, "查询成功")
}
// parsePageParams 解析分页参数
func (h *CertificationHandler) parsePageParams(c *gin.Context) (int, int) {
page := 1
pageSize := 20
if pageStr := c.Query("page"); pageStr != "" {
if p, err := strconv.Atoi(pageStr); err == nil && p > 0 {
page = p
}
}
if sizeStr := c.Query("page_size"); sizeStr != "" {
if s, err := strconv.Atoi(sizeStr); err == nil && s > 0 && s <= 100 {
pageSize = s
}
}
return page, pageSize
}