f
This commit is contained in:
@@ -255,7 +255,7 @@ esign:
|
|||||||
app_id: "7439073138"
|
app_id: "7439073138"
|
||||||
app_secret: "d76e27fdd169b391e09262a0959dac5c"
|
app_secret: "d76e27fdd169b391e09262a0959dac5c"
|
||||||
server_url: "https://smlopenapi.esign.cn"
|
server_url: "https://smlopenapi.esign.cn"
|
||||||
template_id: "9f7a3f63cc5a48b085b127ba027d234d"
|
template_id: "6c91bfd5b1bb48c585f5eaceeea893d4"
|
||||||
contract:
|
contract:
|
||||||
name: "天远数据API合作协议"
|
name: "天远数据API合作协议"
|
||||||
expire_days: 7
|
expire_days: 7
|
||||||
|
|||||||
@@ -46,10 +46,10 @@ ocr:
|
|||||||
# 📝 e签宝服务配置
|
# 📝 e签宝服务配置
|
||||||
# ===========================================
|
# ===========================================
|
||||||
esign:
|
esign:
|
||||||
app_id: "7439073713"
|
app_id: "5112008003"
|
||||||
app_secret: "c7d8cb0d701f7890601d221e9b6edfef"
|
app_secret: "d487672273e7aa70c800804a1d9499b9"
|
||||||
server_url: "https://smlopenapi.esign.cn"
|
server_url: "https://openapi.esign.cn"
|
||||||
template_id: "9f7a3f63cc5a48b085b127ba027d234d"
|
template_id: "6c91bfd5b1bb48c585f5eaceeea893d4"
|
||||||
contract:
|
contract:
|
||||||
name: "天远数据API合作协议"
|
name: "天远数据API合作协议"
|
||||||
expire_days: 7
|
expire_days: 7
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ esign:
|
|||||||
app_id: "5112008003"
|
app_id: "5112008003"
|
||||||
app_secret: "d487672273e7aa70c800804a1d9499b9"
|
app_secret: "d487672273e7aa70c800804a1d9499b9"
|
||||||
server_url: "https://openapi.esign.cn"
|
server_url: "https://openapi.esign.cn"
|
||||||
template_id: "9f7a3f63cc5a48b085b127ba027d234d"
|
template_id: "6c91bfd5b1bb48c585f5eaceeea893d4"
|
||||||
contract:
|
contract:
|
||||||
name: "天远数据API合作协议"
|
name: "天远数据API合作协议"
|
||||||
expire_days: 7
|
expire_days: 7
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"tyapi-server/internal/application/certification/dto/commands"
|
"tyapi-server/internal/application/certification/dto/commands"
|
||||||
"tyapi-server/internal/application/certification/dto/queries"
|
"tyapi-server/internal/application/certification/dto/queries"
|
||||||
"tyapi-server/internal/application/certification/dto/responses"
|
"tyapi-server/internal/application/certification/dto/responses"
|
||||||
@@ -19,11 +18,11 @@ import (
|
|||||||
certification_value_objects "tyapi-server/internal/domains/certification/entities/value_objects"
|
certification_value_objects "tyapi-server/internal/domains/certification/entities/value_objects"
|
||||||
"tyapi-server/internal/domains/certification/enums"
|
"tyapi-server/internal/domains/certification/enums"
|
||||||
"tyapi-server/internal/domains/certification/repositories"
|
"tyapi-server/internal/domains/certification/repositories"
|
||||||
|
"tyapi-server/internal/domains/certification/services"
|
||||||
finance_entities "tyapi-server/internal/domains/finance/entities"
|
finance_entities "tyapi-server/internal/domains/finance/entities"
|
||||||
finance_repositories "tyapi-server/internal/domains/finance/repositories"
|
finance_repositories "tyapi-server/internal/domains/finance/repositories"
|
||||||
"tyapi-server/internal/domains/certification/services"
|
|
||||||
subordinate_repositories "tyapi-server/internal/domains/subordinate/repositories"
|
|
||||||
finance_service "tyapi-server/internal/domains/finance/services"
|
finance_service "tyapi-server/internal/domains/finance/services"
|
||||||
|
subordinate_repositories "tyapi-server/internal/domains/subordinate/repositories"
|
||||||
user_entities "tyapi-server/internal/domains/user/entities"
|
user_entities "tyapi-server/internal/domains/user/entities"
|
||||||
user_service "tyapi-server/internal/domains/user/services"
|
user_service "tyapi-server/internal/domains/user/services"
|
||||||
"tyapi-server/internal/infrastructure/external/notification"
|
"tyapi-server/internal/infrastructure/external/notification"
|
||||||
@@ -32,6 +31,8 @@ import (
|
|||||||
"tyapi-server/internal/shared/esign"
|
"tyapi-server/internal/shared/esign"
|
||||||
sharedOCR "tyapi-server/internal/shared/ocr"
|
sharedOCR "tyapi-server/internal/shared/ocr"
|
||||||
|
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -726,7 +727,7 @@ func (s *CertificationApplicationServiceImpl) HandleEsignCallback(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 生成合同
|
// 生成合同
|
||||||
err = s.generateAndAddContractFile(txCtx, cert, record.CompanyName, record.LegalPersonName, record.UnifiedSocialCode, record.EnterpriseAddress, record.LegalPersonPhone, record.LegalPersonID)
|
err = s.generateAndAddContractFile(txCtx, cert, record.CompanyName, record.UnifiedSocialCode, record.EnterpriseAddress, pickAuthorizedRepName(record, record.LegalPersonName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1287,6 +1288,25 @@ func (s *CertificationApplicationServiceImpl) validateContractApplicationPrecond
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pickAuthorizedRepName 合同模板「客户授权代表」: 优先企业提交记录中的授权代表, 否则为法定代表人
|
||||||
|
func pickAuthorizedRepName(record *entities.EnterpriseInfoSubmitRecord, legalPersonName string) string {
|
||||||
|
if record != nil && strings.TrimSpace(record.AuthorizedRepName) != "" {
|
||||||
|
return strings.TrimSpace(record.AuthorizedRepName)
|
||||||
|
}
|
||||||
|
return legalPersonName
|
||||||
|
}
|
||||||
|
|
||||||
|
// pickEnterpriseString 优先用户域企业表字段,为空则用最近一次认证提交记录(避免 enterprise_infos 未同步导致合同控件无值)
|
||||||
|
func pickEnterpriseString(primary string, record *entities.EnterpriseInfoSubmitRecord, fromRecord func(*entities.EnterpriseInfoSubmitRecord) string) string {
|
||||||
|
if strings.TrimSpace(primary) != "" {
|
||||||
|
return strings.TrimSpace(primary)
|
||||||
|
}
|
||||||
|
if record == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(fromRecord(record))
|
||||||
|
}
|
||||||
|
|
||||||
// generateContractAndSignURL 生成合同和签署链接
|
// generateContractAndSignURL 生成合同和签署链接
|
||||||
func (s *CertificationApplicationServiceImpl) generateContractAndSignURL(ctx context.Context, cert *entities.Certification, enterpriseInfo *user_entities.EnterpriseInfo) (*certification_value_objects.ContractInfo, error) {
|
func (s *CertificationApplicationServiceImpl) generateContractAndSignURL(ctx context.Context, cert *entities.Certification, enterpriseInfo *user_entities.EnterpriseInfo) (*certification_value_objects.ContractInfo, error) {
|
||||||
// 发起签署流程
|
// 发起签署流程
|
||||||
@@ -1361,7 +1381,7 @@ func (s *CertificationApplicationServiceImpl) completeEnterpriseVerification(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 生成合同
|
// 生成合同
|
||||||
err = s.generateAndAddContractFile(ctx, cert, record.CompanyName, record.LegalPersonName, record.UnifiedSocialCode, record.EnterpriseAddress, record.LegalPersonPhone, record.LegalPersonID)
|
err = s.generateAndAddContractFile(ctx, cert, record.CompanyName, record.UnifiedSocialCode, record.EnterpriseAddress, pickAuthorizedRepName(record, record.LegalPersonName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1383,27 +1403,41 @@ func (s *CertificationApplicationServiceImpl) generateAndAddContractFile(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
cert *entities.Certification,
|
cert *entities.Certification,
|
||||||
companyName string,
|
companyName string,
|
||||||
legalPersonName string,
|
|
||||||
unifiedSocialCode string,
|
unifiedSocialCode string,
|
||||||
enterpriseAddress string,
|
enterpriseAddress string,
|
||||||
legalPersonPhone string,
|
authorizedRepName string,
|
||||||
legalPersonID string,
|
|
||||||
) error {
|
) error {
|
||||||
s.logger.Info("合同生成-步骤1-开始填充合同模板",
|
s.logger.Info("合同生成-步骤1-开始填充合同模板",
|
||||||
zap.String("user_id", cert.UserID),
|
zap.String("user_id", cert.UserID),
|
||||||
zap.String("company_name", companyName))
|
zap.String("company_name", companyName))
|
||||||
|
|
||||||
|
// 协议编号:已有则复用,否则新生成
|
||||||
|
if cert.ContractCode == "" {
|
||||||
|
cert.SetContractCode(user_entities.GenerateContractCode(user_entities.ContractTypeCooperation))
|
||||||
|
}
|
||||||
|
agreementNo := cert.ContractCode
|
||||||
|
|
||||||
|
// e签宝日期控件格式必须与模板预设一致(本模板为 yyyy年MM月dd日)
|
||||||
|
signDate := time.Now().Format("2006年01月02日")
|
||||||
|
|
||||||
|
if strings.TrimSpace(unifiedSocialCode) == "" {
|
||||||
|
s.logger.Warn("合同模板控件 jftyshxydm:统一社会信用代码为空;若 PDF 上该处空白,请核对 enterprise_infos.unified_social_code、提交记录或 e签宝模板控件 componentKey 是否与代码键名一致",
|
||||||
|
zap.String("user_id", cert.UserID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 控件 key 须与 e签宝控制台该控件「控件编码/componentKey」完全一致(区分大小写)
|
||||||
fileComponent := map[string]string{
|
fileComponent := map[string]string{
|
||||||
"YFCompanyName": companyName,
|
"jfqym": companyName,
|
||||||
"YFCompanyName2": companyName,
|
"jfqym2": companyName,
|
||||||
"YFLegalPersonName": legalPersonName,
|
"jfsqdb": authorizedRepName,
|
||||||
"YFLegalPersonName2": legalPersonName,
|
"jftyshxydm": unifiedSocialCode,
|
||||||
"YFUnifiedSocialCode": unifiedSocialCode,
|
"jflxdz": enterpriseAddress,
|
||||||
"YFEnterpriseAddress": enterpriseAddress,
|
// 甲方
|
||||||
"YFContactPerson": legalPersonName,
|
"xybh": agreementNo,
|
||||||
"YFMobile": legalPersonPhone,
|
"qsrq1": signDate,
|
||||||
"SignDate": time.Now().Format("2006年01月02日"),
|
"qsrq3": signDate,
|
||||||
"SignDate2": time.Now().Format("2006年01月02日"),
|
// 乙方
|
||||||
"SignDate3": time.Now().Format("2006年01月02日"),
|
"qsrq2": signDate,
|
||||||
}
|
}
|
||||||
fillTemplateResp, err := s.esignClient.FillTemplate(fileComponent)
|
fillTemplateResp, err := s.esignClient.FillTemplate(fileComponent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1412,7 +1446,8 @@ func (s *CertificationApplicationServiceImpl) generateAndAddContractFile(
|
|||||||
}
|
}
|
||||||
s.logger.Info("合同生成-步骤1-模板填充成功",
|
s.logger.Info("合同生成-步骤1-模板填充成功",
|
||||||
zap.String("user_id", cert.UserID),
|
zap.String("user_id", cert.UserID),
|
||||||
zap.String("file_id", fillTemplateResp.FileID))
|
zap.String("file_id", fillTemplateResp.FileID),
|
||||||
|
zap.String("contract_code", agreementNo))
|
||||||
err = cert.AddContractFileID(fillTemplateResp.FileID, fillTemplateResp.FileDownloadUrl)
|
err = cert.AddContractFileID(fillTemplateResp.FileID, fillTemplateResp.FileDownloadUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("加入合同文件ID链接失败", zap.Error(err))
|
s.logger.Error("加入合同文件ID链接失败", zap.Error(err))
|
||||||
@@ -1432,9 +1467,26 @@ func (s *CertificationApplicationServiceImpl) updateContractFile(ctx context.Con
|
|||||||
s.logger.Error("获取企业信息失败", zap.Error(err))
|
s.logger.Error("获取企业信息失败", zap.Error(err))
|
||||||
return fmt.Errorf("获取企业信息失败: %w", err)
|
return fmt.Errorf("获取企业信息失败: %w", err)
|
||||||
}
|
}
|
||||||
|
if enterpriseInfo.EnterpriseInfo == nil {
|
||||||
|
return fmt.Errorf("用户企业信息不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
ei := enterpriseInfo.EnterpriseInfo
|
||||||
|
submitRec, recErr := s.enterpriseInfoSubmitRecordRepo.FindLatestByUserID(ctx, cert.UserID)
|
||||||
|
if recErr != nil {
|
||||||
|
s.logger.Warn("更新合同时加载企业提交记录失败,统一社会信用代码等仅以用户域为准",
|
||||||
|
zap.String("user_id", cert.UserID),
|
||||||
|
zap.Error(recErr))
|
||||||
|
submitRec = nil
|
||||||
|
}
|
||||||
|
authRep := pickAuthorizedRepName(submitRec, ei.LegalPersonName)
|
||||||
|
|
||||||
|
company := pickEnterpriseString(ei.CompanyName, submitRec, func(r *entities.EnterpriseInfoSubmitRecord) string { return r.CompanyName })
|
||||||
|
uscc := pickEnterpriseString(ei.UnifiedSocialCode, submitRec, func(r *entities.EnterpriseInfoSubmitRecord) string { return r.UnifiedSocialCode })
|
||||||
|
addr := pickEnterpriseString(ei.EnterpriseAddress, submitRec, func(r *entities.EnterpriseInfoSubmitRecord) string { return r.EnterpriseAddress })
|
||||||
|
|
||||||
// 生成合同
|
// 生成合同
|
||||||
err = s.generateAndAddContractFile(ctx, cert, enterpriseInfo.EnterpriseInfo.CompanyName, enterpriseInfo.EnterpriseInfo.LegalPersonName, enterpriseInfo.EnterpriseInfo.UnifiedSocialCode, enterpriseInfo.EnterpriseInfo.EnterpriseAddress, enterpriseInfo.EnterpriseInfo.LegalPersonPhone, enterpriseInfo.EnterpriseInfo.LegalPersonID)
|
err = s.generateAndAddContractFile(ctx, cert, company, uscc, addr, authRep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1571,16 +1623,29 @@ func (s *CertificationApplicationServiceImpl) handleContractAfterSignComplete(ct
|
|||||||
|
|
||||||
s.logger.Info("合同文件已上传七牛云", zap.String("file_name", fileName), zap.String("qiniu_url", qiniuURL))
|
s.logger.Info("合同文件已上传七牛云", zap.String("file_name", fileName), zap.String("qiniu_url", qiniuURL))
|
||||||
|
|
||||||
// 4. 保存到合同聚合根
|
// 4. 保存到合同聚合根(复用认证阶段生成的合同编号;旧数据无编号时退回自动生成)
|
||||||
_, err = s.contractAggregateService.CreateContract(
|
if strings.TrimSpace(cert.ContractCode) != "" {
|
||||||
ctx,
|
_, err = s.contractAggregateService.CreateContractWithCode(
|
||||||
user.EnterpriseInfo.ID,
|
ctx,
|
||||||
cert.UserID,
|
user.EnterpriseInfo.ID,
|
||||||
fileName,
|
cert.UserID,
|
||||||
user_entities.ContractTypeCooperation,
|
fileName,
|
||||||
fileId,
|
user_entities.ContractTypeCooperation,
|
||||||
qiniuURL,
|
fileId,
|
||||||
)
|
qiniuURL,
|
||||||
|
strings.TrimSpace(cert.ContractCode),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
_, err = s.contractAggregateService.CreateContract(
|
||||||
|
ctx,
|
||||||
|
user.EnterpriseInfo.ID,
|
||||||
|
cert.UserID,
|
||||||
|
fileName,
|
||||||
|
user_entities.ContractTypeCooperation,
|
||||||
|
fileId,
|
||||||
|
qiniuURL,
|
||||||
|
)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("保存合同信息到聚合根失败", zap.String("file_name", fileName), zap.Error(err))
|
s.logger.Error("保存合同信息到聚合根失败", zap.String("file_name", fileName), zap.Error(err))
|
||||||
continue
|
continue
|
||||||
|
|||||||
1733
internal/application/certification/new.md
Normal file
1733
internal/application/certification/new.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,8 @@ type Certification struct {
|
|||||||
EsignFlowID string `gorm:"type:varchar(500)" json:"esign_flow_id,omitempty" comment:"签署流程ID"`
|
EsignFlowID string `gorm:"type:varchar(500)" json:"esign_flow_id,omitempty" comment:"签署流程ID"`
|
||||||
ContractURL string `gorm:"type:varchar(500)" json:"contract_url,omitempty" comment:"合同文件访问链接"`
|
ContractURL string `gorm:"type:varchar(500)" json:"contract_url,omitempty" comment:"合同文件访问链接"`
|
||||||
ContractSignURL string `gorm:"type:varchar(500)" json:"contract_sign_url,omitempty" comment:"合同签署链接"`
|
ContractSignURL string `gorm:"type:varchar(500)" json:"contract_sign_url,omitempty" comment:"合同签署链接"`
|
||||||
|
// ContractCode 合作协议编号(与电子合同模板控件 xybh 一致,签署完成后写入用户域合同)
|
||||||
|
ContractCode string `gorm:"type:varchar(255)" json:"contract_code,omitempty" comment:"合作协议编号"`
|
||||||
|
|
||||||
// === 失败信息 ===
|
// === 失败信息 ===
|
||||||
FailureReason enums.FailureReason `gorm:"type:varchar(100)" json:"failure_reason,omitempty" comment:"失败原因"`
|
FailureReason enums.FailureReason `gorm:"type:varchar(100)" json:"failure_reason,omitempty" comment:"失败原因"`
|
||||||
@@ -323,6 +325,11 @@ func (c *Certification) ApplyContract(EsignFlowID string, ContractSignURL string
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetContractCode 设置合作协议编号(首次生成合同时写入,后续换文件复用)
|
||||||
|
func (c *Certification) SetContractCode(code string) {
|
||||||
|
c.ContractCode = code
|
||||||
|
}
|
||||||
|
|
||||||
// AddContractFileID 生成合同文件
|
// AddContractFileID 生成合同文件
|
||||||
func (c *Certification) AddContractFileID(contractFileID string, contractURL string) error {
|
func (c *Certification) AddContractFileID(contractFileID string, contractURL string) error {
|
||||||
c.ContractFileID = contractFileID
|
c.ContractFileID = contractFileID
|
||||||
|
|||||||
@@ -119,6 +119,58 @@ func NewContractInfo(enterpriseInfoID, userID, contractName string, contractType
|
|||||||
return contractInfo, nil
|
return contractInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewContractInfoWithContractCode 使用指定合同编号创建合同信息(与认证阶段生成的编号一致)
|
||||||
|
func NewContractInfoWithContractCode(enterpriseInfoID, userID, contractName string, contractType ContractType, contractFileID, contractFileURL, contractCode string) (*ContractInfo, error) {
|
||||||
|
if enterpriseInfoID == "" {
|
||||||
|
return nil, fmt.Errorf("企业信息ID不能为空")
|
||||||
|
}
|
||||||
|
if userID == "" {
|
||||||
|
return nil, fmt.Errorf("用户ID不能为空")
|
||||||
|
}
|
||||||
|
if contractName == "" {
|
||||||
|
return nil, fmt.Errorf("合同名称不能为空")
|
||||||
|
}
|
||||||
|
if contractType == "" {
|
||||||
|
return nil, fmt.Errorf("合同类型不能为空")
|
||||||
|
}
|
||||||
|
if contractFileID == "" {
|
||||||
|
return nil, fmt.Errorf("合同文件ID不能为空")
|
||||||
|
}
|
||||||
|
if contractFileURL == "" {
|
||||||
|
return nil, fmt.Errorf("合同文件URL不能为空")
|
||||||
|
}
|
||||||
|
if contractCode == "" {
|
||||||
|
return nil, fmt.Errorf("合同编号不能为空")
|
||||||
|
}
|
||||||
|
if !isValidContractType(contractType) {
|
||||||
|
return nil, fmt.Errorf("无效的合同类型: %s", contractType)
|
||||||
|
}
|
||||||
|
|
||||||
|
contractInfo := &ContractInfo{
|
||||||
|
ID: uuid.New().String(),
|
||||||
|
EnterpriseInfoID: enterpriseInfoID,
|
||||||
|
UserID: userID,
|
||||||
|
ContractCode: contractCode,
|
||||||
|
ContractName: contractName,
|
||||||
|
ContractType: contractType,
|
||||||
|
ContractFileID: contractFileID,
|
||||||
|
ContractFileURL: contractFileURL,
|
||||||
|
domainEvents: make([]interface{}, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
contractInfo.addDomainEvent(&ContractInfoCreatedEvent{
|
||||||
|
ContractInfoID: contractInfo.ID,
|
||||||
|
EnterpriseInfoID: enterpriseInfoID,
|
||||||
|
UserID: userID,
|
||||||
|
ContractCode: contractCode,
|
||||||
|
ContractName: contractName,
|
||||||
|
ContractType: string(contractType),
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return contractInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ================ 聚合根核心方法 ================
|
// ================ 聚合根核心方法 ================
|
||||||
|
|
||||||
// UpdateContractInfo 更新合同信息
|
// UpdateContractInfo 更新合同信息
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
type ContractAggregateService interface {
|
type ContractAggregateService interface {
|
||||||
// 聚合根生命周期管理
|
// 聚合根生命周期管理
|
||||||
CreateContract(ctx context.Context, enterpriseInfoID, userID, contractName string, contractType entities.ContractType, contractFileID, contractFileURL string) (*entities.ContractInfo, error)
|
CreateContract(ctx context.Context, enterpriseInfoID, userID, contractName string, contractType entities.ContractType, contractFileID, contractFileURL string) (*entities.ContractInfo, error)
|
||||||
|
CreateContractWithCode(ctx context.Context, enterpriseInfoID, userID, contractName string, contractType entities.ContractType, contractFileID, contractFileURL, contractCode string) (*entities.ContractInfo, error)
|
||||||
LoadContract(ctx context.Context, contractID string) (*entities.ContractInfo, error)
|
LoadContract(ctx context.Context, contractID string) (*entities.ContractInfo, error)
|
||||||
SaveContract(ctx context.Context, contract *entities.ContractInfo) error
|
SaveContract(ctx context.Context, contract *entities.ContractInfo) error
|
||||||
DeleteContract(ctx context.Context, contractID string) error
|
DeleteContract(ctx context.Context, contractID string) error
|
||||||
@@ -94,6 +95,51 @@ func (s *ContractAggregateServiceImpl) CreateContract(
|
|||||||
return contract, nil
|
return contract, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateContractWithCode 使用指定合同编号创建合同信息(与认证合同模板上的编号一致)
|
||||||
|
func (s *ContractAggregateServiceImpl) CreateContractWithCode(
|
||||||
|
ctx context.Context,
|
||||||
|
enterpriseInfoID, userID, contractName string,
|
||||||
|
contractType entities.ContractType,
|
||||||
|
contractFileID, contractFileURL, contractCode string,
|
||||||
|
) (*entities.ContractInfo, error) {
|
||||||
|
s.logger.Debug("创建合同信息(指定编号)",
|
||||||
|
zap.String("enterprise_info_id", enterpriseInfoID),
|
||||||
|
zap.String("user_id", userID),
|
||||||
|
zap.String("contract_name", contractName),
|
||||||
|
zap.String("contract_code", contractCode),
|
||||||
|
zap.String("contract_type", string(contractType)))
|
||||||
|
|
||||||
|
exists, err := s.ExistsByContractFileID(ctx, contractFileID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("检查合同文件ID失败: %w", err)
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
return nil, fmt.Errorf("合同文件ID已存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
contract, err := entities.NewContractInfoWithContractCode(enterpriseInfoID, userID, contractName, contractType, contractFileID, contractFileURL, contractCode)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("创建合同信息失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.ValidateBusinessRules(ctx, contract); err != nil {
|
||||||
|
return nil, fmt.Errorf("业务规则验证失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.SaveContract(ctx, contract)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("保存合同信息失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Info("合同信息创建成功",
|
||||||
|
zap.String("contract_id", contract.ID),
|
||||||
|
zap.String("enterprise_info_id", enterpriseInfoID),
|
||||||
|
zap.String("contract_code", contractCode),
|
||||||
|
zap.String("contract_name", contractName))
|
||||||
|
|
||||||
|
return contract, nil
|
||||||
|
}
|
||||||
|
|
||||||
// LoadContract 加载合同信息
|
// LoadContract 加载合同信息
|
||||||
func (s *ContractAggregateServiceImpl) LoadContract(ctx context.Context, contractID string) (*entities.ContractInfo, error) {
|
func (s *ContractAggregateServiceImpl) LoadContract(ctx context.Context, contractID string) (*entities.ContractInfo, error) {
|
||||||
s.logger.Debug("加载合同信息", zap.String("contract_id", contractID))
|
s.logger.Debug("加载合同信息", zap.String("contract_id", contractID))
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ func (s *SignFlowService) UpdateConfig(config *Config) {
|
|||||||
// 创建包含多个签署人的签署流程,支持自动盖章和手动签署
|
// 创建包含多个签署人的签署流程,支持自动盖章和手动签署
|
||||||
func (s *SignFlowService) Create(req *CreateSignFlowRequest) (string, error) {
|
func (s *SignFlowService) Create(req *CreateSignFlowRequest) (string, error) {
|
||||||
fmt.Println("开始创建签署流程...")
|
fmt.Println("开始创建签署流程...")
|
||||||
fmt.Println("(将创建包含甲方自动盖章和乙方手动签署的流程)")
|
fmt.Println("(将创建包含甲方手动签署和乙方自动盖章的流程)")
|
||||||
|
|
||||||
// 构建甲方签署人信息(自动盖章)
|
// 构建甲方签署人信息(手动签署)
|
||||||
partyASigner := s.buildPartyASigner(req.FileID)
|
partyASigner := s.buildPartyASigner(req.FileID, req.SignerAccount, req.SignerName, req.TransactorPhone, req.TransactorName, req.TransactorIDCardNum)
|
||||||
|
|
||||||
// 构建乙方签署人信息(手动签署)
|
// 构建乙方签署人信息(自动盖章)
|
||||||
partyBSigner := s.buildPartyBSigner(req.FileID, req.SignerAccount, req.SignerName, req.TransactorPhone, req.TransactorName, req.TransactorIDCardNum)
|
partyBSigner := s.buildPartyBSigner(req.FileID)
|
||||||
|
|
||||||
signers := []SignerInfo{partyASigner, partyBSigner}
|
signers := []SignerInfo{partyASigner, partyBSigner}
|
||||||
|
|
||||||
@@ -128,34 +128,11 @@ func (s *SignFlowService) GetSignURL(signFlowID, psnAccount, orgName string) (st
|
|||||||
return response.Data.Url, response.Data.ShortUrl, nil
|
return response.Data.Url, response.Data.ShortUrl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildPartyASigner 构建甲方签署人信息(自动盖章)
|
// buildPartyASigner 构建甲方签署人信息(手动签署)
|
||||||
func (s *SignFlowService) buildPartyASigner(fileID string) SignerInfo {
|
func (s *SignFlowService) buildPartyASigner(fileID, signerAccount, signerName, transactorPhone, transactorName, transactorIDCardNum string) SignerInfo {
|
||||||
return SignerInfo{
|
|
||||||
SignConfig: &SignConfig{SignOrder: 1},
|
|
||||||
SignerType: SignerTypeOrg,
|
|
||||||
SignFields: []SignField{
|
|
||||||
{
|
|
||||||
CustomBizNum: "甲方签章",
|
|
||||||
FileId: fileID,
|
|
||||||
NormalSignFieldConfig: &NormalSignFieldConfig{
|
|
||||||
AutoSign: true,
|
|
||||||
SignFieldStyle: SignFieldStyleNormal,
|
|
||||||
SignFieldPosition: &SignFieldPosition{
|
|
||||||
PositionPage: "8",
|
|
||||||
PositionX: 200,
|
|
||||||
PositionY: 430,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// buildPartyBSigner 构建乙方签署人信息(手动签署)
|
|
||||||
func (s *SignFlowService) buildPartyBSigner(fileID, signerAccount, signerName, transactorPhone, transactorName, transactorIDCardNum string) SignerInfo {
|
|
||||||
return SignerInfo{
|
return SignerInfo{
|
||||||
SignConfig: &SignConfig{
|
SignConfig: &SignConfig{
|
||||||
SignOrder: 2,
|
SignOrder: 1,
|
||||||
},
|
},
|
||||||
AuthConfig: &AuthConfig{
|
AuthConfig: &AuthConfig{
|
||||||
PsnAvailableAuthModes: []string{AuthModeMobile3},
|
PsnAvailableAuthModes: []string{AuthModeMobile3},
|
||||||
@@ -182,19 +159,66 @@ func (s *SignFlowService) buildPartyBSigner(fileID, signerAccount, signerName, t
|
|||||||
},
|
},
|
||||||
SignFields: []SignField{
|
SignFields: []SignField{
|
||||||
{
|
{
|
||||||
CustomBizNum: "乙方签章",
|
CustomBizNum: "甲方签章",
|
||||||
FileId: fileID,
|
FileId: fileID,
|
||||||
NormalSignFieldConfig: &NormalSignFieldConfig{
|
NormalSignFieldConfig: &NormalSignFieldConfig{
|
||||||
AutoSign: false,
|
AutoSign: false,
|
||||||
SignFieldStyle: SignFieldStyleNormal,
|
SignFieldStyle: SignFieldStyleNormal,
|
||||||
SignFieldPosition: &SignFieldPosition{
|
SignFieldPosition: &SignFieldPosition{
|
||||||
PositionPage: "8",
|
PositionPage: "10",
|
||||||
PositionX: 450,
|
PositionX: 165,
|
||||||
PositionY: 430,
|
PositionY: 197,
|
||||||
},
|
},
|
||||||
OrgSealBizTypes: "PUBLIC",
|
OrgSealBizTypes: "PUBLIC",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
CustomBizNum: "甲方骑缝章", // 建议设唯一标识,便于调试
|
||||||
|
FileId: fileID,
|
||||||
|
NormalSignFieldConfig: &NormalSignFieldConfig{
|
||||||
|
AutoSign: false,
|
||||||
|
SignFieldStyle: SignFieldStyleSeam, // 必须为 2(Edges)
|
||||||
|
SignFieldPosition: &SignFieldPosition{
|
||||||
|
AcrossPageMode: "ALL", // 覆盖全部页面(推荐)
|
||||||
|
PositionY: 694.0, // 您指定的 Y 坐标(float64)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildPartyBSigner 构建乙方签署人信息(自动盖章)
|
||||||
|
func (s *SignFlowService) buildPartyBSigner(fileID string) SignerInfo {
|
||||||
|
return SignerInfo{
|
||||||
|
SignConfig: &SignConfig{SignOrder: 2},
|
||||||
|
SignerType: SignerTypeOrg,
|
||||||
|
SignFields: []SignField{
|
||||||
|
{
|
||||||
|
CustomBizNum: "乙方签章",
|
||||||
|
FileId: fileID,
|
||||||
|
NormalSignFieldConfig: &NormalSignFieldConfig{
|
||||||
|
AutoSign: true,
|
||||||
|
SignFieldStyle: SignFieldStyleNormal,
|
||||||
|
SignFieldPosition: &SignFieldPosition{
|
||||||
|
PositionPage: "10",
|
||||||
|
PositionX: 403,
|
||||||
|
PositionY: 197,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CustomBizNum: "乙方骑缝章", // 建议设唯一标识,便于调试
|
||||||
|
FileId: fileID,
|
||||||
|
NormalSignFieldConfig: &NormalSignFieldConfig{
|
||||||
|
AutoSign: true, // 骑缝章也支持自动签署
|
||||||
|
SignFieldStyle: SignFieldStyleSeam, // 必须为 2(Edges)
|
||||||
|
SignFieldPosition: &SignFieldPosition{
|
||||||
|
AcrossPageMode: "ALL", // 覆盖全部页面(推荐)
|
||||||
|
PositionY: 554.0, // 您指定的 Y 坐标(float64)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ func CreateDefaultComponents() []Component {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
ComponentKey: "QDRQ",
|
ComponentKey: "QDRQ",
|
||||||
ComponentValue: time.Now().Format("2006年01月02日"),
|
ComponentValue: time.Now().Format("2006-01-02"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ type SignFlowConfig struct {
|
|||||||
|
|
||||||
// RedirectConfig 重定向配置
|
// RedirectConfig 重定向配置
|
||||||
type RedirectConfig struct {
|
type RedirectConfig struct {
|
||||||
RedirectUrl string `json:"redirectUrl"` // 重定向URL
|
RedirectUrl string `json:"redirectUrl"` // 重定向URL
|
||||||
RedirectDelayTime int64 `json:"redirectDelayTime"` //重定向时间
|
RedirectDelayTime int64 `json:"redirectDelayTime"` //重定向时间
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthConfig 认证配置
|
// AuthConfig 认证配置
|
||||||
@@ -170,9 +170,10 @@ type NormalSignFieldConfig struct {
|
|||||||
|
|
||||||
// SignFieldPosition 签署区位置
|
// SignFieldPosition 签署区位置
|
||||||
type SignFieldPosition struct {
|
type SignFieldPosition struct {
|
||||||
PositionPage string `json:"positionPage"` // 页码
|
PositionPage string `json:"positionPage,omitempty"` // 页码(骑缝章可与 acrossPageMode 组合)
|
||||||
PositionX float64 `json:"positionX"` // X坐标
|
PositionX float64 `json:"positionX,omitempty"` // X坐标
|
||||||
PositionY float64 `json:"positionY"` // Y坐标
|
PositionY float64 `json:"positionY,omitempty"` // Y坐标
|
||||||
|
AcrossPageMode string `json:"acrossPageMode,omitempty"` // 骑缝章跨页:如 ALL 表示全部页面
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 签署页面链接相关结构体 ====================
|
// ==================== 签署页面链接相关结构体 ====================
|
||||||
|
|||||||
@@ -104,11 +104,11 @@ func getCurrentDate() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// formatDateForTemplate 格式化日期用于模板填写
|
// formatDateForTemplate 格式化日期用于模板填写
|
||||||
// 格式: "2006年01月02日"
|
// e签宝日期控件通常预设为 yyyy-MM-dd,与中文年月日格式不兼容时需用本格式。
|
||||||
//
|
//
|
||||||
// 返回: 中文格式的日期字符串
|
// 返回: yyyy-MM-dd
|
||||||
func formatDateForTemplate() string {
|
func formatDateForTemplate() string {
|
||||||
return time.Now().Format("2006年01月02日")
|
return time.Now().Format("2006-01-02")
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateFileName 生成带时间戳的文件名
|
// generateFileName 生成带时间戳的文件名
|
||||||
|
|||||||
Reference in New Issue
Block a user