476 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			476 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| //nolint:unused
 | ||
| package handlers
 | ||
| 
 | ||
| import (
 | ||
| 	"bytes"
 | ||
| 	"encoding/json"
 | ||
| 	"io"
 | ||
| 	"time"
 | ||
| 
 | ||
| 	"github.com/gin-gonic/gin"
 | ||
| 	"go.uber.org/zap"
 | ||
| 
 | ||
| 	"tyapi-server/internal/application/certification"
 | ||
| 	"tyapi-server/internal/application/certification/dto/commands"
 | ||
| 	"tyapi-server/internal/application/certification/dto/queries"
 | ||
| 	_ "tyapi-server/internal/application/certification/dto/responses"
 | ||
| 	"tyapi-server/internal/shared/interfaces"
 | ||
| 	"tyapi-server/internal/shared/middleware"
 | ||
| )
 | ||
| 
 | ||
| // CertificationHandler 认证HTTP处理器
 | ||
| type CertificationHandler struct {
 | ||
| 	appService certification.CertificationApplicationService
 | ||
| 	response   interfaces.ResponseBuilder
 | ||
| 	validator  interfaces.RequestValidator
 | ||
| 	logger     *zap.Logger
 | ||
| 	jwtAuth    *middleware.JWTAuthMiddleware
 | ||
| }
 | ||
| 
 | ||
| // NewCertificationHandler 创建认证处理器
 | ||
