temp
This commit is contained in:
@@ -0,0 +1,385 @@
|
||||
package certification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"tyapi-server/internal/domains/certification/entities"
|
||||
"tyapi-server/internal/domains/certification/enums"
|
||||
"tyapi-server/internal/domains/certification/services"
|
||||
user_services "tyapi-server/internal/domains/user/services"
|
||||
"tyapi-server/internal/shared/database"
|
||||
esign_service "tyapi-server/internal/shared/esign"
|
||||
)
|
||||
|
||||
// EsignCallbackData e签宝回调数据结构
|
||||
type EsignCallbackData struct {
|
||||
Action string `json:"action"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
AuthFlowId string `json:"authFlowId,omitempty"`
|
||||
SignFlowId string `json:"signFlowId,omitempty"`
|
||||
CustomBizNum string `json:"customBizNum,omitempty"`
|
||||
SignOrder int `json:"signOrder,omitempty"`
|
||||
OperateTime int64 `json:"operateTime,omitempty"`
|
||||
SignResult int `json:"signResult,omitempty"`
|
||||
ResultDescription string `json:"resultDescription,omitempty"`
|
||||
AuthType string `json:"authType,omitempty"`
|
||||
SignFlowStatus string `json:"signFlowStatus,omitempty"`
|
||||
Operator *EsignOperator `json:"operator,omitempty"`
|
||||
PsnInfo *EsignPsnInfo `json:"psnInfo,omitempty"`
|
||||
Organization *EsignOrganization `json:"organization,omitempty"`
|
||||
}
|
||||
|
||||
// EsignOperator 签署人信息
|
||||
type EsignOperator struct {
|
||||
PsnId string `json:"psnId"`
|
||||
PsnAccount *EsignPsnAccount `json:"psnAccount"`
|
||||
}
|
||||
|
||||
// EsignPsnInfo 个人认证信息
|
||||
type EsignPsnInfo struct {
|
||||
PsnId string `json:"psnId"`
|
||||
PsnAccount *EsignPsnAccount `json:"psnAccount"`
|
||||
}
|
||||
|
||||
// EsignPsnAccount 个人账户信息
|
||||
type EsignPsnAccount struct {
|
||||
AccountMobile string `json:"accountMobile"`
|
||||
AccountEmail string `json:"accountEmail"`
|
||||
}
|
||||
|
||||
// EsignOrganization 企业信息
|
||||
type EsignOrganization struct {
|
||||
OrgName string `json:"orgName"`
|
||||
// 可以根据需要添加更多企业信息字段
|
||||
}
|
||||
|
||||
// EsignCallbackApplicationServiceImpl e签宝回调应用服务实现
|
||||
type EsignCallbackApplicationServiceImpl struct {
|
||||
certManagementService *services.CertificationManagementService
|
||||
certWorkflowService *services.CertificationWorkflowService
|
||||
certificationEsignService *services.CertificationEsignService
|
||||
enterpriseService *user_services.EnterpriseService
|
||||
esignService *esign_service.Client
|
||||
enterpriseRecordService *services.EnterpriseInfoSubmitRecordService
|
||||
txManager *database.TransactionManager
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewEsignCallbackApplicationService 创建e签宝回调应用服务
|
||||
func NewEsignCallbackApplicationService(
|
||||
certManagementService *services.CertificationManagementService,
|
||||
certWorkflowService *services.CertificationWorkflowService,
|
||||
certificationEsignService *services.CertificationEsignService,
|
||||
enterpriseService *user_services.EnterpriseService,
|
||||
esignService *esign_service.Client,
|
||||
enterpriseRecordService *services.EnterpriseInfoSubmitRecordService,
|
||||
txManager *database.TransactionManager,
|
||||
logger *zap.Logger,
|
||||
) EsignCallbackApplicationService {
|
||||
return &EsignCallbackApplicationServiceImpl{
|
||||
certManagementService: certManagementService,
|
||||
certWorkflowService: certWorkflowService,
|
||||
certificationEsignService: certificationEsignService,
|
||||
enterpriseService: enterpriseService,
|
||||
esignService: esignService,
|
||||
enterpriseRecordService: enterpriseRecordService,
|
||||
txManager: txManager,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// HandleCallback 处理e签宝回调
|
||||
func (s *EsignCallbackApplicationServiceImpl) HandleCallback(ctx context.Context, callbackData map[string]interface{}, headers map[string]string, queryParams map[string]string) error {
|
||||
s.logger.Info("开始处理e签宝回调", zap.Any("callback_data", callbackData))
|
||||
|
||||
// 1. 验签
|
||||
if err := s.verifySignature(callbackData, headers, queryParams); err != nil {
|
||||
s.logger.Error("e签宝回调验签失败", zap.Error(err))
|
||||
return fmt.Errorf("验签失败: %w", err)
|
||||
}
|
||||
|
||||
// 2. 解析回调数据为结构体
|
||||
var callback EsignCallbackData
|
||||
jsonBytes, err := json.Marshal(callbackData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("序列化回调数据失败: %w", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(jsonBytes, &callback); err != nil {
|
||||
return fmt.Errorf("解析回调数据失败: %w", err)
|
||||
}
|
||||
|
||||
// 3. 记录回调信息
|
||||
s.logger.Info("e签宝回调信息解析",
|
||||
zap.String("action", callback.Action),
|
||||
zap.String("auth_flow_id", callback.AuthFlowId),
|
||||
zap.String("sign_flow_id", callback.SignFlowId),
|
||||
zap.String("auth_type", callback.AuthType),
|
||||
zap.String("sign_flow_status", callback.SignFlowStatus),
|
||||
zap.Int64("timestamp", callback.Timestamp),
|
||||
)
|
||||
|
||||
// 4. 根据回调类型处理业务逻辑
|
||||
switch callback.Action {
|
||||
case "AUTH_PASS":
|
||||
// 只处理企业认证通过
|
||||
if callback.AuthType == "ORG" {
|
||||
return s.handleEnterpriseAuthPass(ctx, &callback)
|
||||
}
|
||||
s.logger.Info("忽略非企业认证通过回调", zap.String("auth_type", callback.AuthType))
|
||||
return nil
|
||||
case "AUTH_FAIL":
|
||||
// 只处理企业认证失败
|
||||
if callback.AuthType == "ORG" {
|
||||
return s.handleEnterpriseAuthFail(ctx, &callback)
|
||||
}
|
||||
s.logger.Info("忽略非企业认证失败回调", zap.String("auth_type", callback.AuthType))
|
||||
return nil
|
||||
case "SIGN_FLOW_COMPLETE":
|
||||
// 合同签署流程完成
|
||||
return s.handleContractSignFlowComplete(ctx, &callback)
|
||||
default:
|
||||
s.logger.Info("忽略未知的回调动作", zap.String("action", callback.Action))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// verifySignature 验证e签宝回调签名
|
||||
func (s *EsignCallbackApplicationServiceImpl) verifySignature(callbackData map[string]interface{}, headers map[string]string, queryParams map[string]string) error {
|
||||
// 1. 获取签名相关参数
|
||||
signature, ok := headers["X-Tsign-Open-Signature"]
|
||||
if !ok {
|
||||
return fmt.Errorf("缺少签名头: X-Tsign-Open-Signature")
|
||||
}
|
||||
|
||||
timestamp, ok := headers["X-Tsign-Open-Timestamp"]
|
||||
if !ok {
|
||||
return fmt.Errorf("缺少时间戳头: X-Tsign-Open-Timestamp")
|
||||
}
|
||||
|
||||
// 2. 构建查询参数字符串
|
||||
var queryKeys []string
|
||||
for key := range queryParams {
|
||||
queryKeys = append(queryKeys, key)
|
||||
}
|
||||
sort.Strings(queryKeys) // 按ASCII码升序排序
|
||||
|
||||
var queryValues []string
|
||||
for _, key := range queryKeys {
|
||||
queryValues = append(queryValues, queryParams[key])
|
||||
}
|
||||
queryString := strings.Join(queryValues, "")
|
||||
|
||||
// 3. 获取请求体数据
|
||||
bodyData, err := s.getRequestBodyString(callbackData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取请求体数据失败: %w", err)
|
||||
}
|
||||
|
||||
// 4. 构建验签数据
|
||||
data := timestamp + queryString + bodyData
|
||||
|
||||
// 5. 计算签名
|
||||
expectedSignature := s.calculateSignature(data, s.esignService.GetConfig().AppSecret)
|
||||
|
||||
// 6. 比较签名
|
||||
if strings.ToLower(expectedSignature) != strings.ToLower(signature) {
|
||||
s.logger.Error("签名验证失败",
|
||||
zap.String("expected", strings.ToLower(expectedSignature)),
|
||||
zap.String("received", strings.ToLower(signature)),
|
||||
zap.String("data", data),
|
||||
)
|
||||
return fmt.Errorf("签名验证失败")
|
||||
}
|
||||
|
||||
s.logger.Info("e签宝回调验签成功")
|
||||
return nil
|
||||
}
|
||||
|
||||
// calculateSignature 计算HMAC-SHA256签名
|
||||
func (s *EsignCallbackApplicationServiceImpl) calculateSignature(data, secret string) string {
|
||||
h := hmac.New(sha256.New, []byte(secret))
|
||||
h.Write([]byte(data))
|
||||
return strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
|
||||
}
|
||||
|
||||
// getRequestBodyString 获取请求体字符串
|
||||
func (s *EsignCallbackApplicationServiceImpl) getRequestBodyString(callbackData map[string]interface{}) (string, error) {
|
||||
// 将map转换为JSON字符串
|
||||
jsonBytes, err := json.Marshal(callbackData)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("JSON序列化失败: %w", err)
|
||||
}
|
||||
return string(jsonBytes), nil
|
||||
}
|
||||
|
||||
// handleEnterpriseAuthPass 处理企业认证通过回调
|
||||
func (s *EsignCallbackApplicationServiceImpl) handleEnterpriseAuthPass(ctx context.Context, callback *EsignCallbackData) error {
|
||||
s.logger.Info("处理企业认证通过回调")
|
||||
|
||||
if callback.Organization == nil {
|
||||
return fmt.Errorf("回调数据中缺少organization字段")
|
||||
}
|
||||
|
||||
if callback.AuthFlowId == "" {
|
||||
return fmt.Errorf("回调数据中缺少authFlowId字段")
|
||||
}
|
||||
|
||||
// 查找对应的认证申请
|
||||
certification, err := s.certManagementService.GetCertificationByAuthFlowID(ctx, callback.AuthFlowId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("查找认证申请失败: %w", err)
|
||||
}
|
||||
if certification.Status != enums.StatusInfoSubmitted {
|
||||
s.logger.Warn("当前状态不允许完成企业认证", zap.String("status", string(certification.Status)))
|
||||
return nil
|
||||
}
|
||||
if err := s.completeEnterpriseAuth(ctx, certification); err != nil {
|
||||
return fmt.Errorf("完成企业认证失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("企业认证通过处理完成",
|
||||
zap.String("user_id", certification.UserID),
|
||||
zap.String("certification_id", certification.ID),
|
||||
zap.String("org_name", callback.Organization.OrgName),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleEnterpriseAuthFail 处理企业认证失败回调
|
||||
func (s *EsignCallbackApplicationServiceImpl) handleEnterpriseAuthFail(ctx context.Context, callback *EsignCallbackData) error {
|
||||
s.logger.Info("处理企业认证失败回调")
|
||||
|
||||
if callback.Organization == nil {
|
||||
return fmt.Errorf("回调数据中缺少organization字段")
|
||||
}
|
||||
|
||||
// 暂时忽略
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleContractSignFlowComplete 处理合同签署流程完成回调
|
||||
func (s *EsignCallbackApplicationServiceImpl) handleContractSignFlowComplete(ctx context.Context, callback *EsignCallbackData) error {
|
||||
s.logger.Info("处理合同签署流程完成回调")
|
||||
|
||||
if callback.SignFlowId == "" {
|
||||
return fmt.Errorf("回调数据中缺少signFlowId字段")
|
||||
}
|
||||
|
||||
if callback.SignFlowStatus == "" {
|
||||
return fmt.Errorf("回调数据中缺少signFlowStatus字段")
|
||||
}
|
||||
|
||||
// 查找对应的认证申请
|
||||
certification, err := s.certManagementService.GetCertificationByEsignFlowID(ctx, callback.SignFlowId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("查找认证申请失败: %w", err)
|
||||
}
|
||||
|
||||
// 根据签署流程状态处理
|
||||
switch callback.SignFlowStatus {
|
||||
case "2": // 已完成(所有签署方完成签署)
|
||||
s.logger.Info("合同签署流程已完成,所有签署方完成签署")
|
||||
|
||||
// 完成合同签署
|
||||
if err := s.certWorkflowService.CompleteContractSign(ctx, certification.ID, "所有签署方完成签署"); err != nil {
|
||||
return fmt.Errorf("完成合同签署失败: %w", err)
|
||||
}
|
||||
|
||||
// 自动完成认证
|
||||
if err := s.certWorkflowService.CompleteCertification(ctx, certification.ID); err != nil {
|
||||
return fmt.Errorf("完成认证失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("合同签署流程完成处理成功",
|
||||
zap.String("user_id", certification.UserID),
|
||||
zap.String("certification_id", certification.ID),
|
||||
zap.String("sign_flow_id", callback.SignFlowId),
|
||||
zap.String("sign_flow_status", callback.SignFlowStatus),
|
||||
)
|
||||
|
||||
case "3": // 已撤销(发起方撤销签署任务)
|
||||
s.logger.Info("合同签署流程已撤销")
|
||||
|
||||
// 可以在这里添加撤销处理逻辑
|
||||
s.logger.Info("合同签署流程撤销处理完成",
|
||||
zap.String("user_id", certification.UserID),
|
||||
zap.String("certification_id", certification.ID),
|
||||
zap.String("sign_flow_id", callback.SignFlowId),
|
||||
zap.String("sign_flow_status", callback.SignFlowStatus),
|
||||
)
|
||||
|
||||
// 暂无撤销业务逻辑
|
||||
|
||||
case "5": // 已过期(签署截止日到期后触发)
|
||||
s.logger.Info("合同签署流程已过期")
|
||||
|
||||
// 可以在这里添加过期处理逻辑
|
||||
s.logger.Info("合同签署流程过期处理完成",
|
||||
zap.String("user_id", certification.UserID),
|
||||
zap.String("certification_id", certification.ID),
|
||||
zap.String("sign_flow_id", callback.SignFlowId),
|
||||
zap.String("sign_flow_status", callback.SignFlowStatus),
|
||||
)
|
||||
|
||||
// 暂无过期业务逻辑
|
||||
|
||||
case "7": // 已拒签(签署方拒绝签署)
|
||||
s.logger.Info("合同签署流程已拒签")
|
||||
|
||||
// 可以在这里添加拒签处理逻辑
|
||||
s.logger.Info("合同签署流程拒签处理完成",
|
||||
zap.String("user_id", certification.UserID),
|
||||
zap.String("certification_id", certification.ID),
|
||||
zap.String("sign_flow_id", callback.SignFlowId),
|
||||
zap.String("sign_flow_status", callback.SignFlowStatus),
|
||||
)
|
||||
|
||||
default:
|
||||
s.logger.Warn("未知的签署流程状态",
|
||||
zap.String("sign_flow_status", callback.SignFlowStatus),
|
||||
zap.String("sign_flow_id", callback.SignFlowId),
|
||||
)
|
||||
|
||||
// 暂无拒签业务逻辑
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 企业认证成功后操作
|
||||
func (s *EsignCallbackApplicationServiceImpl) completeEnterpriseAuth(ctx context.Context, certification *entities.Certification) error {
|
||||
err := s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error {
|
||||
// 1. 获取企业信息提交记录
|
||||
enterpriseRecord, err := s.enterpriseRecordService.GetLatestByUserID(txCtx, certification.UserID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取企业信息失败: %w", err)
|
||||
}
|
||||
// 2. 转换状态
|
||||
if err := s.certWorkflowService.CompleteEnterpriseVerification(txCtx, certification.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
// 3. 创建企业信息
|
||||
_, err = s.enterpriseService.CreateEnterpriseInfo(txCtx, certification.UserID, enterpriseRecord.CompanyName, enterpriseRecord.UnifiedSocialCode, enterpriseRecord.LegalPersonName, enterpriseRecord.LegalPersonID)
|
||||
if err != nil {
|
||||
s.logger.Warn("创建用户企业信息失败", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("完成企业认证失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user