Files
tyapi-server/internal/infrastructure/http/handlers/certification_handler.go
2025-09-12 01:15:09 +08:00

476 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.

//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 ""
}