| func NewCertificationHandler(
 | ||
| 	appService certification.CertificationApplicationService,
 | ||
| 	response interfaces.ResponseBuilder,
 | ||
| 	validator interfaces.RequestValidator,
 | ||
| 	logger *zap.Logger,
 | ||
| 	jwtAuth *middleware.JWTAuthMiddleware,
 | ||
| ) *CertificationHandler {
 | ||
| 	return &CertificationHandler{
 | ||
| 		appService: appService,
 | ||
| 		response:   response,
 | ||
| 		validator:  validator,
 | ||
| 		logger:     logger,
 | ||
| 		jwtAuth:    jwtAuth,
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| // ================ 认证申请管理 ================
 | ||
| // GetCertification 获取认证详情
 | ||
| // @Summary 获取认证详情
 | ||
| // @Description 根据认证ID获取认证详情
 | ||
| // @Tags 认证管理
 | ||
| // @Accept json
 | ||
| // @Produce json
 | ||
| // @Security Bearer
 | ||
| // @Success 200 {object} responses.CertificationResponse "获取认证详情成功"
 | ||
| // @Failure 400 {object} map[string]interface{} "请求参数错误"
 | ||
| // @Failure 401 {object} map[string]interface{} "未认证"
 | ||
| // @Failure 404 {object} map[string]interface{} "认证记录不存在"
 | ||
| // @Failure 500 {object} map[string]interface{} "服务器内部错误"
 | ||
| // @Router /api/v1/certifications/details [get]
 | ||
| func (h *CertificationHandler) GetCertification(c *gin.Context) {
 | ||
| 	userID := h.getCurrentUserID(c)
 | ||
| 	if userID == "" {
 | ||
| 		h.response.Unauthorized(c, "用户未登录")
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	query := &queries.GetCertificationQuery{
 | ||
| 		UserID: userID,
 | ||
| 	}
 | ||
| 
 | ||
| 	result, err := h.appService.GetCertification(c.Request.Context(), query)
 | ||
| 	if err != nil {
 | ||
| 		h.logger.Error("获取认证详情失败", zap.Error(err), zap.String("user_id", userID))
 | ||
| 		h.response.BadRequest(c, err.Error())
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	h.response.Success(c, result, "获取认证详情成功")
 | ||
| }
 | ||
| 
 | ||
| // ================ 企业信息管理 ================
 | ||
| 
 | ||
| // SubmitEnterpriseInfo 提交企业信息
 | ||
| // @Summary 提交企业信息
 | ||
| // @Description 提交企业认证所需的企业信息
 | ||
| // @Tags 认证管理
 | ||
| // @Accept json
 | ||
| // @Produce json
 | ||
| // @Security Bearer
 | ||
| // @Param request body commands.SubmitEnterpriseInfoCommand true "提交企业信息请求"
 | ||
| // @Success 200 {object} responses.CertificationResponse "企业信息提交成功"
 | ||
| // @Failure 400 {object} map[string]interface{} "请求参数错误"
 | ||
| // @Failure 401 {object} map[string]interface{} "未认证"
 | ||
| // @Failure 404 {object} map[string]interface{} "认证记录不存在"
 | ||
| // @Failure 500 {object} map[string]interface{} "服务器内部错误"
 | ||
| // @Router /api/v1/certifications/enterprise-info [post]
 | ||
| func (h *CertificationHandler) SubmitEnterpriseInfo(c *gin.Context) {
 | ||
| 	userID := h.getCurrentUserID(c)
 | ||
| 	if userID == "" {
 | ||
| 		h.response.Unauthorized(c, "用户未登录")
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	var cmd commands.SubmitEnterpriseInfoCommand
 | ||
| 	if err := h.validator.BindAndValidate(c, &cmd); err != nil {
 | ||
| 		return
 | ||
| 	}
 | ||
| 	cmd.UserID = userID
 | ||
| 
 | ||
| 	result, err := h.appService.SubmitEnterpriseInfo(c.Request.Context(), &cmd)
 | ||
| 	if err != nil {
 | ||
| 		h.logger.Error("提交企业信息失败", zap.Error(err), zap.String("user_id", userID))
 | ||
| 		h.response.BadRequest(c, err.Error())
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	h.response.Success(c, result, "企业信息提交成功")
 | ||
| }
 | ||
| 
 | ||
| // ConfirmAuth 前端确认是否完成认证
 | ||
| // @Summary 前端确认认证状态
 | ||
| // @Description 前端轮询确认企业认证是否完成
 | ||
| // @Tags 认证管理
 | ||
| // @Accept json
 | ||
| // @Produce json
 | ||
| // @Security Bearer
 | ||
| // @Param request body queries.ConfirmAuthCommand true "确认状态请求"
 | ||
| // @Success 200 {object} responses.ConfirmAuthResponse "状态确认成功"
 | ||
| // @Failure 400 {object} map[string]interface{} "请求参数错误"
 | ||
| // @Failure 401 {object} map[string]interface{} "未认证"
 | ||
| // @Failure 404 {object} map[string]interface{} "认证记录不存在"
 | ||
| // @Failure 500 {object} map[string]interface{} "服务器内部错误"
 | ||
| // @Router /api/v1/certifications/confirm-auth [post]
 | ||
| func (h *CertificationHandler) ConfirmAuth(c *gin.Context) {
 | ||
| 	var cmd queries.ConfirmAuthCommand
 | ||
| 	cmd.UserID = h.getCurrentUserID(c)
 | ||
| 	if cmd.UserID == "" {
 | ||
| 		h.response.Unauthorized(c, "用户未登录")
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	result, err := h.appService.ConfirmAuth(c.Request.Context(), &cmd)
 | ||
| 	if err != nil {
 | ||
| 		h.logger.Error("确认认证/签署状态失败", zap.Error(err), zap.String("user_id", cmd.UserID))
 | ||
| 		h.response.BadRequest(c, err.Error())
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	h.response.Success(c, result, "状态确认成功")
 | ||
| }
 | ||
| 
 | ||
| // ConfirmSign 前端确认是否完成签署
 | ||
| // @Summary 前端确认签署状态
 | ||
| // @Description 前端轮询确认合同签署是否完成
 | ||
| // @Tags 认证管理
 | ||
| // @Accept json
 | ||
| // @Produce json
 | ||
| // @Security Bearer
 | ||
| // @Param request body queries.ConfirmSignCommand true "确认状态请求"
 | ||
| // @Success 200 {object} responses.ConfirmSignResponse "状态确认成功"
 | ||
| // @Failure 400 {object} map[string]interface{} "请求参数错误"
 | ||
| // @Failure 401 {object} map[string]interface{} "未认证"
 | ||
| // @Failure 404 {object} map[string]interface{} "认证记录不存在"
 | ||
| // @Failure 500 {object} map[string]interface{} "服务器内部错误"
 | ||
| // @Router /api/v1/certifications/confirm-sign [post]
 | ||
| func (h *CertificationHandler) ConfirmSign(c *gin.Context) {
 | ||
| 	var cmd queries.ConfirmSignCommand
 | ||
| 	cmd.UserID = h.getCurrentUserID(c)
 | ||
| 	if cmd.UserID == "" {
 | ||
| 		h.response.Unauthorized(c, "用户未登录")
 | ||
| 		return
 | ||
| 	}
 | ||
| 	result, err := h.appService.ConfirmSign(c.Request.Context(), &cmd)
 | ||
| 	if err != nil {
 | ||
| 		h.logger.Error("确认认证/签署状态失败", zap.Error(err), zap.String("user_id", cmd.UserID))
 | ||
| 		h.response.BadRequest(c, err.Error())
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	h.response.Success(c, result, "状态确认成功")
 | ||
| }
 | ||
| 
 | ||
| // ================ 合同管理 ================
 | ||
| 
 | ||
| // ApplyContract 申请合同签署
 | ||
| // @Summary 申请合同签署
 | ||
| // @Description 申请企业认证合同签署
 | ||
| // @Tags 认证管理
 | ||
| // @Accept json
 | ||
| // @Produce json
 | ||
| // @Security Bearer
 | ||
| // @Param request body commands.ApplyContractCommand true "申请合同请求"
 | ||
| // @Success 200 {object} responses.ContractSignUrlResponse "合同申请成功"
 | ||
| // @Failure 400 {object} map[string]interface{} "请求参数错误"
 | ||
| // @Failure 401 {object} map[string]interface{} "未认证"
 | ||
| // @Failure 404 {object} map[string]interface{} "认证记录不存在"
 | ||
| // @Failure 500 {object} map[string]interface{} "服务器内部错误"
 | ||
| // @Router /api/v1/certifications/apply-contract [post]
 | ||
| func (h *CertificationHandler) ApplyContract(c *gin.Context) {
 | ||
| 	var cmd commands.ApplyContractCommand
 | ||
| 	cmd.UserID = h.getCurrentUserID(c)
 | ||
| 	if cmd.UserID == "" {
 | ||
| 		h.response.Unauthorized(c, "用户未登录")
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	result, err := h.appService.ApplyContract(c.Request.Context(), &cmd)
 | ||
| 	if err != nil {
 | ||
| 		h.logger.Error("申请合同失败", zap.Error(err), zap.String("user_id", cmd.UserID))
 | ||
| 		h.response.BadRequest(c, err.Error())
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	h.response.Success(c, result, "合同申请成功")
 | ||
| }
 | ||
| 
 | ||
| // RecognizeBusinessLicense OCR识别营业执照
 | ||
| // @Summary OCR识别营业执照
 | ||
| // @Description 上传营业执照图片进行OCR识别,自动填充企业信息
 | ||
| // @Tags 认证管理
 | ||
| // @Accept multipart/form-data
 | ||
| // @Produce json
 | ||
| // @Security Bearer
 | ||
| // @Param image formData file true "营业执照图片文件"
 | ||
| // @Success 200 {object} responses.BusinessLicenseResult "营业执照识别成功"
 | ||
| // @Failure 400 {object} map[string]interface{} "请求参数错误"
 | ||
| // @Failure 401 {object} map[string]interface{} "未认证"
 | ||
| // @Failure 500 {object} map[string]interface{} "服务器内部错误"
 | ||
| // @Router /api/v1/certifications/ocr/business-license [post]
 | ||
| func (h *CertificationHandler) RecognizeBusinessLicense(c *gin.Context) {
 | ||
| 	userID := h.getCurrentUserID(c)
 | ||
| 	if userID == "" {
 | ||
| 		h.response.Unauthorized(c, "用户未登录")
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取上传的文件
 | ||
| 	file, err := c.FormFile("image")
 | ||
| 	if err != nil {
 | ||
| 		h.logger.Error("获取上传文件失败", zap.Error(err), zap.String("user_id", userID))
 | ||
| 		h.response.BadRequest(c, "请选择要上传的营业执照图片")
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	// 验证文件类型
 | ||
| 	allowedTypes := map[string]bool{
 | ||
| 		"image/jpeg": true,
 | ||
| 		"image/jpg":  true,
 | ||
| 		"image/png":  true,
 | ||
| 		"image/webp": true,
 | ||
| 	}
 | ||
| 	if !allowedTypes[file.Header.Get("Content-Type")] {
 | ||
| 		h.response.BadRequest(c, "只支持JPG、PNG、WEBP格式的图片")
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	// 验证文件大小(限制为5MB)
 | ||
| 	if file.Size > 5*1024*1024 {
 | ||
| 		h.response.BadRequest(c, "图片大小不能超过5MB")
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	// 打开文件
 | ||
| 	src, err := file.Open()
 | ||
| 	if err != nil {
 | ||
| 		h.logger.Error("打开上传文件失败", zap.Error(err), zap.String("user_id", userID))
 | ||
| 		h.response.BadRequest(c, "文件读取失败")
 | ||
| 		return
 | ||
| 	}
 | ||
| 	defer src.Close()
 | ||
| 
 | ||
| 	// 读取文件内容
 | ||
| 	imageBytes, err := io.ReadAll(src)
 | ||
| 	if err != nil {
 | ||
| 		h.logger.Error("读取文件内容失败", zap.Error(err), zap.String("user_id", userID))
 | ||
| 		h.response.BadRequest(c, "文件读取失败")
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	// 调用OCR服务识别营业执照
 | ||
| 	result, err := h.appService.RecognizeBusinessLicense(c.Request.Context(), imageBytes)
 | ||
| 	if err != nil {
 | ||
| 		h.logger.Error("营业执照OCR识别失败", zap.Error(err), zap.String("user_id", userID))
 | ||
| 		h.response.BadRequest(c, "营业执照识别失败:"+err.Error())
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	h.logger.Info("营业执照OCR识别成功",
 | ||
| 		zap.String("user_id", userID),
 | ||
| 		zap.String("company_name", result.CompanyName),
 | ||
| 		zap.Float64("confidence", result.Confidence),
 | ||
| 	)
 | ||
| 
 | ||
| 	h.response.Success(c, result, "营业执照识别成功")
 | ||
| }
 | ||
| 
 | ||
| // ListCertifications 获取认证列表(管理员)
 | ||
| // @Summary 获取认证列表
 | ||
| // @Description 管理员获取认证申请列表
 | ||
| // @Tags 认证管理
 | ||
| // @Accept json
 | ||
| // @Produce json
 | ||
| // @Security Bearer
 | ||
| // @Param page query int false "页码" default(1)
 | ||
| // @Param page_size query int false "每页数量" default(10)
 | ||
| // @Param sort_by query string false "排序字段"
 | ||
| // @Param sort_order query string false "排序方向" Enums(asc, desc)
 | ||
| // @Param status query string false "认证状态"
 | ||
| // @Param user_id query string false "用户ID"
 | ||
| // @Param company_name query string false "公司名称"
 | ||
| // @Param legal_person_name query string false "法人姓名"
 | ||
| // @Param search_keyword query string false "搜索关键词"
 | ||
| // @Success 200 {object} responses.CertificationListResponse "获取认证列表成功"
 | ||
| // @Failure 401 {object} map[string]interface{} "未认证"
 | ||
| // @Failure 403 {object} map[string]interface{} "权限不足"
 | ||
| // @Failure 500 {object} map[string]interface{} "服务器内部错误"
 | ||
| // @Router /api/v1/certifications [get]
 | ||
| func (h *CertificationHandler) ListCertifications(c *gin.Context) {
 | ||
| 	userID := h.getCurrentUserID(c)
 | ||
| 	if userID == "" {
 | ||
| 		h.response.Unauthorized(c, "用户未登录")
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	var query queries.ListCertificationsQuery
 | ||
| 	if err := h.validator.BindAndValidate(c, &query); err != nil {
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	result, err := h.appService.ListCertifications(c.Request.Context(), &query)
 | ||
| 	if err != nil {
 | ||
| 		h.logger.Error("获取认证列表失败", zap.Error(err))
 | ||
| 		h.response.BadRequest(c, err.Error())
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	h.response.Success(c, result, "获取认证列表成功")
 | ||
| }
 | ||
| 
 | ||
| // ================ 回调处理 ================
 | ||
| 
 | ||
| // HandleEsignCallback 处理e签宝回调
 | ||
| // @Summary 处理e签宝回调
 | ||
| // @Description 处理e签宝的异步回调通知
 | ||
| // @Tags 认证管理
 | ||
| // @Accept application/json
 | ||
| // @Produce text/plain
 | ||
| // @Success 200 {string} string "success"
 | ||
| // @Failure 400 {string} string "fail"
 | ||
| // @Router /api/v1/certifications/esign/callback [post]
 | ||
| func (h *CertificationHandler) HandleEsignCallback(c *gin.Context) {
 | ||
| 	// 记录请求基本信息
 | ||
| 	h.logger.Info("收到e签宝回调请求",
 | ||
| 		zap.String("method", c.Request.Method),
 | ||
| 		zap.String("url", c.Request.URL.String()),
 | ||
| 		zap.String("remote_addr", c.ClientIP()),
 | ||
| 		zap.String("user_agent", c.GetHeader("User-Agent")),
 | ||
| 	)
 | ||
| 
 | ||
| 	// 记录所有请求头
 | ||
| 	headers := make(map[string]string)
 | ||
| 	for key, values := range c.Request.Header {
 | ||
| 		if len(values) > 0 {
 | ||
| 			headers[key] = values[0]
 | ||
| 		}
 | ||
| 	}
 | ||
| 	h.logger.Info("回调请求头信息", zap.Any("headers", headers))
 | ||
| 
 | ||
| 	// 记录URL查询参数
 | ||
| 	queryParams := make(map[string]string)
 | ||
| 	for key, values := range c.Request.URL.Query() {
 | ||
| 		if len(values) > 0 {
 | ||
| 			queryParams[key] = values[0]
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if len(queryParams) > 0 {
 | ||
| 		h.logger.Info("回调URL查询参数", zap.Any("query_params", queryParams))
 | ||
| 	}
 | ||
| 
 | ||
| 	// 读取并记录请求体
 | ||
| 	var callbackData *commands.EsignCallbackData
 | ||
| 	if c.Request.Body != nil {
 | ||
| 		bodyBytes, err := c.GetRawData()
 | ||
| 		if err != nil {
 | ||
| 			h.logger.Error("读取回调请求体失败", zap.Error(err))
 | ||
| 			h.response.BadRequest(c, "读取请求体失败")
 | ||
| 			return
 | ||
| 		}
 | ||
| 
 | ||
| 		if err := json.Unmarshal(bodyBytes, &callbackData); err != nil {
 | ||
| 			h.logger.Error("回调请求体不是有效的JSON格式", zap.Error(err))
 | ||
| 			h.response.BadRequest(c, "请求体格式错误")
 | ||
| 			return
 | ||
| 		}
 | ||
| 		h.logger.Info("回调请求体内容", zap.Any("body", callbackData))
 | ||
| 
 | ||
| 		// 如果后续还需要用 c.Request.Body
 | ||
| 		c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
 | ||
| 	}
 | ||
| 
 | ||
| 	// 记录Content-Type
 | ||
| 	contentType := c.GetHeader("Content-Type")
 | ||
| 	h.logger.Info("回调请求Content-Type", zap.String("content_type", contentType))
 | ||
| 
 | ||
| 	// 记录Content-Length
 | ||
| 	contentLength := c.GetHeader("Content-Length")
 | ||
| 	if contentLength != "" {
 | ||
| 		h.logger.Info("回调请求Content-Length", zap.String("content_length", contentLength))
 | ||
| 	}
 | ||
| 
 | ||
| 	// 记录时间戳
 | ||
| 	h.logger.Info("回调请求时间",
 | ||
| 		zap.Time("request_time", time.Now()),
 | ||
| 		zap.String("request_id", c.GetHeader("X-Request-ID")),
 | ||
| 	)
 | ||
| 
 | ||
| 	// 记录完整的请求信息摘要
 | ||
| 	h.logger.Info("e签宝回调完整信息摘要",
 | ||
| 		zap.String("method", c.Request.Method),
 | ||
| 		zap.String("url", c.Request.URL.String()),
 | ||
| 		zap.String("client_ip", c.ClientIP()),
 | ||
| 		zap.String("content_type", contentType),
 | ||
| 		zap.Any("headers", headers),
 | ||
| 		zap.Any("query_params", queryParams),
 | ||
| 		zap.Any("body", callbackData),
 | ||
| 	)
 | ||
| 
 | ||
| 	// 处理回调数据
 | ||
| 	if callbackData != nil {
 | ||
| 		// 构建请求头映射
 | ||
| 		headers := make(map[string]string)
 | ||
| 		for key, values := range c.Request.Header {
 | ||
| 			if len(values) > 0 {
 | ||
| 				headers[key] = values[0]
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		// 构建查询参数映射
 | ||
| 		queryParams := make(map[string]string)
 | ||
| 		for key, values := range c.Request.URL.Query() {
 | ||
| 			if len(values) > 0 {
 | ||
| 				queryParams[key] = values[0]
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if err := h.appService.HandleEsignCallback(c.Request.Context(), &commands.EsignCallbackCommand{
 | ||
| 			Data:        callbackData,
 | ||
| 			Headers:     headers,
 | ||
| 			QueryParams: queryParams,
 | ||
| 		}); err != nil {
 | ||
| 			h.logger.Error("处理e签宝回调失败", zap.Error(err))
 | ||
| 			h.response.BadRequest(c, "回调处理失败: "+err.Error())
 | ||
| 			return
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	// 返回成功响应
 | ||
| 	c.JSON(200, map[string]interface{}{
 | ||
| 		"code": "200",
 | ||
| 		"msg":  "success",
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| // ================ 辅助方法 ================
 | ||
| 
 | ||
| // getCurrentUserID 获取当前用户ID
 | ||
| func (h *CertificationHandler) getCurrentUserID(c *gin.Context) string {
 | ||
| 	if userID, exists := c.Get("user_id"); exists {
 | ||
| 		if id, ok := userID.(string); ok {
 | ||
| 			return id
 | ||
| 		}
 | ||
| 	}
 | ||
| 	return ""
 | ||
| }
 |