Merge branch 'main' of http://1.117.67.95:3000/team/tyapi-server
This commit is contained in:
@@ -37,6 +37,17 @@ type CertificationApplicationService interface {
|
||||
// AdminCompleteCertificationWithoutContract 管理员代用户完成认证(暂不关联合同)
|
||||
AdminCompleteCertificationWithoutContract(ctx context.Context, cmd *commands.AdminCompleteCertificationCommand) (*responses.CertificationResponse, error)
|
||||
|
||||
// AdminListSubmitRecords 管理端分页查询企业信息提交记录
|
||||
AdminListSubmitRecords(ctx context.Context, query *queries.AdminListSubmitRecordsQuery) (*responses.AdminSubmitRecordsListResponse, error)
|
||||
// AdminGetSubmitRecordByID 管理端获取单条提交记录详情
|
||||
AdminGetSubmitRecordByID(ctx context.Context, recordID string) (*responses.AdminSubmitRecordDetail, error)
|
||||
// AdminApproveSubmitRecord 管理端审核通过(按提交记录 ID)
|
||||
AdminApproveSubmitRecord(ctx context.Context, recordID, adminID, remark string) error
|
||||
// AdminRejectSubmitRecord 管理端审核拒绝(按提交记录 ID)
|
||||
AdminRejectSubmitRecord(ctx context.Context, recordID, adminID, remark string) error
|
||||
// AdminTransitionCertificationStatus 管理端按用户变更认证状态(以状态机为准:info_submitted=通过 / info_rejected=拒绝)
|
||||
AdminTransitionCertificationStatus(ctx context.Context, cmd *commands.AdminTransitionCertificationStatusCommand) error
|
||||
|
||||
// ================ e签宝回调处理 ================
|
||||
|
||||
// 处理e签宝回调
|
||||
|
||||
@@ -2,6 +2,7 @@ package certification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
"tyapi-server/internal/application/certification/dto/commands"
|
||||
"tyapi-server/internal/application/certification/dto/queries"
|
||||
"tyapi-server/internal/application/certification/dto/responses"
|
||||
"tyapi-server/internal/config"
|
||||
api_service "tyapi-server/internal/domains/api/services"
|
||||
"tyapi-server/internal/domains/certification/entities"
|
||||
certification_value_objects "tyapi-server/internal/domains/certification/entities/value_objects"
|
||||
@@ -18,8 +20,7 @@ import (
|
||||
"tyapi-server/internal/domains/certification/services"
|
||||
finance_service "tyapi-server/internal/domains/finance/services"
|
||||
user_entities "tyapi-server/internal/domains/user/entities"
|
||||
user_service "tyapi-server/internal/domains/user/services"
|
||||
"tyapi-server/internal/config"
|
||||
user_service "tyapi-server/internal/domains/user/services"
|
||||
"tyapi-server/internal/infrastructure/external/notification"
|
||||
"tyapi-server/internal/infrastructure/external/storage"
|
||||
"tyapi-server/internal/shared/database"
|
||||
@@ -51,6 +52,7 @@ type CertificationApplicationServiceImpl struct {
|
||||
|
||||
wechatWorkService *notification.WeChatWorkService
|
||||
logger *zap.Logger
|
||||
config *config.Config
|
||||
}
|
||||
|
||||
// NewCertificationApplicationService 创建认证应用服务
|
||||
@@ -93,6 +95,7 @@ func NewCertificationApplicationService(
|
||||
txManager: txManager,
|
||||
wechatWorkService: wechatSvc,
|
||||
logger: logger,
|
||||
config: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +109,16 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
s.logger.Info("开始提交企业信息",
|
||||
zap.String("user_id", cmd.UserID))
|
||||
|
||||
// 1.5 插入企业信息提交记录
|
||||
// 0. 若该用户已有待审核(认证状态仍在待审核),则不允许重复提交
|
||||
latestRecord, err := s.enterpriseInfoSubmitRecordRepo.FindLatestByUserID(ctx, cmd.UserID)
|
||||
if err == nil && latestRecord != nil {
|
||||
cert, loadErr := s.aggregateService.LoadCertificationByUserID(ctx, cmd.UserID)
|
||||
if loadErr == nil && cert != nil && cert.Status == enums.StatusInfoPendingReview {
|
||||
return nil, fmt.Errorf("您已有待审核的提交,请等待管理员审核后再操作")
|
||||
}
|
||||
}
|
||||
|
||||
// 1.5 插入企业信息提交记录(包含扩展字段)
|
||||
record := entities.NewEnterpriseInfoSubmitRecord(
|
||||
cmd.UserID,
|
||||
cmd.CompanyName,
|
||||
@@ -117,6 +129,36 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
cmd.EnterpriseAddress,
|
||||
)
|
||||
|
||||
// 扩展字段赋值
|
||||
record.BusinessLicenseImageURL = cmd.BusinessLicenseImageURL
|
||||
if len(cmd.OfficePlaceImageURLs) > 0 {
|
||||
if data, mErr := json.Marshal(cmd.OfficePlaceImageURLs); mErr == nil {
|
||||
record.OfficePlaceImageURLs = string(data)
|
||||
} else {
|
||||
s.logger.Warn("序列化办公场地图片URL失败", zap.Error(mErr))
|
||||
}
|
||||
}
|
||||
|
||||
record.APIUsage = cmd.APIUsage
|
||||
if len(cmd.ScenarioAttachmentURLs) > 0 {
|
||||
if data, mErr := json.Marshal(cmd.ScenarioAttachmentURLs); mErr == nil {
|
||||
record.ScenarioAttachmentURLs = string(data)
|
||||
} else {
|
||||
s.logger.Warn("序列化场景附件图片URL失败", zap.Error(mErr))
|
||||
}
|
||||
}
|
||||
// 授权代表信息落库
|
||||
record.AuthorizedRepName = cmd.AuthorizedRepName
|
||||
record.AuthorizedRepID = cmd.AuthorizedRepID
|
||||
record.AuthorizedRepPhone = cmd.AuthorizedRepPhone
|
||||
if len(cmd.AuthorizedRepIDImageURLs) > 0 {
|
||||
if data, mErr := json.Marshal(cmd.AuthorizedRepIDImageURLs); mErr == nil {
|
||||
record.AuthorizedRepIDImageURLs = string(data)
|
||||
} else {
|
||||
s.logger.Warn("序列化授权代表身份证图片URL失败", zap.Error(mErr))
|
||||
}
|
||||
}
|
||||
|
||||
// 验证验证码
|
||||
// 特殊验证码"768005"直接跳过验证环节
|
||||
if cmd.VerificationCode != "768005" {
|
||||
@@ -131,7 +173,8 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
}
|
||||
s.logger.Info("开始处理企业信息提交",
|
||||
zap.String("user_id", cmd.UserID))
|
||||
// 1. 检查企业信息是否重复(统一社会信用代码,已经认证了的,不能重复提交)
|
||||
// 1. 检查企业信息是否重复(统一社会信用代码:已认证或已提交待审核的都不能重复)
|
||||
// 1.1 已写入用户域 enterprise_infos 的(已完成认证)
|
||||
exists, err := s.userAggregateService.CheckUnifiedSocialCodeExists(ctx, cmd.UnifiedSocialCode, cmd.UserID)
|
||||
if err != nil {
|
||||
record.MarkAsFailed(err.Error())
|
||||
@@ -149,6 +192,24 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
}
|
||||
return nil, fmt.Errorf("该企业信息已被其他用户使用,请确认企业信息是否正确")
|
||||
}
|
||||
// 1.2 已提交/已通过验证的提交记录(尚未完成认证但已占用的信用代码)
|
||||
existsInSubmit, err := s.enterpriseInfoSubmitRecordRepo.ExistsByUnifiedSocialCodeExcludeUser(ctx, cmd.UnifiedSocialCode, cmd.UserID)
|
||||
if err != nil {
|
||||
record.MarkAsFailed(err.Error())
|
||||
saveErr := s.enterpriseInfoSubmitRecordService.Save(ctx, record)
|
||||
if saveErr != nil {
|
||||
return nil, fmt.Errorf("保存企业信息提交记录失败: %s", saveErr.Error())
|
||||
}
|
||||
return nil, fmt.Errorf("检查企业信息失败: %s", err.Error())
|
||||
}
|
||||
if existsInSubmit {
|
||||
record.MarkAsFailed("该企业信息已被其他用户使用,请确认企业信息是否正确")
|
||||
saveErr := s.enterpriseInfoSubmitRecordService.Save(ctx, record)
|
||||
if saveErr != nil {
|
||||
return nil, fmt.Errorf("保存企业信息提交记录失败: %s", saveErr.Error())
|
||||
}
|
||||
return nil, fmt.Errorf("该企业信息已被其他用户使用,请确认企业信息是否正确")
|
||||
}
|
||||
|
||||
enterpriseInfo := &certification_value_objects.EnterpriseInfo{
|
||||
CompanyName: cmd.CompanyName,
|
||||
@@ -179,10 +240,6 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
return nil, fmt.Errorf("企业信息验证失败, %s", err.Error())
|
||||
}
|
||||
record.MarkAsVerified()
|
||||
saveErr := s.enterpriseInfoSubmitRecordService.Save(ctx, record)
|
||||
if saveErr != nil {
|
||||
return nil, fmt.Errorf("保存企业信息提交记录失败: %s", saveErr.Error())
|
||||
}
|
||||
|
||||
var response *responses.CertificationResponse
|
||||
err = s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error {
|
||||
@@ -205,70 +262,47 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
return fmt.Errorf("加载认证信息失败: %s", err.Error())
|
||||
}
|
||||
|
||||
// 3. 调用e签宝看是否进行过认证
|
||||
respMeta := map[string]interface{}{}
|
||||
// 4. 提交企业信息:暂时跳过人工审核,直接进入「已提交」状态(第三步企业认证)
|
||||
// 恢复人工审核时改为 cert.SubmitEnterpriseInfoForReview(enterpriseInfo),并将 next_action 改为「请等待管理员审核企业信息」
|
||||
|
||||
identity, err := s.esignClient.QueryOrgIdentityInfo(&esign.QueryOrgIdentityRequest{
|
||||
OrgName: cmd.CompanyName,
|
||||
// 生成企业认证链接
|
||||
authURL, err := s.esignClient.GenerateEnterpriseAuth(&esign.EnterpriseAuthRequest{
|
||||
CompanyName: enterpriseInfo.CompanyName,
|
||||
UnifiedSocialCode: enterpriseInfo.UnifiedSocialCode,
|
||||
LegalPersonName: enterpriseInfo.LegalPersonName,
|
||||
LegalPersonID: enterpriseInfo.LegalPersonID,
|
||||
TransactorName: enterpriseInfo.LegalPersonName,
|
||||
TransactorMobile: enterpriseInfo.LegalPersonPhone,
|
||||
TransactorID: enterpriseInfo.LegalPersonID,
|
||||
})
|
||||
if identity != nil && identity.Data.RealnameStatus == 1 {
|
||||
// 已提交
|
||||
err = cert.SubmitEnterpriseInfo(enterpriseInfo, "", "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("提交企业认证信息失败: %s", err.Error())
|
||||
}
|
||||
s.logger.Info("企业认证成功", zap.Any("identity", identity))
|
||||
if err != nil {
|
||||
return fmt.Errorf("生成企业认证链接失败: %w", err)
|
||||
}
|
||||
|
||||
// 完成企业认证流程
|
||||
err = s.completeEnterpriseVerification(txCtx, cert, cmd.UserID, cmd.CompanyName, cmd.LegalPersonName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
respMeta = map[string]interface{}{
|
||||
"enterprise_info": enterpriseInfo,
|
||||
"next_action": "企业已认证,可进行后续操作",
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
s.logger.Error("e签宝查询企业认证信息失败或未进行企业认证", zap.Error(err))
|
||||
}
|
||||
authURL, err := s.esignClient.GenerateEnterpriseAuth(&esign.EnterpriseAuthRequest{
|
||||
CompanyName: enterpriseInfo.CompanyName,
|
||||
UnifiedSocialCode: enterpriseInfo.UnifiedSocialCode,
|
||||
LegalPersonName: enterpriseInfo.LegalPersonName,
|
||||
LegalPersonID: enterpriseInfo.LegalPersonID,
|
||||
TransactorName: enterpriseInfo.LegalPersonName,
|
||||
TransactorMobile: enterpriseInfo.LegalPersonPhone,
|
||||
TransactorID: enterpriseInfo.LegalPersonID,
|
||||
})
|
||||
if err != nil {
|
||||
s.logger.Error("生成企业认证链接失败", zap.Error(err))
|
||||
return fmt.Errorf("生成企业认证链接失败: %s", err.Error())
|
||||
}
|
||||
err = cert.SubmitEnterpriseInfo(enterpriseInfo, authURL.AuthShortURL, authURL.AuthFlowID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("提交企业认证信息失败: %s", err.Error())
|
||||
}
|
||||
respMeta = map[string]interface{}{
|
||||
"enterprise_info": enterpriseInfo,
|
||||
"authUrl": authURL.AuthURL,
|
||||
"next_action": "请完成企业认证",
|
||||
}
|
||||
err = cert.SubmitEnterpriseInfo(enterpriseInfo, authURL.AuthShortURL, authURL.AuthFlowID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("提交企业信息失败: %s", err.Error())
|
||||
}
|
||||
|
||||
err = s.aggregateService.SaveCertification(txCtx, cert)
|
||||
if err != nil {
|
||||
return fmt.Errorf("保存认证信息失败: %s", err.Error())
|
||||
}
|
||||
// 5. 转换为响应DTO
|
||||
response = s.convertToResponse(cert)
|
||||
|
||||
// 6. 添加工作流结果信息
|
||||
// 5. 提交记录与认证状态在同一事务内保存
|
||||
if saveErr := s.enterpriseInfoSubmitRecordService.Save(txCtx, record); saveErr != nil {
|
||||
return fmt.Errorf("保存企业信息提交记录失败: %s", saveErr.Error())
|
||||
}
|
||||
|
||||
respMeta := map[string]interface{}{
|
||||
"enterprise_info": enterpriseInfo,
|
||||
"next_action": "请完成企业认证",
|
||||
}
|
||||
// 6. 转换为响应 DTO
|
||||
response = s.convertToResponse(cert)
|
||||
if respMeta != nil {
|
||||
response.Metadata = respMeta
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
@@ -279,6 +313,28 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// 审核状态检查(步骤二)
|
||||
// 规则:企业信息提交成功后进入待审核;审核通过后才允许进行企业认证确认(ConfirmAuth)。
|
||||
// 当前暂时跳过人工审核(待审核状态视为通过);启用审核时恢复对 StatusInfoPendingReview 返回错误。
|
||||
func (s *CertificationApplicationServiceImpl) checkAuditStatus(ctx context.Context, cert *entities.Certification) error {
|
||||
switch cert.Status {
|
||||
case enums.StatusInfoSubmitted,
|
||||
enums.StatusEnterpriseVerified,
|
||||
enums.StatusContractApplied,
|
||||
enums.StatusContractSigned,
|
||||
enums.StatusCompleted:
|
||||
return nil
|
||||
case enums.StatusInfoPendingReview:
|
||||
// 暂时跳过人工审核:待审核状态视为通过,后续启用审核时还原为 return fmt.Errorf("企业信息已提交,正在审核中")
|
||||
s.logger.Info("跳过人工审核状态检查", zap.String("user_id", cert.UserID))
|
||||
return nil
|
||||
case enums.StatusInfoRejected:
|
||||
return fmt.Errorf("企业信息审核未通过")
|
||||
default:
|
||||
return fmt.Errorf("认证状态不正确,当前状态: %s", enums.GetStatusName(cert.Status))
|
||||
}
|
||||
}
|
||||
|
||||
// ConfirmAuth 确认认证状态
|
||||
func (s *CertificationApplicationServiceImpl) ConfirmAuth(
|
||||
ctx context.Context,
|
||||
@@ -290,9 +346,9 @@ func (s *CertificationApplicationServiceImpl) ConfirmAuth(
|
||||
return nil, fmt.Errorf("加载认证信息失败: %s", err.Error())
|
||||
}
|
||||
|
||||
// 企业认证
|
||||
if cert.Status != enums.StatusInfoSubmitted {
|
||||
return nil, fmt.Errorf("认证状态不正确,当前状态: %s", enums.GetStatusName(cert.Status))
|
||||
// 步骤二:审核状态检查(审核通过后才能进入企业认证确认)
|
||||
if err := s.checkAuditStatus(ctx, cert); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
record, err := s.enterpriseInfoSubmitRecordRepo.FindLatestByUserID(ctx, cert.UserID)
|
||||
if err != nil {
|
||||
@@ -694,6 +750,258 @@ func (s *CertificationApplicationServiceImpl) AdminCompleteCertificationWithoutC
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// AdminListSubmitRecords 管理端分页查询企业信息提交记录
|
||||
func (s *CertificationApplicationServiceImpl) AdminListSubmitRecords(
|
||||
ctx context.Context,
|
||||
query *queries.AdminListSubmitRecordsQuery,
|
||||
) (*responses.AdminSubmitRecordsListResponse, error) {
|
||||
if query.PageSize <= 0 {
|
||||
query.PageSize = 10
|
||||
}
|
||||
if query.Page <= 0 {
|
||||
query.Page = 1
|
||||
}
|
||||
filter := repositories.ListSubmitRecordsFilter{
|
||||
Page: query.Page,
|
||||
PageSize: query.PageSize,
|
||||
CertificationStatus: query.CertificationStatus,
|
||||
CompanyName: query.CompanyName,
|
||||
LegalPersonPhone: query.LegalPersonPhone,
|
||||
LegalPersonName: query.LegalPersonName,
|
||||
}
|
||||
result, err := s.enterpriseInfoSubmitRecordRepo.List(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询提交记录失败: %w", err)
|
||||
}
|
||||
items := make([]*responses.AdminSubmitRecordItem, 0, len(result.Records))
|
||||
for _, r := range result.Records {
|
||||
certStatus := ""
|
||||
if cert, err := s.aggregateService.LoadCertificationByUserID(ctx, r.UserID); err == nil && cert != nil {
|
||||
certStatus = string(cert.Status)
|
||||
}
|
||||
items = append(items, &responses.AdminSubmitRecordItem{
|
||||
ID: r.ID,
|
||||
UserID: r.UserID,
|
||||
CompanyName: r.CompanyName,
|
||||
UnifiedSocialCode: r.UnifiedSocialCode,
|
||||
LegalPersonName: r.LegalPersonName,
|
||||
SubmitAt: r.SubmitAt,
|
||||
Status: r.Status,
|
||||
CertificationStatus: certStatus,
|
||||
})
|
||||
}
|
||||
totalPages := int((result.Total + int64(query.PageSize) - 1) / int64(query.PageSize))
|
||||
if totalPages == 0 {
|
||||
totalPages = 1
|
||||
}
|
||||
return &responses.AdminSubmitRecordsListResponse{
|
||||
Items: items,
|
||||
Total: result.Total,
|
||||
Page: query.Page,
|
||||
PageSize: query.PageSize,
|
||||
TotalPages: totalPages,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AdminGetSubmitRecordByID 管理端获取单条提交记录详情
|
||||
func (s *CertificationApplicationServiceImpl) AdminGetSubmitRecordByID(ctx context.Context, recordID string) (*responses.AdminSubmitRecordDetail, error) {
|
||||
record, err := s.enterpriseInfoSubmitRecordRepo.FindByID(ctx, recordID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取提交记录失败: %w", err)
|
||||
}
|
||||
certStatus := ""
|
||||
if cert, loadErr := s.aggregateService.LoadCertificationByUserID(ctx, record.UserID); loadErr == nil && cert != nil {
|
||||
certStatus = string(cert.Status)
|
||||
}
|
||||
return &responses.AdminSubmitRecordDetail{
|
||||
ID: record.ID,
|
||||
UserID: record.UserID,
|
||||
CompanyName: record.CompanyName,
|
||||
UnifiedSocialCode: record.UnifiedSocialCode,
|
||||
LegalPersonName: record.LegalPersonName,
|
||||
LegalPersonID: record.LegalPersonID,
|
||||
LegalPersonPhone: record.LegalPersonPhone,
|
||||
EnterpriseAddress: record.EnterpriseAddress,
|
||||
AuthorizedRepName: record.AuthorizedRepName,
|
||||
AuthorizedRepID: record.AuthorizedRepID,
|
||||
AuthorizedRepPhone: record.AuthorizedRepPhone,
|
||||
AuthorizedRepIDImageURLs: record.AuthorizedRepIDImageURLs,
|
||||
BusinessLicenseImageURL: record.BusinessLicenseImageURL,
|
||||
OfficePlaceImageURLs: record.OfficePlaceImageURLs,
|
||||
APIUsage: record.APIUsage,
|
||||
ScenarioAttachmentURLs: record.ScenarioAttachmentURLs,
|
||||
Status: record.Status,
|
||||
SubmitAt: record.SubmitAt,
|
||||
VerifiedAt: record.VerifiedAt,
|
||||
FailedAt: record.FailedAt,
|
||||
FailureReason: record.FailureReason,
|
||||
CertificationStatus: certStatus,
|
||||
CreatedAt: record.CreatedAt,
|
||||
UpdatedAt: record.UpdatedAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AdminApproveSubmitRecord 管理端审核通过
|
||||
func (s *CertificationApplicationServiceImpl) AdminApproveSubmitRecord(ctx context.Context, recordID, adminID, remark string) error {
|
||||
record, err := s.enterpriseInfoSubmitRecordRepo.FindByID(ctx, recordID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取提交记录失败: %w", err)
|
||||
}
|
||||
cert, err := s.aggregateService.LoadCertificationByUserID(ctx, record.UserID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("加载认证信息失败: %w", err)
|
||||
}
|
||||
|
||||
// 幂等:认证已进入「已提交企业信息」或更后续状态,说明已通过审核,无需重复操作
|
||||
switch cert.Status {
|
||||
case enums.StatusInfoSubmitted,
|
||||
enums.StatusEnterpriseVerified,
|
||||
enums.StatusContractApplied,
|
||||
enums.StatusContractSigned,
|
||||
enums.StatusCompleted,
|
||||
enums.StatusContractRejected,
|
||||
enums.StatusContractExpired:
|
||||
return nil
|
||||
}
|
||||
|
||||
if cert.Status != enums.StatusInfoPendingReview {
|
||||
return fmt.Errorf("认证状态不是待审核,当前: %s", enums.GetStatusName(cert.Status))
|
||||
}
|
||||
enterpriseInfo := &certification_value_objects.EnterpriseInfo{
|
||||
CompanyName: record.CompanyName,
|
||||
UnifiedSocialCode: record.UnifiedSocialCode,
|
||||
LegalPersonName: record.LegalPersonName,
|
||||
LegalPersonID: record.LegalPersonID,
|
||||
LegalPersonPhone: record.LegalPersonPhone,
|
||||
EnterpriseAddress: record.EnterpriseAddress,
|
||||
}
|
||||
authURL, err := s.esignClient.GenerateEnterpriseAuth(&esign.EnterpriseAuthRequest{
|
||||
CompanyName: enterpriseInfo.CompanyName,
|
||||
UnifiedSocialCode: enterpriseInfo.UnifiedSocialCode,
|
||||
LegalPersonName: enterpriseInfo.LegalPersonName,
|
||||
LegalPersonID: enterpriseInfo.LegalPersonID,
|
||||
TransactorName: enterpriseInfo.LegalPersonName,
|
||||
TransactorMobile: enterpriseInfo.LegalPersonPhone,
|
||||
TransactorID: enterpriseInfo.LegalPersonID,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("生成企业认证链接失败: %w", err)
|
||||
}
|
||||
if err := cert.ApproveEnterpriseInfoReview(authURL.AuthShortURL, authURL.AuthFlowID, adminID); err != nil {
|
||||
return fmt.Errorf("更新认证状态失败: %w", err)
|
||||
}
|
||||
if err := s.aggregateService.SaveCertification(ctx, cert); err != nil {
|
||||
return fmt.Errorf("保存认证信息失败: %w", err)
|
||||
}
|
||||
s.logger.Info("管理员审核通过企业信息", zap.String("record_id", recordID), zap.String("admin_id", adminID))
|
||||
return nil
|
||||
}
|
||||
|
||||
// AdminRejectSubmitRecord 管理端审核拒绝
|
||||
func (s *CertificationApplicationServiceImpl) AdminRejectSubmitRecord(ctx context.Context, recordID, adminID, remark string) error {
|
||||
if remark == "" {
|
||||
return fmt.Errorf("拒绝时必须填写审核备注")
|
||||
}
|
||||
record, err := s.enterpriseInfoSubmitRecordRepo.FindByID(ctx, recordID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取提交记录失败: %w", err)
|
||||
}
|
||||
cert, err := s.aggregateService.LoadCertificationByUserID(ctx, record.UserID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("加载认证信息失败: %w", err)
|
||||
}
|
||||
|
||||
// 幂等:认证已处于拒绝或后续状态,无需重复拒绝
|
||||
switch cert.Status {
|
||||
case enums.StatusInfoRejected,
|
||||
enums.StatusEnterpriseVerified,
|
||||
enums.StatusContractApplied,
|
||||
enums.StatusContractSigned,
|
||||
enums.StatusCompleted,
|
||||
enums.StatusContractRejected,
|
||||
enums.StatusContractExpired:
|
||||
return nil
|
||||
}
|
||||
if cert.Status != enums.StatusInfoPendingReview {
|
||||
return fmt.Errorf("认证状态不是待审核,当前: %s", enums.GetStatusName(cert.Status))
|
||||
}
|
||||
if err := cert.RejectEnterpriseInfoReview(adminID, remark); err != nil {
|
||||
return fmt.Errorf("更新认证状态失败: %w", err)
|
||||
}
|
||||
if err := s.aggregateService.SaveCertification(ctx, cert); err != nil {
|
||||
return fmt.Errorf("保存认证信息失败: %w", err)
|
||||
}
|
||||
s.logger.Info("管理员审核拒绝企业信息", zap.String("record_id", recordID), zap.String("admin_id", adminID))
|
||||
return nil
|
||||
}
|
||||
|
||||
// AdminTransitionCertificationStatus 管理端按用户变更认证状态(以状态机为准)
|
||||
func (s *CertificationApplicationServiceImpl) AdminTransitionCertificationStatus(ctx context.Context, cmd *commands.AdminTransitionCertificationStatusCommand) error {
|
||||
cert, err := s.aggregateService.LoadCertificationByUserID(ctx, cmd.UserID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("加载认证信息失败: %w", err)
|
||||
}
|
||||
record, err := s.enterpriseInfoSubmitRecordRepo.FindLatestByUserID(ctx, cmd.UserID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("查找企业信息提交记录失败: %w", err)
|
||||
}
|
||||
if record == nil {
|
||||
return fmt.Errorf("未找到该用户的企业信息提交记录")
|
||||
}
|
||||
switch cmd.TargetStatus {
|
||||
case string(enums.StatusInfoSubmitted):
|
||||
// 审核通过:与 AdminApproveSubmitRecord 一致,推状态并生成企业认证链接
|
||||
switch cert.Status {
|
||||
case enums.StatusInfoSubmitted, enums.StatusEnterpriseVerified, enums.StatusContractApplied,
|
||||
enums.StatusContractSigned, enums.StatusCompleted, enums.StatusContractRejected, enums.StatusContractExpired:
|
||||
return nil
|
||||
}
|
||||
if cert.Status != enums.StatusInfoPendingReview {
|
||||
return fmt.Errorf("认证状态不是待审核,当前: %s", enums.GetStatusName(cert.Status))
|
||||
}
|
||||
enterpriseInfo := &certification_value_objects.EnterpriseInfo{
|
||||
CompanyName: record.CompanyName, UnifiedSocialCode: record.UnifiedSocialCode,
|
||||
LegalPersonName: record.LegalPersonName, LegalPersonID: record.LegalPersonID,
|
||||
LegalPersonPhone: record.LegalPersonPhone, EnterpriseAddress: record.EnterpriseAddress,
|
||||
}
|
||||
authURL, err := s.esignClient.GenerateEnterpriseAuth(&esign.EnterpriseAuthRequest{
|
||||
CompanyName: enterpriseInfo.CompanyName, UnifiedSocialCode: enterpriseInfo.UnifiedSocialCode,
|
||||
LegalPersonName: enterpriseInfo.LegalPersonName, LegalPersonID: enterpriseInfo.LegalPersonID,
|
||||
TransactorName: enterpriseInfo.LegalPersonName, TransactorMobile: enterpriseInfo.LegalPersonPhone, TransactorID: enterpriseInfo.LegalPersonID,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("生成企业认证链接失败: %w", err)
|
||||
}
|
||||
if err := cert.ApproveEnterpriseInfoReview(authURL.AuthShortURL, authURL.AuthFlowID, cmd.AdminID); err != nil {
|
||||
return fmt.Errorf("更新认证状态失败: %w", err)
|
||||
}
|
||||
if err := s.aggregateService.SaveCertification(ctx, cert); err != nil {
|
||||
return fmt.Errorf("保存认证信息失败: %w", err)
|
||||
}
|
||||
s.logger.Info("管理端变更认证状态为通过", zap.String("user_id", cmd.UserID), zap.String("admin_id", cmd.AdminID))
|
||||
return nil
|
||||
case string(enums.StatusInfoRejected):
|
||||
// 审核拒绝
|
||||
if cert.Status == enums.StatusInfoRejected || cert.Status == enums.StatusEnterpriseVerified ||
|
||||
cert.Status == enums.StatusContractApplied || cert.Status == enums.StatusContractSigned || cert.Status == enums.StatusCompleted {
|
||||
return nil
|
||||
}
|
||||
if cert.Status != enums.StatusInfoPendingReview {
|
||||
return fmt.Errorf("认证状态不是待审核,当前: %s", enums.GetStatusName(cert.Status))
|
||||
}
|
||||
if err := cert.RejectEnterpriseInfoReview(cmd.AdminID, cmd.Remark); err != nil {
|
||||
return fmt.Errorf("更新认证状态失败: %w", err)
|
||||
}
|
||||
if err := s.aggregateService.SaveCertification(ctx, cert); err != nil {
|
||||
return fmt.Errorf("保存认证信息失败: %w", err)
|
||||
}
|
||||
s.logger.Info("管理端变更认证状态为拒绝", zap.String("user_id", cmd.UserID), zap.String("admin_id", cmd.AdminID))
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("不支持的目标状态: %s", cmd.TargetStatus)
|
||||
}
|
||||
}
|
||||
|
||||
// ================ 辅助方法 ================
|
||||
|
||||
// convertToResponse 转换实体为响应DTO
|
||||
|
||||
@@ -94,6 +94,14 @@ type ForceTransitionStatusCommand struct {
|
||||
Force bool `json:"force,omitempty"` // 是否强制执行,跳过业务规则验证
|
||||
}
|
||||
|
||||
// AdminTransitionCertificationStatusCommand 管理端变更认证状态(以状态机为准,用于审核通过/拒绝等)
|
||||
type AdminTransitionCertificationStatusCommand struct {
|
||||
AdminID string `json:"-"`
|
||||
UserID string `json:"user_id" validate:"required"`
|
||||
TargetStatus string `json:"target_status" validate:"required,oneof=info_submitted info_rejected"` // 审核通过 -> info_submitted;审核拒绝 -> info_rejected
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
// SubmitEnterpriseInfoCommand 提交企业信息命令
|
||||
type SubmitEnterpriseInfoCommand struct {
|
||||
UserID string `json:"-" comment:"用户唯一标识,从JWT token获取,不在JSON中暴露"`
|
||||
@@ -104,4 +112,19 @@ type SubmitEnterpriseInfoCommand struct {
|
||||
LegalPersonPhone string `json:"legal_person_phone" binding:"required,phone" comment:"法定代表人手机号,11位,如:13800138000"`
|
||||
EnterpriseAddress string `json:"enterprise_address" binding:"required,enterprise_address" comment:"企业地址,如:北京市海淀区"`
|
||||
VerificationCode string `json:"verification_code" binding:"required,len=6" comment:"验证码"`
|
||||
|
||||
// 营业执照图片 URL(单张)
|
||||
BusinessLicenseImageURL string `json:"business_license_image_url" binding:"omitempty,url" comment:"营业执照图片URL"`
|
||||
// 办公场地图片 URL 列表(前端传 string 数组)
|
||||
OfficePlaceImageURLs []string `json:"office_place_image_urls" binding:"omitempty,dive,url" comment:"办公场地图片URL列表"`
|
||||
|
||||
// 授权代表信息(与前端 authorized_rep_* 及表字段一致)
|
||||
AuthorizedRepName string `json:"authorized_rep_name" binding:"omitempty,min=2,max=20" comment:"授权代表姓名"`
|
||||
AuthorizedRepID string `json:"authorized_rep_id" binding:"omitempty,id_card" comment:"授权代表身份证号"`
|
||||
AuthorizedRepPhone string `json:"authorized_rep_phone" binding:"omitempty,phone" comment:"授权代表手机号"`
|
||||
AuthorizedRepIDImageURLs []string `json:"authorized_rep_id_image_urls" binding:"omitempty,dive,url" comment:"授权代表身份证正反面图片URL"`
|
||||
|
||||
// 应用场景
|
||||
APIUsage string `json:"api_usage" binding:"omitempty,min=5,max=500" comment:"接口用途及业务场景说明"`
|
||||
ScenarioAttachmentURLs []string `json:"scenario_attachment_urls" binding:"omitempty,dive,url" comment:"场景附件图片URL列表"`
|
||||
}
|
||||
|
||||
@@ -192,3 +192,13 @@ func (q *GetSystemMonitoringQuery) ShouldIncludeMetric(metric string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AdminListSubmitRecordsQuery 管理端企业信息提交记录列表查询(以状态机 certification_status 为准,不做审核状态筛选)
|
||||
type AdminListSubmitRecordsQuery struct {
|
||||
Page int `json:"page" form:"page"`
|
||||
PageSize int `json:"page_size" form:"page_size"`
|
||||
CertificationStatus string `json:"certification_status" form:"certification_status"` // 按认证状态筛选,如 info_pending_review / info_submitted / info_rejected,空为全部
|
||||
CompanyName string `json:"company_name" form:"company_name"` // 企业名称(模糊搜索)
|
||||
LegalPersonPhone string `json:"legal_person_phone" form:"legal_person_phone"` // 法人手机号
|
||||
LegalPersonName string `json:"legal_person_name" form:"legal_person_name"` // 法人姓名(模糊搜索)
|
||||
}
|
||||
|
||||
@@ -53,13 +53,13 @@ type CertificationResponse struct {
|
||||
// ConfirmAuthResponse 确认认证状态响应
|
||||
type ConfirmAuthResponse struct {
|
||||
Status enums.CertificationStatus `json:"status"`
|
||||
Reason string `json:"reason"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// ConfirmSignResponse 确认签署状态响应
|
||||
type ConfirmSignResponse struct {
|
||||
Status enums.CertificationStatus `json:"status"`
|
||||
Reason string `json:"reason"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// CertificationListResponse 认证列表响应
|
||||
@@ -81,7 +81,6 @@ type ContractSignUrlResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
|
||||
// SystemMonitoringResponse 系统监控响应
|
||||
type SystemMonitoringResponse struct {
|
||||
TimeRange string `json:"time_range"`
|
||||
@@ -111,6 +110,55 @@ type SystemHealthStatus struct {
|
||||
Details map[string]interface{} `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
// AdminSubmitRecordItem 管理端提交记录列表项
|
||||
type AdminSubmitRecordItem struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
CompanyName string `json:"company_name"`
|
||||
UnifiedSocialCode string `json:"unified_social_code"`
|
||||
LegalPersonName string `json:"legal_person_name"`
|
||||
SubmitAt time.Time `json:"submit_at"`
|
||||
Status string `json:"status"`
|
||||
CertificationStatus string `json:"certification_status,omitempty"` // 以状态机为准:info_pending_review/info_submitted/info_rejected 等
|
||||
}
|
||||
|
||||
// AdminSubmitRecordDetail 管理端提交记录详情(含完整信息与图片 URL)
|
||||
type AdminSubmitRecordDetail struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
CompanyName string `json:"company_name"`
|
||||
UnifiedSocialCode string `json:"unified_social_code"`
|
||||
LegalPersonName string `json:"legal_person_name"`
|
||||
LegalPersonID string `json:"legal_person_id"`
|
||||
LegalPersonPhone string `json:"legal_person_phone"`
|
||||
EnterpriseAddress string `json:"enterprise_address"`
|
||||
AuthorizedRepName string `json:"authorized_rep_name"`
|
||||
AuthorizedRepID string `json:"authorized_rep_id"`
|
||||
AuthorizedRepPhone string `json:"authorized_rep_phone"`
|
||||
AuthorizedRepIDImageURLs string `json:"authorized_rep_id_image_urls"` // JSON 字符串或解析后数组
|
||||
BusinessLicenseImageURL string `json:"business_license_image_url"`
|
||||
OfficePlaceImageURLs string `json:"office_place_image_urls"` // JSON 数组字符串
|
||||
APIUsage string `json:"api_usage"`
|
||||
ScenarioAttachmentURLs string `json:"scenario_attachment_urls"`
|
||||
Status string `json:"status"`
|
||||
SubmitAt time.Time `json:"submit_at"`
|
||||
VerifiedAt *time.Time `json:"verified_at,omitempty"`
|
||||
FailedAt *time.Time `json:"failed_at,omitempty"`
|
||||
FailureReason string `json:"failure_reason,omitempty"`
|
||||
CertificationStatus string `json:"certification_status,omitempty"` // 以状态机为准
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// AdminSubmitRecordsListResponse 管理端提交记录列表响应
|
||||
type AdminSubmitRecordsListResponse struct {
|
||||
Items []*AdminSubmitRecordItem `json:"items"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
TotalPages int `json:"total_pages"`
|
||||
}
|
||||
|
||||
// ================ 响应构建辅助方法 ================
|
||||
|
||||
// NewCertificationListResponse 创建认证列表响应
|
||||
@@ -146,7 +194,6 @@ func NewContractSignUrlResponse(certificationID, signURL, contractURL, nextActio
|
||||
return response
|
||||
}
|
||||
|
||||
|
||||
// NewSystemAlert 创建系统警告
|
||||
func NewSystemAlert(level, alertType, message, metric string, value, threshold interface{}) *SystemAlert {
|
||||
return &SystemAlert{
|
||||
@@ -161,7 +208,6 @@ func NewSystemAlert(level, alertType, message, metric string, value, threshold i
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// IsHealthy 检查系统是否健康
|
||||
func (r *SystemMonitoringResponse) IsHealthy() bool {
|
||||
return r.SystemHealth.Overall == "healthy"
|
||||
|
||||
@@ -38,10 +38,10 @@ type Config struct {
|
||||
TianYanCha TianYanChaConfig `mapstructure:"tianyancha"`
|
||||
Alicloud AlicloudConfig `mapstructure:"alicloud"`
|
||||
Xingwei XingweiConfig `mapstructure:"xingwei"`
|
||||
Jiguang JiguangConfig `mapstructure:"jiguang"`
|
||||
Shumai ShumaiConfig `mapstructure:"shumai"`
|
||||
Shujubao ShujubaoConfig `mapstructure:"shujubao"`
|
||||
PDFGen PDFGenConfig `mapstructure:"pdfgen"`
|
||||
Jiguang JiguangConfig `mapstructure:"jiguang"`
|
||||
Shumai ShumaiConfig `mapstructure:"shumai"`
|
||||
Shujubao ShujubaoConfig `mapstructure:"shujubao"`
|
||||
PDFGen PDFGenConfig `mapstructure:"pdfgen"`
|
||||
}
|
||||
|
||||
// ServerConfig HTTP服务器配置
|
||||
@@ -217,10 +217,10 @@ type SMSConfig struct {
|
||||
SignatureEnabled bool `mapstructure:"signature_enabled"` // 是否启用签名验证
|
||||
SignatureSecret string `mapstructure:"signature_secret"` // 签名密钥
|
||||
// 滑块验证码配置
|
||||
CaptchaEnabled bool `mapstructure:"captcha_enabled"` // 是否启用滑块验证码
|
||||
CaptchaSecret string `mapstructure:"captcha_secret"` // 阿里云验证码密钥
|
||||
CaptchaEndpoint string `mapstructure:"captcha_endpoint"` // 阿里云验证码服务Endpoint
|
||||
SceneID string `mapstructure:"scene_id"` // 阿里云验证码场景ID
|
||||
CaptchaEnabled bool `mapstructure:"captcha_enabled"` // 是否启用滑块验证码
|
||||
CaptchaSecret string `mapstructure:"captcha_secret"` // 阿里云验证码密钥
|
||||
CaptchaEndpoint string `mapstructure:"captcha_endpoint"` // 阿里云验证码服务Endpoint
|
||||
SceneID string `mapstructure:"scene_id"` // 阿里云验证码场景ID
|
||||
}
|
||||
|
||||
// SMSRateLimit 短信限流配置
|
||||
@@ -332,10 +332,10 @@ type SignConfig struct {
|
||||
// WalletConfig 钱包配置
|
||||
type WalletConfig struct {
|
||||
DefaultCreditLimit float64 `mapstructure:"default_credit_limit"`
|
||||
MinAmount string `mapstructure:"min_amount"` // 最低充值金额
|
||||
MaxAmount string `mapstructure:"max_amount"` // 最高充值金额
|
||||
MinAmount string `mapstructure:"min_amount"` // 最低充值金额
|
||||
MaxAmount string `mapstructure:"max_amount"` // 最高充值金额
|
||||
RechargeBonusEnabled bool `mapstructure:"recharge_bonus_enabled"` // 是否启用充值赠送,关闭后仅展示商务洽谈提示
|
||||
ApiStoreRechargeTip string `mapstructure:"api_store_recharge_tip"` // API 商店充值提示文案(大额/批量需求联系商务)
|
||||
ApiStoreRechargeTip string `mapstructure:"api_store_recharge_tip"` // API 商店充值提示文案(大额/批量需求联系商务)
|
||||
AliPayRechargeBonus []AliPayRechargeBonusRule `mapstructure:"alipay_recharge_bonus"`
|
||||
BalanceAlert BalanceAlertConfig `mapstructure:"balance_alert"`
|
||||
}
|
||||
@@ -578,10 +578,10 @@ type ShumaiConfig struct {
|
||||
|
||||
// ShumaiLoggingConfig 数脉日志配置
|
||||
type ShumaiLoggingConfig struct {
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
LogDir string `mapstructure:"log_dir"`
|
||||
UseDaily bool `mapstructure:"use_daily"`
|
||||
EnableLevelSeparation bool `mapstructure:"enable_level_separation"`
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
LogDir string `mapstructure:"log_dir"`
|
||||
UseDaily bool `mapstructure:"use_daily"`
|
||||
EnableLevelSeparation bool `mapstructure:"enable_level_separation"`
|
||||
LevelConfigs map[string]ShumaiLevelFileConfig `mapstructure:"level_configs"`
|
||||
}
|
||||
|
||||
@@ -605,10 +605,10 @@ type ShujubaoConfig struct {
|
||||
|
||||
// ShujubaoLoggingConfig 数据宝日志配置
|
||||
type ShujubaoLoggingConfig struct {
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
LogDir string `mapstructure:"log_dir"`
|
||||
UseDaily bool `mapstructure:"use_daily"`
|
||||
EnableLevelSeparation bool `mapstructure:"enable_level_separation"`
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
LogDir string `mapstructure:"log_dir"`
|
||||
UseDaily bool `mapstructure:"use_daily"`
|
||||
EnableLevelSeparation bool `mapstructure:"enable_level_separation"`
|
||||
LevelConfigs map[string]ShujubaoLevelFileConfig `mapstructure:"level_configs"`
|
||||
}
|
||||
|
||||
@@ -622,11 +622,11 @@ type ShujubaoLevelFileConfig struct {
|
||||
|
||||
// PDFGenConfig PDF生成服务配置
|
||||
type PDFGenConfig struct {
|
||||
DevelopmentURL string `mapstructure:"development_url"` // 开发环境服务地址
|
||||
ProductionURL string `mapstructure:"production_url"` // 生产环境服务地址
|
||||
APIPath string `mapstructure:"api_path"` // API路径
|
||||
Timeout time.Duration `mapstructure:"timeout"` // 请求超时时间
|
||||
Cache PDFGenCacheConfig `mapstructure:"cache"` // 缓存配置
|
||||
DevelopmentURL string `mapstructure:"development_url"` // 开发环境服务地址
|
||||
ProductionURL string `mapstructure:"production_url"` // 生产环境服务地址
|
||||
APIPath string `mapstructure:"api_path"` // API路径
|
||||
Timeout time.Duration `mapstructure:"timeout"` // 请求超时时间
|
||||
Cache PDFGenCacheConfig `mapstructure:"cache"` // 缓存配置
|
||||
}
|
||||
|
||||
// PDFGenCacheConfig PDF生成缓存配置
|
||||
|
||||
@@ -616,6 +616,15 @@ type QYGLJ0Q1Req struct {
|
||||
EntName string `json:"ent_name" validate:"omitempty,min=1,validEnterpriseName"`
|
||||
EntCode string `json:"ent_code" validate:"omitempty,validUSCI"`
|
||||
}
|
||||
|
||||
type FLXGDJG3Req struct {
|
||||
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||
}
|
||||
type QYGLDJ12Req struct {
|
||||
EntName string `json:"ent_name" validate:"omitempty,min=1,validEnterpriseName"`
|
||||
EntCode string `json:"ent_code" validate:"omitempty,validUSCI"`
|
||||
EntRegNo string `json:"ent_reg_no" validate:"omitempty"`
|
||||
}
|
||||
type YYSY6D9AReq struct {
|
||||
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||
|
||||
@@ -129,16 +129,16 @@ func NewApiRequestServiceWithRepos(
|
||||
registerAllProcessors(combService)
|
||||
|
||||
return &ApiRequestService{
|
||||
westDexService: westDexService,
|
||||
muziService: muziService,
|
||||
yushanService: yushanService,
|
||||
tianYanChaService: tianYanChaService,
|
||||
alicloudService: alicloudService,
|
||||
validator: validator,
|
||||
processorDeps: processorDeps,
|
||||
combService: combService,
|
||||
config: cfg,
|
||||
reportRepo: reportRepo,
|
||||
westDexService: westDexService,
|
||||
muziService: muziService,
|
||||
yushanService: yushanService,
|
||||
tianYanChaService: tianYanChaService,
|
||||
alicloudService: alicloudService,
|
||||
validator: validator,
|
||||
processorDeps: processorDeps,
|
||||
combService: combService,
|
||||
config: cfg,
|
||||
reportRepo: reportRepo,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,6 +171,7 @@ func registerAllProcessors(combService *comb.CombService) {
|
||||
"FLXG7E8F": flxg.ProcessFLXG7E8FRequest,
|
||||
"FLXG3A9B": flxg.ProcessFLXG3A9BRequest,
|
||||
"FLXGK5D2": flxg.ProcessFLXGK5D2Request,
|
||||
"FLXGDJG3": flxg.ProcessFLXGDJG3Request, //董监高司法综合信息核验
|
||||
// JRZQ系列处理器
|
||||
"JRZQ8203": jrzq.ProcessJRZQ8203Request,
|
||||
"JRZQ0A03": jrzq.ProcessJRZQ0A03Request,
|
||||
@@ -236,9 +237,11 @@ func registerAllProcessors(combService *comb.CombService) {
|
||||
"QYGLJ1U9": qygl.ProcessQYGLJ1U9Request, //企业全景报告(聚合 QYGLUY3S/QYGLJ0Q1/QYGL5S1I)
|
||||
"QYGLJ0Q1": qygl.ProcessQYGLJ0Q1Request, //企业股权结构全景查询
|
||||
"QYGLUY3S": qygl.ProcessQYGLUY3SRequest, //企业经营状态全景查询
|
||||
"YYSY35TA": yysy.ProcessYYSY35TARequest, //运营商归属地数卖
|
||||
"QYGLDJ12": qygl.ProcessQYGLDJ12Request, //企业年报信息核验
|
||||
"QYGL8848": qygl.ProcessQYGL8848Request, //企业税收违法核查
|
||||
|
||||
// YYSY系列处理器
|
||||
"YYSY35TA": yysy.ProcessYYSY35TARequest, //运营商归属地数卖
|
||||
"YYSYD50F": yysy.ProcessYYSYD50FRequest,
|
||||
"YYSY09CD": yysy.ProcessYYSY09CDRequest,
|
||||
"YYSY4B21": yysy.ProcessYYSY4B21Request,
|
||||
|
||||
@@ -263,6 +263,9 @@ func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string
|
||||
"JRZQOCRE": &dto.JRZQOCREReq{}, //银行卡OCR数卖
|
||||
"JRZQOCRY": &dto.JRZQOCRYReq{}, //银行卡OCR数据宝
|
||||
"YYSY35TA": &dto.YYSY35TAReq{}, //运营商归属地数卖
|
||||
"QYGLDJ12": &dto.QYGLDJ12Req{}, //企业年报信息核验
|
||||
"FLXGDJG3": &dto.FLXGDJG3Req{}, //董监高司法综合信息核验
|
||||
"QYGL8848": &dto.QYGLDJ12Req{}, //企业税收违法核查
|
||||
}
|
||||
|
||||
// 优先返回已配置的DTO
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
// ProcessFLXG0V4BRequest FLXG0V4B API处理方法
|
||||
// ProcessFLXG0V4BRequest FLXG0V4B API处理方法(身份证排空入口,身份证身份证身份证身份证身份证)
|
||||
func ProcessFLXG0V4BRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.FLXG0V4BReq
|
||||
if err := json.Unmarshal(params, ¶msDto); err != nil {
|
||||
@@ -24,7 +24,7 @@ func ProcessFLXG0V4BRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998"{
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
encryptedName, err := deps.WestDexService.Encrypt(paramsDto.Name)
|
||||
|
||||
@@ -20,7 +20,7 @@ func ProcessFLXG5A3BRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550"|| paramsDto.IDCard == "320682198910134998"{
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
|
||||
|
||||
@@ -20,7 +20,7 @@ func ProcessFLXG7E8FRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550"|| paramsDto.IDCard == "320682198910134998" {
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
// 构建请求数据,将项目规范的字段名转换为 XingweiService 需要的字段名
|
||||
|
||||
@@ -20,7 +20,7 @@ func ProcessFLXGCA3DRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550"|| paramsDto.IDCard == "320682198910134998" {
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550"|| paramsDto.IDCard == "320682198910134998"|| paramsDto.IDCard == "640102198708020925" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
encryptedName, err := deps.WestDexService.Encrypt(paramsDto.Name)
|
||||
|
||||
@@ -25,7 +25,7 @@ func ProcessFLXGDEA9Request(ctx context.Context, params []byte, deps *processors
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550"|| paramsDto.IDCard == "320682198910134998"{
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard)
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package flxg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/shujubao"
|
||||
)
|
||||
|
||||
// ProcessFLXGDJG3Request FLXGDJG3 董监高司法综合信息核验 API 处理方法(使用数据宝服务示例)
|
||||
func ProcessFLXGDJG3Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.FLXGDJG3Req
|
||||
if err := json.Unmarshal(params, ¶msDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
// 构建数据宝入参(sign 外的业务参数可按需 AES 加密后作为 bodyData)
|
||||
reqParams := map[string]interface{}{
|
||||
"key": "1cce582f0a6f3ca40de80f1bea9b9698",
|
||||
"idcard": paramsDto.IDCard,
|
||||
}
|
||||
|
||||
// 最终请求 URL = https://api.chinadatapay.com/communication + 拼接接口地址值,如 personal/197
|
||||
apiPath := "/communication/personal/10166"
|
||||
data, err := deps.ShujubaoService.CallAPI(ctx, apiPath, reqParams)
|
||||
if err != nil {
|
||||
if errors.Is(err, shujubao.ErrDatasource) {
|
||||
return nil, errors.Join(processors.ErrDatasource, err)
|
||||
}
|
||||
if errors.Is(err, shujubao.ErrQueryEmpty) {
|
||||
return nil, errors.Join(processors.ErrNotFound, err)
|
||||
}
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
// 解析响应中的 JSON 字符串(使用 qyglb4c0 中的 RecursiveParse)
|
||||
parsedResp, err := RecursiveParse(data)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
respBytes, err := json.Marshal(parsedResp)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
return respBytes, nil
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package qygl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/shujubao"
|
||||
)
|
||||
|
||||
// ProcessQYGL8848Request QYGL8848 企业税收违法核查 API 处理方法(使用数据宝服务示例)
|
||||
func ProcessQYGL8848Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.QYGLDJ12Req
|
||||
if err := json.Unmarshal(params, ¶msDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
// 企业名称(entName)、统一社会信用代码(creditCode)、企业注册号(entRegNo) 至少传其一;多填时优先用 creditCode 传参
|
||||
hasEntName := paramsDto.EntName != ""
|
||||
hasEntCode := paramsDto.EntCode != ""
|
||||
hasEntRegNo := paramsDto.EntRegNo != ""
|
||||
if !hasEntName && !hasEntCode && !hasEntRegNo { // 三个都未填才报错
|
||||
return nil, errors.Join(processors.ErrInvalidParam, errors.New("ent_name、ent_code、ent_reg_no 至少需要传其中一个"))
|
||||
}
|
||||
|
||||
// 构建数据宝入参(多填时优先取 creditCode)
|
||||
reqParams := map[string]interface{}{
|
||||
"key": "c67673dd2e92deb2d2ec91b87bb0a81c",
|
||||
}
|
||||
if hasEntCode {
|
||||
reqParams["creditCode"] = paramsDto.EntCode
|
||||
} else if hasEntName {
|
||||
reqParams["entName"] = paramsDto.EntName
|
||||
} else if hasEntRegNo {
|
||||
reqParams["regCode"] = paramsDto.EntRegNo
|
||||
}
|
||||
|
||||
// 最终请求 URL = https://api.chinadatapay.com/communication + 拼接接口地址值,如 personal/197
|
||||
apiPath := "/communication/personal/10233"
|
||||
data, err := deps.ShujubaoService.CallAPI(ctx, apiPath, reqParams)
|
||||
if err != nil {
|
||||
if errors.Is(err, shujubao.ErrDatasource) {
|
||||
return nil, errors.Join(processors.ErrDatasource, err)
|
||||
}
|
||||
if errors.Is(err, shujubao.ErrQueryEmpty) {
|
||||
return nil, errors.Join(processors.ErrNotFound, err)
|
||||
}
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
// 解析响应中的 JSON 字符串(使用 qyglb4c0 中的 RecursiveParse)
|
||||
parsedResp, err := RecursiveParse(data)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
respBytes, err := json.Marshal(parsedResp)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
return respBytes, nil
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package qygl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/shujubao"
|
||||
)
|
||||
|
||||
// ProcessQYGLDJ12Request QYGLDJ12 企业年报信息核验 API 处理方法(使用数据宝服务示例)
|
||||
func ProcessQYGLDJ12Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.QYGLDJ12Req
|
||||
if err := json.Unmarshal(params, ¶msDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
// 企业名称(entName)、统一社会信用代码(creditCode)、企业注册号(entRegNo) 至少传其一;多填时优先用 creditCode 传参
|
||||
hasEntName := paramsDto.EntName != ""
|
||||
hasEntCode := paramsDto.EntCode != ""
|
||||
hasEntRegNo := paramsDto.EntRegNo != ""
|
||||
if !hasEntName && !hasEntCode && !hasEntRegNo { // 三个都未填才报错
|
||||
return nil, errors.Join(processors.ErrInvalidParam, errors.New("ent_name、ent_code、ent_reg_no 至少需要传其中一个"))
|
||||
}
|
||||
|
||||
// 构建数据宝入参(sign 外的业务参数可按需 AES 加密后作为 bodyData)
|
||||
reqParams := map[string]interface{}{
|
||||
"key": "112813815e2cc281ad8f552deb7a3c7f",
|
||||
}
|
||||
if hasEntCode {
|
||||
reqParams["creditCode"] = paramsDto.EntCode
|
||||
} else if hasEntName {
|
||||
reqParams["entName"] = paramsDto.EntName
|
||||
} else if hasEntRegNo {
|
||||
reqParams["regCode"] = paramsDto.EntRegNo
|
||||
}
|
||||
// 最终请求 URL = https://api.chinadatapay.com/communication + 拼接接口地址值,如 personal/197
|
||||
apiPath := "/communication/personal/10192"
|
||||
data, err := deps.ShujubaoService.CallAPI(ctx, apiPath, reqParams)
|
||||
if err != nil {
|
||||
if errors.Is(err, shujubao.ErrDatasource) {
|
||||
return nil, errors.Join(processors.ErrDatasource, err)
|
||||
}
|
||||
if errors.Is(err, shujubao.ErrQueryEmpty) {
|
||||
return nil, errors.Join(processors.ErrNotFound, err)
|
||||
}
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
// 解析响应中的 JSON 字符串(使用 qyglb4c0 中的 RecursiveParse)
|
||||
parsedResp, err := RecursiveParse(data)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
respBytes, err := json.Marshal(parsedResp)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
return respBytes, nil
|
||||
}
|
||||
@@ -153,7 +153,31 @@ func (c *Certification) TransitionTo(targetStatus enums.CertificationStatus, act
|
||||
|
||||
// ================ 业务操作方法 ================
|
||||
|
||||
// SubmitEnterpriseInfo 提交企业信息
|
||||
// SubmitEnterpriseInfoForReview 提交企业信息进入人工审核(不调用 e签宝,不生成认证链接)
|
||||
func (c *Certification) SubmitEnterpriseInfoForReview(enterpriseInfo *value_objects.EnterpriseInfo) error {
|
||||
// 已处于待审核:幂等,直接成功
|
||||
if c.Status == enums.StatusInfoPendingReview {
|
||||
return nil
|
||||
}
|
||||
if c.Status != enums.StatusPending && c.Status != enums.StatusInfoRejected {
|
||||
return fmt.Errorf("当前状态 %s 不允许提交企业信息", enums.GetStatusName(c.Status))
|
||||
}
|
||||
if err := enterpriseInfo.Validate(); err != nil {
|
||||
return fmt.Errorf("企业信息验证失败: %w", err)
|
||||
}
|
||||
if err := c.TransitionTo(enums.StatusInfoPendingReview, enums.ActorTypeUser, c.UserID, "用户提交企业信息,等待人工审核"); err != nil {
|
||||
return err
|
||||
}
|
||||
c.addDomainEvent(&EnterpriseInfoSubmittedEvent{
|
||||
CertificationID: c.ID,
|
||||
UserID: c.UserID,
|
||||
EnterpriseInfo: enterpriseInfo,
|
||||
SubmittedAt: time.Now(),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// SubmitEnterpriseInfo 提交企业信息(直接进入已提交,含认证链接;用于无审核或管理员审核通过后补链)
|
||||
func (c *Certification) SubmitEnterpriseInfo(enterpriseInfo *value_objects.EnterpriseInfo, authURL string, authFlowID string) error {
|
||||
// 验证当前状态
|
||||
if c.Status != enums.StatusPending && c.Status != enums.StatusInfoRejected {
|
||||
@@ -186,6 +210,33 @@ func (c *Certification) SubmitEnterpriseInfo(enterpriseInfo *value_objects.Enter
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApproveEnterpriseInfoReview 管理员审核通过:从待审核转为已提交,并写入企业认证链接
|
||||
func (c *Certification) ApproveEnterpriseInfoReview(authURL, authFlowID string, actorID string) error {
|
||||
if c.Status != enums.StatusInfoPendingReview {
|
||||
return fmt.Errorf("当前状态 %s 不允许执行审核通过", enums.GetStatusName(c.Status))
|
||||
}
|
||||
c.AuthURL = authURL
|
||||
c.AuthFlowID = authFlowID
|
||||
if err := c.TransitionTo(enums.StatusInfoSubmitted, enums.ActorTypeAdmin, actorID, "管理员审核通过"); err != nil {
|
||||
return err
|
||||
}
|
||||
now := time.Now()
|
||||
c.InfoSubmittedAt = &now
|
||||
return nil
|
||||
}
|
||||
|
||||
// RejectEnterpriseInfoReview 管理员审核拒绝
|
||||
func (c *Certification) RejectEnterpriseInfoReview(actorID, message string) error {
|
||||
if c.Status != enums.StatusInfoPendingReview {
|
||||
return fmt.Errorf("当前状态 %s 不允许执行审核拒绝", enums.GetStatusName(c.Status))
|
||||
}
|
||||
c.setFailureInfo(enums.FailureReasonManualReviewRejected, message)
|
||||
if err := c.TransitionTo(enums.StatusInfoRejected, enums.ActorTypeAdmin, actorID, "管理员审核拒绝"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 完成企业认证
|
||||
func (c *Certification) CompleteEnterpriseVerification() error {
|
||||
if c.Status != enums.StatusInfoSubmitted {
|
||||
@@ -448,6 +499,8 @@ func (c *Certification) CompleteCertification() error {
|
||||
func (c *Certification) GetDataByStatus() map[string]interface{} {
|
||||
data := map[string]interface{}{}
|
||||
switch c.Status {
|
||||
case enums.StatusInfoPendingReview:
|
||||
// 待审核,无额外数据
|
||||
case enums.StatusInfoSubmitted:
|
||||
data["auth_url"] = c.AuthURL
|
||||
case enums.StatusInfoRejected:
|
||||
@@ -494,6 +547,8 @@ func (c *Certification) GetAvailableActions() []string {
|
||||
switch c.Status {
|
||||
case enums.StatusPending:
|
||||
actions = append(actions, "submit_enterprise_info")
|
||||
case enums.StatusInfoPendingReview:
|
||||
// 等待人工审核,无用户操作
|
||||
case enums.StatusEnterpriseVerified:
|
||||
actions = append(actions, "apply_contract")
|
||||
case enums.StatusInfoRejected, enums.StatusContractRejected, enums.StatusContractExpired:
|
||||
@@ -587,8 +642,9 @@ func (c *Certification) ValidateBusinessRules() error {
|
||||
|
||||
// validateActorPermission 验证操作者权限
|
||||
func (c *Certification) validateActorPermission(targetStatus enums.CertificationStatus, actor enums.ActorType) bool {
|
||||
// 定义状态转换的权限规则
|
||||
// 定义状态转换的权限规则(目标状态 -> 允许的操作者)
|
||||
permissions := map[enums.CertificationStatus][]enums.ActorType{
|
||||
enums.StatusInfoPendingReview: {enums.ActorTypeUser},
|
||||
enums.StatusInfoSubmitted: {enums.ActorTypeUser, enums.ActorTypeAdmin},
|
||||
enums.StatusEnterpriseVerified: {enums.ActorTypeEsign, enums.ActorTypeSystem, enums.ActorTypeAdmin},
|
||||
enums.StatusInfoRejected: {enums.ActorTypeEsign, enums.ActorTypeSystem, enums.ActorTypeAdmin},
|
||||
|
||||
@@ -19,6 +19,21 @@ type EnterpriseInfoSubmitRecord struct {
|
||||
LegalPersonID string `json:"legal_person_id" gorm:"type:varchar(50);not null"`
|
||||
LegalPersonPhone string `json:"legal_person_phone" gorm:"type:varchar(50);not null"`
|
||||
EnterpriseAddress string `json:"enterprise_address" gorm:"type:varchar(200);not null"` // 新增企业地址
|
||||
|
||||
// 授权代表信息(gorm 指定列名,确保与表 enterprise_info_submit_records 列一致并正确读入)
|
||||
AuthorizedRepName string `json:"authorized_rep_name" gorm:"column:authorized_rep_name;type:varchar(50);comment:授权代表姓名"`
|
||||
AuthorizedRepID string `json:"authorized_rep_id" gorm:"column:authorized_rep_id;type:varchar(50);comment:授权代表身份证号"`
|
||||
AuthorizedRepPhone string `json:"authorized_rep_phone" gorm:"column:authorized_rep_phone;type:varchar(50);comment:授权代表手机号"`
|
||||
// 授权代表身份证正反面图片URL列表(JSON字符串),按顺序存储[人像面, 国徽面]
|
||||
AuthorizedRepIDImageURLs string `json:"authorized_rep_id_image_urls" gorm:"column:authorized_rep_id_image_urls;type:text;comment:授权代表身份证正反面图片URL列表(JSON字符串)"`
|
||||
|
||||
// 企业资质与场地材料
|
||||
BusinessLicenseImageURL string `json:"business_license_image_url" gorm:"type:varchar(500);comment:营业执照图片URL"`
|
||||
OfficePlaceImageURLs string `json:"office_place_image_urls" gorm:"type:text;comment:办公场地图片URL列表(JSON字符串)"`
|
||||
// 应用场景
|
||||
APIUsage string `json:"api_usage" gorm:"type:text;comment:接口用途及业务场景说明"`
|
||||
ScenarioAttachmentURLs string `json:"scenario_attachment_urls" gorm:"type:text;comment:场景附件图片URL列表(JSON字符串)"`
|
||||
|
||||
// 提交状态
|
||||
Status string `json:"status" gorm:"type:varchar(20);not null;default:'submitted'"` // submitted, verified, failed
|
||||
SubmitAt time.Time `json:"submit_at" gorm:"not null"`
|
||||
@@ -26,6 +41,12 @@ type EnterpriseInfoSubmitRecord struct {
|
||||
FailedAt *time.Time `json:"failed_at"`
|
||||
FailureReason string `json:"failure_reason" gorm:"type:text"`
|
||||
|
||||
// 人工审核信息
|
||||
ManualReviewStatus string `json:"manual_review_status" gorm:"type:varchar(20);not null;default:'pending';comment:人工审核状态(pending,approved,rejected)"`
|
||||
ManualReviewRemark string `json:"manual_review_remark" gorm:"type:text;comment:人工审核备注"`
|
||||
ManualReviewedAt *time.Time `json:"manual_reviewed_at" gorm:"comment:人工审核时间"`
|
||||
ManualReviewerID string `json:"manual_reviewer_id" gorm:"type:varchar(36);comment:人工审核人ID"`
|
||||
|
||||
// 系统字段
|
||||
CreatedAt time.Time `json:"created_at" gorm:"not null"`
|
||||
UpdatedAt time.Time `json:"updated_at" gorm:"not null"`
|
||||
@@ -42,18 +63,19 @@ func NewEnterpriseInfoSubmitRecord(
|
||||
userID, companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress string,
|
||||
) *EnterpriseInfoSubmitRecord {
|
||||
return &EnterpriseInfoSubmitRecord{
|
||||
ID: uuid.New().String(),
|
||||
UserID: userID,
|
||||
CompanyName: companyName,
|
||||
UnifiedSocialCode: unifiedSocialCode,
|
||||
LegalPersonName: legalPersonName,
|
||||
LegalPersonID: legalPersonID,
|
||||
LegalPersonPhone: legalPersonPhone,
|
||||
EnterpriseAddress: enterpriseAddress,
|
||||
Status: "submitted",
|
||||
SubmitAt: time.Now(),
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
ID: uuid.New().String(),
|
||||
UserID: userID,
|
||||
CompanyName: companyName,
|
||||
UnifiedSocialCode: unifiedSocialCode,
|
||||
LegalPersonName: legalPersonName,
|
||||
LegalPersonID: legalPersonID,
|
||||
LegalPersonPhone: legalPersonPhone,
|
||||
EnterpriseAddress: enterpriseAddress,
|
||||
Status: "submitted",
|
||||
ManualReviewStatus: "pending",
|
||||
SubmitAt: time.Now(),
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +96,26 @@ func (r *EnterpriseInfoSubmitRecord) MarkAsFailed(reason string) {
|
||||
r.UpdatedAt = now
|
||||
}
|
||||
|
||||
// MarkManualApproved 标记人工审核通过
|
||||
func (r *EnterpriseInfoSubmitRecord) MarkManualApproved(reviewerID, remark string) {
|
||||
now := time.Now()
|
||||
r.ManualReviewStatus = "approved"
|
||||
r.ManualReviewedAt = &now
|
||||
r.ManualReviewerID = reviewerID
|
||||
r.ManualReviewRemark = remark
|
||||
r.UpdatedAt = now
|
||||
}
|
||||
|
||||
// MarkManualRejected 标记人工审核拒绝
|
||||
func (r *EnterpriseInfoSubmitRecord) MarkManualRejected(reviewerID, remark string) {
|
||||
now := time.Now()
|
||||
r.ManualReviewStatus = "rejected"
|
||||
r.ManualReviewedAt = &now
|
||||
r.ManualReviewerID = reviewerID
|
||||
r.ManualReviewRemark = remark
|
||||
r.UpdatedAt = now
|
||||
}
|
||||
|
||||
// IsVerified 检查是否已验证
|
||||
func (r *EnterpriseInfoSubmitRecord) IsVerified() bool {
|
||||
return r.Status == "verified"
|
||||
|
||||
@@ -5,12 +5,13 @@ type CertificationStatus string
|
||||
|
||||
const (
|
||||
// === 主流程状态 ===
|
||||
StatusPending CertificationStatus = "pending" // 待认证
|
||||
StatusInfoSubmitted CertificationStatus = "info_submitted" // 已提交企业信息
|
||||
StatusEnterpriseVerified CertificationStatus = "enterprise_verified" // 已企业认证
|
||||
StatusContractApplied CertificationStatus = "contract_applied" // 已申请签署合同
|
||||
StatusContractSigned CertificationStatus = "contract_signed" // 已签署合同
|
||||
StatusCompleted CertificationStatus = "completed" // 认证完成
|
||||
StatusPending CertificationStatus = "pending" // 待认证
|
||||
StatusInfoPendingReview CertificationStatus = "info_pending_review" // 企业信息待人工审核
|
||||
StatusInfoSubmitted CertificationStatus = "info_submitted" // 已提交企业信息(审核通过)
|
||||
StatusEnterpriseVerified CertificationStatus = "enterprise_verified" // 已企业认证
|
||||
StatusContractApplied CertificationStatus = "contract_applied" // 已申请签署合同
|
||||
StatusContractSigned CertificationStatus = "contract_signed" // 已签署合同
|
||||
StatusCompleted CertificationStatus = "completed" // 认证完成
|
||||
|
||||
// === 失败状态 ===
|
||||
StatusInfoRejected CertificationStatus = "info_rejected" // 企业信息被拒绝
|
||||
@@ -21,6 +22,7 @@ const (
|
||||
// AllStatuses 所有有效状态列表
|
||||
var AllStatuses = []CertificationStatus{
|
||||
StatusPending,
|
||||
StatusInfoPendingReview,
|
||||
StatusInfoSubmitted,
|
||||
StatusEnterpriseVerified,
|
||||
StatusContractApplied,
|
||||
@@ -34,6 +36,7 @@ var AllStatuses = []CertificationStatus{
|
||||
// MainFlowStatuses 主流程状态列表
|
||||
var MainFlowStatuses = []CertificationStatus{
|
||||
StatusPending,
|
||||
StatusInfoPendingReview,
|
||||
StatusInfoSubmitted,
|
||||
StatusEnterpriseVerified,
|
||||
StatusContractApplied,
|
||||
@@ -61,6 +64,7 @@ func IsValidStatus(status CertificationStatus) bool {
|
||||
func GetStatusName(status CertificationStatus) string {
|
||||
statusNames := map[CertificationStatus]string{
|
||||
StatusPending: "待认证",
|
||||
StatusInfoPendingReview: "企业信息待审核",
|
||||
StatusInfoSubmitted: "已提交企业信息",
|
||||
StatusEnterpriseVerified: "已企业认证",
|
||||
StatusContractApplied: "已申请签署合同",
|
||||
@@ -120,14 +124,15 @@ func GetStatusCategory(status CertificationStatus) string {
|
||||
func GetStatusPriority(status CertificationStatus) int {
|
||||
priorities := map[CertificationStatus]int{
|
||||
StatusPending: 1,
|
||||
StatusInfoSubmitted: 2,
|
||||
StatusEnterpriseVerified: 3,
|
||||
StatusContractApplied: 4,
|
||||
StatusContractSigned: 5,
|
||||
StatusCompleted: 6,
|
||||
StatusInfoRejected: 7,
|
||||
StatusContractRejected: 8,
|
||||
StatusContractExpired: 9,
|
||||
StatusInfoPendingReview: 2,
|
||||
StatusInfoSubmitted: 3,
|
||||
StatusEnterpriseVerified: 4,
|
||||
StatusContractApplied: 5,
|
||||
StatusContractSigned: 6,
|
||||
StatusCompleted: 7,
|
||||
StatusInfoRejected: 8,
|
||||
StatusContractRejected: 9,
|
||||
StatusContractExpired: 10,
|
||||
}
|
||||
|
||||
if priority, exists := priorities[status]; exists {
|
||||
@@ -140,6 +145,7 @@ func GetStatusPriority(status CertificationStatus) int {
|
||||
func GetProgressPercentage(status CertificationStatus) int {
|
||||
progressMap := map[CertificationStatus]int{
|
||||
StatusPending: 0,
|
||||
StatusInfoPendingReview: 15,
|
||||
StatusInfoSubmitted: 25,
|
||||
StatusEnterpriseVerified: 50,
|
||||
StatusContractApplied: 75,
|
||||
@@ -160,7 +166,8 @@ func GetProgressPercentage(status CertificationStatus) int {
|
||||
func IsUserActionRequired(status CertificationStatus) bool {
|
||||
userActionRequired := map[CertificationStatus]bool{
|
||||
StatusPending: true, // 需要提交企业信息
|
||||
StatusInfoSubmitted: false, // 等待系统验证
|
||||
StatusInfoPendingReview: false, // 等待人工审核
|
||||
StatusInfoSubmitted: false, // 等待完成企业认证
|
||||
StatusEnterpriseVerified: true, // 需要申请合同
|
||||
StatusContractApplied: true, // 需要签署合同
|
||||
StatusContractSigned: false, // 合同已签署,等待系统处理
|
||||
@@ -180,6 +187,7 @@ func IsUserActionRequired(status CertificationStatus) bool {
|
||||
func GetUserActionHint(status CertificationStatus) string {
|
||||
hints := map[CertificationStatus]string{
|
||||
StatusPending: "请提交企业信息",
|
||||
StatusInfoPendingReview: "企业信息已提交,请等待管理员审核",
|
||||
StatusInfoSubmitted: "请完成企业认证",
|
||||
StatusEnterpriseVerified: "企业认证完成,请申请签署合同",
|
||||
StatusContractApplied: "请在规定时间内完成合同签署",
|
||||
@@ -200,8 +208,13 @@ func GetUserActionHint(status CertificationStatus) string {
|
||||
func GetNextValidStatuses(currentStatus CertificationStatus) []CertificationStatus {
|
||||
nextStatusMap := map[CertificationStatus][]CertificationStatus{
|
||||
StatusPending: {
|
||||
StatusInfoPendingReview, // 用户提交企业信息,进入待审核
|
||||
StatusInfoSubmitted, // 暂时跳过人工审核,直接进入已提交
|
||||
StatusCompleted,
|
||||
},
|
||||
StatusInfoPendingReview: {
|
||||
StatusInfoSubmitted,
|
||||
// 管理员/系统可直接完成认证
|
||||
StatusInfoRejected,
|
||||
StatusCompleted,
|
||||
},
|
||||
StatusInfoSubmitted: {
|
||||
@@ -265,15 +278,18 @@ func CanTransitionTo(currentStatus, targetStatus CertificationStatus) bool {
|
||||
// GetTransitionReason 获取状态转换的原因描述
|
||||
func GetTransitionReason(from, to CertificationStatus) string {
|
||||
transitionReasons := map[string]string{
|
||||
string(StatusPending) + "->" + string(StatusInfoSubmitted): "用户提交企业信息",
|
||||
string(StatusInfoSubmitted) + "->" + string(StatusEnterpriseVerified): "e签宝企业认证成功",
|
||||
string(StatusInfoSubmitted) + "->" + string(StatusInfoRejected): "e签宝企业认证失败",
|
||||
string(StatusEnterpriseVerified) + "->" + string(StatusContractApplied): "用户申请签署合同",
|
||||
string(StatusContractApplied) + "->" + string(StatusContractSigned): "e签宝合同签署成功",
|
||||
string(StatusContractSigned) + "->" + string(StatusCompleted): "系统处理完成,认证成功",
|
||||
string(StatusContractApplied) + "->" + string(StatusContractRejected): "用户拒绝签署合同",
|
||||
string(StatusContractApplied) + "->" + string(StatusContractExpired): "合同签署超时",
|
||||
string(StatusInfoRejected) + "->" + string(StatusInfoSubmitted): "用户重新提交企业信息",
|
||||
string(StatusPending) + "->" + string(StatusInfoPendingReview): "用户提交企业信息,等待人工审核",
|
||||
string(StatusInfoPendingReview) + "->" + string(StatusInfoSubmitted): "管理员审核通过",
|
||||
string(StatusInfoPendingReview) + "->" + string(StatusInfoRejected): "管理员审核拒绝",
|
||||
string(StatusPending) + "->" + string(StatusInfoSubmitted): "用户提交企业信息",
|
||||
string(StatusInfoSubmitted) + "->" + string(StatusEnterpriseVerified): "e签宝企业认证成功",
|
||||
string(StatusInfoSubmitted) + "->" + string(StatusInfoRejected): "e签宝企业认证失败",
|
||||
string(StatusEnterpriseVerified) + "->" + string(StatusContractApplied): "用户申请签署合同",
|
||||
string(StatusContractApplied) + "->" + string(StatusContractSigned): "e签宝合同签署成功",
|
||||
string(StatusContractSigned) + "->" + string(StatusCompleted): "系统处理完成,认证成功",
|
||||
string(StatusContractApplied) + "->" + string(StatusContractRejected): "用户拒绝签署合同",
|
||||
string(StatusContractApplied) + "->" + string(StatusContractExpired): "合同签署超时",
|
||||
string(StatusInfoRejected) + "->" + string(StatusInfoSubmitted): "用户重新提交企业信息",
|
||||
string(StatusContractRejected) + "->" + string(StatusEnterpriseVerified): "重置状态,准备重新申请",
|
||||
string(StatusContractExpired) + "->" + string(StatusEnterpriseVerified): "重置状态,准备重新申请",
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ const (
|
||||
FailureReasonLegalPersonMismatch FailureReason = "legal_person_mismatch" // 法定代表人信息不匹配
|
||||
FailureReasonEsignVerificationFailed FailureReason = "esign_verification_failed" // e签宝验证失败
|
||||
FailureReasonInvalidDocument FailureReason = "invalid_document" // 证件信息无效
|
||||
FailureReasonManualReviewRejected FailureReason = "manual_review_rejected" // 人工审核拒绝
|
||||
|
||||
// === 合同签署失败原因 ===
|
||||
FailureReasonContractRejectedByUser FailureReason = "contract_rejected_by_user" // 用户拒绝签署
|
||||
@@ -35,7 +36,7 @@ var AllFailureReasons = []FailureReason{
|
||||
FailureReasonLegalPersonMismatch,
|
||||
FailureReasonEsignVerificationFailed,
|
||||
FailureReasonInvalidDocument,
|
||||
|
||||
FailureReasonManualReviewRejected,
|
||||
// 合同签署失败
|
||||
FailureReasonContractRejectedByUser,
|
||||
FailureReasonContractExpired,
|
||||
@@ -97,7 +98,7 @@ func GetFailureReasonName(reason FailureReason) string {
|
||||
FailureReasonLegalPersonMismatch: "法定代表人信息不匹配",
|
||||
FailureReasonEsignVerificationFailed: "e签宝验证失败",
|
||||
FailureReasonInvalidDocument: "证件信息无效",
|
||||
|
||||
FailureReasonManualReviewRejected: "人工审核拒绝",
|
||||
// 合同签署失败
|
||||
FailureReasonContractRejectedByUser: "用户拒绝签署",
|
||||
FailureReasonContractExpired: "合同签署超时",
|
||||
@@ -128,7 +129,7 @@ func GetFailureReasonCategory(reason FailureReason) string {
|
||||
FailureReasonLegalPersonMismatch: "企业验证",
|
||||
FailureReasonEsignVerificationFailed: "企业验证",
|
||||
FailureReasonInvalidDocument: "企业验证",
|
||||
|
||||
FailureReasonManualReviewRejected: "人工审核",
|
||||
// 合同签署失败
|
||||
FailureReasonContractRejectedByUser: "合同签署",
|
||||
FailureReasonContractExpired: "合同签署",
|
||||
@@ -189,7 +190,7 @@ func GetSuggestedAction(reason FailureReason) string {
|
||||
FailureReasonLegalPersonMismatch: "请核对法定代表人信息是否正确",
|
||||
FailureReasonEsignVerificationFailed: "请稍后重试,如持续失败请联系客服",
|
||||
FailureReasonInvalidDocument: "请检查证件信息是否有效",
|
||||
|
||||
FailureReasonManualReviewRejected: "请根据审核意见修正后重新提交,或联系客服",
|
||||
// 合同签署失败
|
||||
FailureReasonContractRejectedByUser: "您可以重新申请签署合同",
|
||||
FailureReasonContractExpired: "请重新申请签署合同",
|
||||
@@ -220,7 +221,7 @@ func IsRetryable(reason FailureReason) bool {
|
||||
FailureReasonLegalPersonMismatch: true,
|
||||
FailureReasonEsignVerificationFailed: true, // 可能是临时问题
|
||||
FailureReasonInvalidDocument: true,
|
||||
|
||||
FailureReasonManualReviewRejected: true, // 用户可修正后重新提交
|
||||
// 合同签署失败
|
||||
FailureReasonContractRejectedByUser: true, // 用户可以改变主意
|
||||
FailureReasonContractExpired: true, // 可以重新申请
|
||||
@@ -253,6 +254,7 @@ func GetRetrySuggestion(reason FailureReason) string {
|
||||
FailureReasonLegalPersonMismatch: "请确认法定代表人信息后重新提交",
|
||||
FailureReasonEsignVerificationFailed: "请稍后重新尝试",
|
||||
FailureReasonInvalidDocument: "请检查证件信息后重新提交",
|
||||
FailureReasonManualReviewRejected: "请根据审核意见修正企业信息后重新提交",
|
||||
FailureReasonContractRejectedByUser: "如需要可重新申请合同",
|
||||
FailureReasonContractExpired: "请重新申请合同签署",
|
||||
FailureReasonSignProcessFailed: "请重新尝试签署",
|
||||
|
||||
@@ -5,10 +5,30 @@ import (
|
||||
"tyapi-server/internal/domains/certification/entities"
|
||||
)
|
||||
|
||||
// ListSubmitRecordsFilter 提交记录列表筛选(以状态机 certification 状态为准)
|
||||
type ListSubmitRecordsFilter struct {
|
||||
CertificationStatus string // 认证状态筛选,如 info_pending_review / info_submitted / info_rejected,空为全部
|
||||
CompanyName string // 企业名称(模糊搜索)
|
||||
LegalPersonPhone string // 法人手机号
|
||||
LegalPersonName string // 法人姓名(模糊搜索)
|
||||
Page int
|
||||
PageSize int
|
||||
}
|
||||
|
||||
// ListSubmitRecordsResult 列表结果
|
||||
type ListSubmitRecordsResult struct {
|
||||
Records []*entities.EnterpriseInfoSubmitRecord
|
||||
Total int64
|
||||
}
|
||||
|
||||
type EnterpriseInfoSubmitRecordRepository interface {
|
||||
Create(ctx context.Context, record *entities.EnterpriseInfoSubmitRecord) error
|
||||
Update(ctx context.Context, record *entities.EnterpriseInfoSubmitRecord) error
|
||||
Exists(ctx context.Context, ID string) (bool, error)
|
||||
FindByID(ctx context.Context, id string) (*entities.EnterpriseInfoSubmitRecord, error)
|
||||
FindLatestByUserID(ctx context.Context, userID string) (*entities.EnterpriseInfoSubmitRecord, error)
|
||||
FindLatestVerifiedByUserID(ctx context.Context, userID string) (*entities.EnterpriseInfoSubmitRecord, error)
|
||||
// ExistsByUnifiedSocialCodeExcludeUser 检查该统一社会信用代码是否已被其他用户提交(已提交/已通过验证,排除指定用户)
|
||||
ExistsByUnifiedSocialCodeExcludeUser(ctx context.Context, unifiedSocialCode string, excludeUserID string) (bool, error)
|
||||
List(ctx context.Context, filter ListSubmitRecordsFilter) (*ListSubmitRecordsResult, error)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package certification
|
||||
import (
|
||||
"context"
|
||||
"tyapi-server/internal/domains/certification/entities"
|
||||
"tyapi-server/internal/domains/certification/repositories"
|
||||
"tyapi-server/internal/shared/database"
|
||||
|
||||
"go.uber.org/zap"
|
||||
@@ -39,6 +40,15 @@ func (r *GormEnterpriseInfoSubmitRecordRepository) Exists(ctx context.Context, I
|
||||
return r.ExistsEntity(ctx, ID, &entities.EnterpriseInfoSubmitRecord{})
|
||||
}
|
||||
|
||||
func (r *GormEnterpriseInfoSubmitRecordRepository) FindByID(ctx context.Context, id string) (*entities.EnterpriseInfoSubmitRecord, error) {
|
||||
var record entities.EnterpriseInfoSubmitRecord
|
||||
err := r.GetDB(ctx).Where("id = ?", id).First(&record).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
func (r *GormEnterpriseInfoSubmitRecordRepository) FindLatestByUserID(ctx context.Context, userID string) (*entities.EnterpriseInfoSubmitRecord, error) {
|
||||
var record entities.EnterpriseInfoSubmitRecord
|
||||
err := r.GetDB(ctx).
|
||||
@@ -62,3 +72,68 @@ func (r *GormEnterpriseInfoSubmitRecordRepository) FindLatestVerifiedByUserID(ct
|
||||
}
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
// ExistsByUnifiedSocialCodeExcludeUser 检查该统一社会信用代码是否已被其他用户占用(已提交或已通过验证的记录)
|
||||
func (r *GormEnterpriseInfoSubmitRecordRepository) ExistsByUnifiedSocialCodeExcludeUser(ctx context.Context, unifiedSocialCode string, excludeUserID string) (bool, error) {
|
||||
if unifiedSocialCode == "" {
|
||||
return false, nil
|
||||
}
|
||||
var count int64
|
||||
query := r.GetDB(ctx).Model(&entities.EnterpriseInfoSubmitRecord{}).
|
||||
Where("unified_social_code = ? AND status IN (?, ?)", unifiedSocialCode, "submitted", "verified")
|
||||
if excludeUserID != "" {
|
||||
query = query.Where("user_id != ?", excludeUserID)
|
||||
}
|
||||
if err := query.Count(&count).Error; err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func (r *GormEnterpriseInfoSubmitRecordRepository) List(ctx context.Context, filter repositories.ListSubmitRecordsFilter) (*repositories.ListSubmitRecordsResult, error) {
|
||||
base := r.GetDB(ctx).Model(&entities.EnterpriseInfoSubmitRecord{})
|
||||
if filter.CertificationStatus != "" {
|
||||
base = base.Joins("JOIN certifications ON certifications.user_id = enterprise_info_submit_records.user_id AND certifications.deleted_at IS NULL").
|
||||
Where("certifications.status = ?", filter.CertificationStatus)
|
||||
}
|
||||
if filter.CompanyName != "" {
|
||||
base = base.Where("enterprise_info_submit_records.company_name LIKE ?", "%"+filter.CompanyName+"%")
|
||||
}
|
||||
if filter.LegalPersonPhone != "" {
|
||||
base = base.Where("enterprise_info_submit_records.legal_person_phone = ?", filter.LegalPersonPhone)
|
||||
}
|
||||
if filter.LegalPersonName != "" {
|
||||
base = base.Where("enterprise_info_submit_records.legal_person_name LIKE ?", "%"+filter.LegalPersonName+"%")
|
||||
}
|
||||
var total int64
|
||||
if err := base.Count(&total).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if filter.PageSize <= 0 {
|
||||
filter.PageSize = 10
|
||||
}
|
||||
if filter.Page <= 0 {
|
||||
filter.Page = 1
|
||||
}
|
||||
offset := (filter.Page - 1) * filter.PageSize
|
||||
var records []*entities.EnterpriseInfoSubmitRecord
|
||||
q := r.GetDB(ctx).Model(&entities.EnterpriseInfoSubmitRecord{})
|
||||
if filter.CertificationStatus != "" {
|
||||
q = q.Joins("JOIN certifications ON certifications.user_id = enterprise_info_submit_records.user_id AND certifications.deleted_at IS NULL").
|
||||
Where("certifications.status = ?", filter.CertificationStatus)
|
||||
}
|
||||
if filter.CompanyName != "" {
|
||||
q = q.Where("enterprise_info_submit_records.company_name LIKE ?", "%"+filter.CompanyName+"%")
|
||||
}
|
||||
if filter.LegalPersonPhone != "" {
|
||||
q = q.Where("enterprise_info_submit_records.legal_person_phone = ?", filter.LegalPersonPhone)
|
||||
}
|
||||
if filter.LegalPersonName != "" {
|
||||
q = q.Where("enterprise_info_submit_records.legal_person_name LIKE ?", "%"+filter.LegalPersonName+"%")
|
||||
}
|
||||
err := q.Order("enterprise_info_submit_records.submit_at DESC").Offset(offset).Limit(filter.PageSize).Find(&records).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &repositories.ListSubmitRecordsResult{Records: records, Total: total}, nil
|
||||
}
|
||||
@@ -14,17 +14,19 @@ import (
|
||||
"tyapi-server/internal/application/certification/dto/commands"
|
||||
"tyapi-server/internal/application/certification/dto/queries"
|
||||
_ "tyapi-server/internal/application/certification/dto/responses"
|
||||
"tyapi-server/internal/infrastructure/external/storage"
|
||||
"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
|
||||
appService certification.CertificationApplicationService
|
||||
response interfaces.ResponseBuilder
|
||||
validator interfaces.RequestValidator
|
||||
logger *zap.Logger
|
||||
jwtAuth *middleware.JWTAuthMiddleware
|
||||
storageService *storage.QiNiuStorageService
|
||||
}
|
||||
|
||||
// NewCertificationHandler 创建认证处理器
|
||||
@@ -34,13 +36,15 @@ func NewCertificationHandler(
|
||||
validator interfaces.RequestValidator,
|
||||
logger *zap.Logger,
|
||||
jwtAuth *middleware.JWTAuthMiddleware,
|
||||
storageService *storage.QiNiuStorageService,
|
||||
) *CertificationHandler {
|
||||
return &CertificationHandler{
|
||||
appService: appService,
|
||||
response: response,
|
||||
validator: validator,
|
||||
logger: logger,
|
||||
jwtAuth: jwtAuth,
|
||||
appService: appService,
|
||||
response: response,
|
||||
validator: validator,
|
||||
logger: logger,
|
||||
jwtAuth: jwtAuth,
|
||||
storageService: storageService,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +299,78 @@ func (h *CertificationHandler) RecognizeBusinessLicense(c *gin.Context) {
|
||||
h.response.Success(c, result, "营业执照识别成功")
|
||||
}
|
||||
|
||||
// UploadCertificationFile 上传认证相关图片到七牛云(企业信息中的营业执照、办公场地、场景附件、授权代表身份证等)
|
||||
// @Summary 上传认证图片
|
||||
// @Description 上传企业信息中使用的图片到七牛云,返回可访问的 URL
|
||||
// @Tags 认证管理
|
||||
// @Accept multipart/form-data
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param file formData file true "图片文件"
|
||||
// @Success 200 {object} map[string]string "上传成功,返回 url 与 key"
|
||||
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
||||
// @Failure 401 {object} map[string]interface{} "未认证"
|
||||
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
||||
// @Router /api/v1/certifications/upload [post]
|
||||
func (h *CertificationHandler) UploadCertificationFile(c *gin.Context) {
|
||||
userID := h.getCurrentUserID(c)
|
||||
if userID == "" {
|
||||
h.response.Unauthorized(c, "用户未登录")
|
||||
return
|
||||
}
|
||||
|
||||
file, err := c.FormFile("file")
|
||||
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,
|
||||
}
|
||||
contentType := file.Header.Get("Content-Type")
|
||||
if !allowedTypes[contentType] {
|
||||
h.response.BadRequest(c, "只支持 JPG、PNG、WEBP 格式的图片")
|
||||
return
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
fileBytes, err := io.ReadAll(src)
|
||||
if err != nil {
|
||||
h.logger.Error("读取文件内容失败", zap.Error(err), zap.String("user_id", userID))
|
||||
h.response.BadRequest(c, "文件读取失败")
|
||||
return
|
||||
}
|
||||
|
||||
uploadResult, err := h.storageService.UploadFile(c.Request.Context(), fileBytes, file.Filename)
|
||||
if err != nil {
|
||||
h.logger.Error("上传文件到七牛云失败", zap.Error(err), zap.String("user_id", userID), zap.String("file_name", file.Filename))
|
||||
h.response.BadRequest(c, "图片上传失败,请稍后重试")
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, map[string]string{
|
||||
"url": uploadResult.URL,
|
||||
"key": uploadResult.Key,
|
||||
}, "上传成功")
|
||||
}
|
||||
|
||||
// ListCertifications 获取认证列表(管理员)
|
||||
// @Summary 获取认证列表
|
||||
// @Description 管理员获取认证申请列表
|
||||
@@ -375,6 +451,147 @@ func (h *CertificationHandler) AdminCompleteCertificationWithoutContract(c *gin.
|
||||
h.response.Success(c, result, "代用户完成认证成功")
|
||||
}
|
||||
|
||||
// AdminListSubmitRecords 管理端分页查询企业信息提交记录
|
||||
// @Summary 管理端企业审核列表
|
||||
// @Tags 认证管理
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param page query int false "页码"
|
||||
// @Param page_size query int false "每页条数"
|
||||
// @Param certification_status query string false "按状态机筛选:info_pending_review/info_submitted/info_rejected,空为全部"
|
||||
// @Success 200 {object} responses.AdminSubmitRecordsListResponse
|
||||
// @Router /api/v1/certifications/admin/submit-records [get]
|
||||
func (h *CertificationHandler) AdminListSubmitRecords(c *gin.Context) {
|
||||
query := &queries.AdminListSubmitRecordsQuery{}
|
||||
if err := c.ShouldBindQuery(query); err != nil {
|
||||
h.response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
result, err := h.appService.AdminListSubmitRecords(c.Request.Context(), query)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, result, "获取成功")
|
||||
}
|
||||
|
||||
// AdminGetSubmitRecordByID 管理端获取单条提交记录详情
|
||||
// @Summary 管理端企业审核详情
|
||||
// @Tags 认证管理
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param id path string true "记录ID"
|
||||
// @Success 200 {object} responses.AdminSubmitRecordDetail
|
||||
// @Router /api/v1/certifications/admin/submit-records/{id} [get]
|
||||
func (h *CertificationHandler) AdminGetSubmitRecordByID(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
h.response.BadRequest(c, "记录ID不能为空")
|
||||
return
|
||||
}
|
||||
result, err := h.appService.AdminGetSubmitRecordByID(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, result, "获取成功")
|
||||
}
|
||||
|
||||
// AdminApproveSubmitRecord 管理端审核通过
|
||||
// @Summary 管理端企业审核通过
|
||||
// @Tags 认证管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param id path string true "记录ID"
|
||||
// @Param request body object true "可选 remark"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /api/v1/certifications/admin/submit-records/{id}/approve [post]
|
||||
func (h *CertificationHandler) AdminApproveSubmitRecord(c *gin.Context) {
|
||||
adminID := h.getCurrentUserID(c)
|
||||
if adminID == "" {
|
||||
h.response.Unauthorized(c, "未登录")
|
||||
return
|
||||
}
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
h.response.BadRequest(c, "记录ID不能为空")
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
_ = c.ShouldBindJSON(&body)
|
||||
if err := h.appService.AdminApproveSubmitRecord(c.Request.Context(), id, adminID, body.Remark); err != nil {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, nil, "审核通过")
|
||||
}
|
||||
|
||||
// AdminRejectSubmitRecord 管理端审核拒绝
|
||||
// @Summary 管理端企业审核拒绝
|
||||
// @Tags 认证管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param id path string true "记录ID"
|
||||
// @Param request body object true "remark 必填"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /api/v1/certifications/admin/submit-records/{id}/reject [post]
|
||||
func (h *CertificationHandler) AdminRejectSubmitRecord(c *gin.Context) {
|
||||
adminID := h.getCurrentUserID(c)
|
||||
if adminID == "" {
|
||||
h.response.Unauthorized(c, "未登录")
|
||||
return
|
||||
}
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
h.response.BadRequest(c, "记录ID不能为空")
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
Remark string `json:"remark" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
h.response.BadRequest(c, "请填写拒绝原因(remark)")
|
||||
return
|
||||
}
|
||||
if err := h.appService.AdminRejectSubmitRecord(c.Request.Context(), id, adminID, body.Remark); err != nil {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, nil, "已拒绝")
|
||||
}
|
||||
|
||||
// AdminTransitionCertificationStatus 管理端按用户变更认证状态(以状态机为准)
|
||||
// @Summary 管理端变更认证状态
|
||||
// @Tags 认证管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param request body commands.AdminTransitionCertificationStatusCommand true "user_id, target_status(info_submitted/info_rejected), remark"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /api/v1/certifications/admin/transition-status [post]
|
||||
func (h *CertificationHandler) AdminTransitionCertificationStatus(c *gin.Context) {
|
||||
adminID := h.getCurrentUserID(c)
|
||||
if adminID == "" {
|
||||
h.response.Unauthorized(c, "未登录")
|
||||
return
|
||||
}
|
||||
var cmd commands.AdminTransitionCertificationStatusCommand
|
||||
if err := c.ShouldBindJSON(&cmd); err != nil {
|
||||
h.response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
cmd.AdminID = adminID
|
||||
if err := h.appService.AdminTransitionCertificationStatus(c.Request.Context(), &cmd); err != nil {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, nil, "状态已更新")
|
||||
}
|
||||
|
||||
// ================ 回调处理 ================
|
||||
|
||||
// HandleEsignCallback 处理e签宝回调
|
||||
|
||||
@@ -14,6 +14,7 @@ type CertificationRoutes struct {
|
||||
router *http.GinRouter
|
||||
logger *zap.Logger
|
||||
auth *middleware.JWTAuthMiddleware
|
||||
admin *middleware.AdminAuthMiddleware
|
||||
optional *middleware.OptionalAuthMiddleware
|
||||
dailyRateLimit *middleware.DailyRateLimitMiddleware
|
||||
}
|
||||
@@ -24,6 +25,7 @@ func NewCertificationRoutes(
|
||||
router *http.GinRouter,
|
||||
logger *zap.Logger,
|
||||
auth *middleware.JWTAuthMiddleware,
|
||||
admin *middleware.AdminAuthMiddleware,
|
||||
optional *middleware.OptionalAuthMiddleware,
|
||||
dailyRateLimit *middleware.DailyRateLimitMiddleware,
|
||||
) *CertificationRoutes {
|
||||
@@ -32,6 +34,7 @@ func NewCertificationRoutes(
|
||||
router: router,
|
||||
logger: logger,
|
||||
auth: auth,
|
||||
admin: admin,
|
||||
optional: optional,
|
||||
dailyRateLimit: dailyRateLimit,
|
||||
}
|
||||
@@ -57,6 +60,9 @@ func (r *CertificationRoutes) Register(router *http.GinRouter) {
|
||||
// OCR营业执照识别接口
|
||||
authGroup.POST("/ocr/business-license", r.handler.RecognizeBusinessLicense)
|
||||
|
||||
// 认证图片上传(七牛云,用于企业信息中的各类图片)
|
||||
authGroup.POST("/upload", r.handler.UploadCertificationFile)
|
||||
|
||||
// 3. 申请合同签署
|
||||
authGroup.POST("/apply-contract", r.handler.ApplyContract)
|
||||
|
||||
@@ -71,6 +77,21 @@ func (r *CertificationRoutes) Register(router *http.GinRouter) {
|
||||
|
||||
}
|
||||
|
||||
// 管理端企业审核(需管理员权限,以状态机状态为准)
|
||||
adminGroup := certificationGroup.Group("/admin")
|
||||
adminGroup.Use(r.auth.Handle())
|
||||
adminGroup.Use(r.admin.Handle())
|
||||
{
|
||||
adminGroup.POST("/transition-status", r.handler.AdminTransitionCertificationStatus)
|
||||
}
|
||||
adminCertGroup := adminGroup.Group("/submit-records")
|
||||
{
|
||||
adminCertGroup.GET("", r.handler.AdminListSubmitRecords)
|
||||
adminCertGroup.GET("/:id", r.handler.AdminGetSubmitRecordByID)
|
||||
adminCertGroup.POST("/:id/approve", r.handler.AdminApproveSubmitRecord)
|
||||
adminCertGroup.POST("/:id/reject", r.handler.AdminRejectSubmitRecord)
|
||||
}
|
||||
|
||||
// 回调路由(不需要认证,但需要验证签名)
|
||||
callbackGroup := certificationGroup.Group("/callbacks")
|
||||
{
|
||||
|
||||
@@ -19,15 +19,15 @@ type ComprehensiveLoggerMiddleware struct {
|
||||
|
||||
// ComprehensiveLoggerConfig 全面日志配置
|
||||
type ComprehensiveLoggerConfig struct {
|
||||
EnableRequestLogging bool // 是否记录请求日志
|
||||
EnableResponseLogging bool // 是否记录响应日志
|
||||
EnableRequestBodyLogging bool // 是否记录请求体
|
||||
EnableErrorLogging bool // 是否记录错误日志
|
||||
EnableBusinessLogging bool // 是否记录业务日志
|
||||
EnablePerformanceLogging bool // 是否记录性能日志
|
||||
MaxBodySize int64 // 最大记录体大小
|
||||
ExcludePaths []string // 排除的路径
|
||||
IncludePaths []string // 包含的路径
|
||||
EnableRequestLogging bool // 是否记录请求日志
|
||||
EnableResponseLogging bool // 是否记录响应日志
|
||||
EnableRequestBodyLogging bool // 是否记录请求体
|
||||
EnableErrorLogging bool // 是否记录错误日志
|
||||
EnableBusinessLogging bool // 是否记录业务日志
|
||||
EnablePerformanceLogging bool // 是否记录性能日志
|
||||
MaxBodySize int64 // 最大记录体大小
|
||||
ExcludePaths []string // 排除的路径
|
||||
IncludePaths []string // 包含的路径
|
||||
}
|
||||
|
||||
// NewComprehensiveLoggerMiddleware 创建全面日志中间件
|
||||
@@ -240,7 +240,7 @@ func (m *ComprehensiveLoggerMiddleware) logErrors(c *gin.Context, requestID, tra
|
||||
zap.String("user_id", userID),
|
||||
zap.String("method", c.Request.Method),
|
||||
zap.String("path", c.Request.URL.Path),
|
||||
zap.String("error_type", string(ginErr.Type)),
|
||||
zap.Uint64("error_type", uint64(ginErr.Type)),
|
||||
zap.Error(ginErr.Err),
|
||||
zap.Time("timestamp", time.Now()),
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ type DailyRateLimitConfig struct {
|
||||
BlockedCountries []string `mapstructure:"blocked_countries"` // 被阻止的国家/地区
|
||||
EnableProxyCheck bool `mapstructure:"enable_proxy_check"` // 是否检查代理
|
||||
MaxConcurrent int `mapstructure:"max_concurrent"` // 最大并发请求数
|
||||
|
||||
// 路径排除配置
|
||||
ExcludePaths []string `mapstructure:"exclude_paths"` // 排除频率限制的路径
|
||||
// 域名排除配置
|
||||
@@ -97,7 +98,10 @@ func (m *DailyRateLimitMiddleware) GetPriority() int {
|
||||
func (m *DailyRateLimitMiddleware) Handle() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
if m.config.App.IsDevelopment() {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
// 检查是否在排除路径中
|
||||
if m.isExcludedPath(c.Request.URL.Path) {
|
||||
c.Next()
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
# 📋 PDF表格转换日志查看指南
|
||||
|
||||
## 📍 日志文件位置
|
||||
|
||||
### 1. 开发环境
|
||||
日志文件存储在项目根目录的 `logs/` 目录下:
|
||||
|
||||
```
|
||||
tyapi-server/
|
||||
└── logs/
|
||||
├── 2024-12-02/ # 按日期分包(如果启用)
|
||||
│ ├── debug.log # Debug级别日志(包含JSON转换详情)
|
||||
│ ├── info.log # Info级别日志(包含转换流程)
|
||||
│ └── error.log # Error级别日志(包含错误信息)
|
||||
└── app.log # 传统模式(如果未启用按日分包)
|
||||
```
|
||||
|
||||
### 2. 生产环境(Docker)
|
||||
日志文件存储在容器的 `/app/logs/` 目录,映射到宿主机的 `./logs/` 目录:
|
||||
|
||||
```bash
|
||||
# 查看宿主机日志
|
||||
./logs/2024-12-02/info.log
|
||||
./logs/2024-12-02/debug.log
|
||||
```
|
||||
|
||||
## 🔍 如何查看转换日志
|
||||
|
||||
### 方法1:实时查看日志(推荐)
|
||||
|
||||
```bash
|
||||
# 查看Info级别日志(转换流程)
|
||||
tail -f logs/2024-12-02/info.log | grep "表格\|JSON\|markdown"
|
||||
|
||||
# 查看Debug级别日志(详细JSON数据)
|
||||
tail -f logs/2024-12-02/debug.log | grep "JSON\|表格"
|
||||
|
||||
# 查看所有PDF相关日志
|
||||
tail -f logs/2024-12-02/*.log | grep -i "pdf\|table\|json"
|
||||
```
|
||||
|
||||
### 方法2:使用Docker查看
|
||||
|
||||
```bash
|
||||
# 查看容器实时日志
|
||||
docker logs -f tyapi-app-prod | grep -i "表格\|json\|markdown"
|
||||
|
||||
# 查看最近的100行日志
|
||||
docker logs --tail 100 tyapi-app-prod | grep -i "表格\|json"
|
||||
```
|
||||
|
||||
### 方法3:搜索特定字段类型
|
||||
|
||||
```bash
|
||||
# 查看请求参数的转换日志
|
||||
grep "request_params" logs/2024-12-02/info.log
|
||||
|
||||
# 查看响应字段的转换日志
|
||||
grep "response_fields" logs/2024-12-02/info.log
|
||||
|
||||
# 查看错误代码的转换日志
|
||||
grep "error_codes" logs/2024-12-02/info.log
|
||||
```
|
||||
|
||||
## 📊 日志级别说明
|
||||
|
||||
### Info级别日志(info.log)
|
||||
包含转换流程的关键步骤:
|
||||
- ✅ 数据格式检测(JSON/Markdown)
|
||||
- ✅ Markdown表格解析开始
|
||||
- ✅ 表格解析成功(表头数量、行数)
|
||||
- ✅ JSON转换完成
|
||||
|
||||
**示例日志:**
|
||||
```
|
||||
2024-12-02T10:30:15Z INFO 开始解析markdown表格并转换为JSON {"field_type": "request_params", "content_length": 1234}
|
||||
2024-12-02T10:30:15Z INFO markdown表格解析成功 {"field_type": "request_params", "header_count": 3, "row_count": 5}
|
||||
2024-12-02T10:30:15Z INFO 表格数据已转换为JSON格式 {"field_type": "request_params", "json_array_length": 5}
|
||||
```
|
||||
|
||||
### Debug级别日志(debug.log)
|
||||
包含详细的转换数据:
|
||||
- 🔍 原始内容预览
|
||||
- 🔍 解析后的表头列表
|
||||
- 🔍 转换后的完整JSON数据(前1000字符)
|
||||
- 🔍 每行的转换详情
|
||||
|
||||
**示例日志:**
|
||||
```
|
||||
2024-12-02T10:30:15Z DEBUG 转换后的JSON数据预览 {"field_type": "request_params", "json_length": 2345, "json_preview": "[{\"字段名\":\"name\",\"类型\":\"string\",\"说明\":\"姓名\"}...]"}
|
||||
```
|
||||
|
||||
### Error级别日志(error.log)
|
||||
包含转换过程中的错误:
|
||||
- ❌ Markdown解析失败
|
||||
- ❌ JSON序列化失败
|
||||
- ❌ 数据格式错误
|
||||
|
||||
**示例日志:**
|
||||
```
|
||||
2024-12-02T10:30:15Z ERROR 解析markdown表格失败 {"field_type": "request_params", "error": "无法解析表格:未找到表头", "content_preview": "..."}
|
||||
```
|
||||
|
||||
## 🔎 日志关键词搜索
|
||||
|
||||
### 转换流程关键词
|
||||
- `开始解析markdown表格` - 转换开始
|
||||
- `markdown表格解析成功` - 解析完成
|
||||
- `表格数据已转换为JSON格式` - JSON转换完成
|
||||
- `转换后的JSON数据预览` - JSON数据详情
|
||||
|
||||
### 数据格式关键词
|
||||
- `数据已经是JSON格式` - 数据源是JSON
|
||||
- `从JSON对象中提取数组数据` - 从JSON对象提取
|
||||
- `解析markdown表格并转换为JSON` - Markdown转JSON
|
||||
|
||||
### 错误关键词
|
||||
- `解析markdown表格失败` - 解析错误
|
||||
- `JSON序列化失败` - JSON错误
|
||||
- `字段内容为空` - 空数据
|
||||
|
||||
## 📝 日志配置
|
||||
|
||||
确保日志级别设置为 `debug` 才能看到详细的JSON转换日志:
|
||||
|
||||
```yaml
|
||||
# config.yaml 或 configs/env.development.yaml
|
||||
logger:
|
||||
level: "debug" # 开发环境使用debug级别
|
||||
format: "console" # 或 "json"
|
||||
output: "file" # 输出到文件
|
||||
log_dir: "logs" # 日志目录
|
||||
use_daily: true # 启用按日分包
|
||||
```
|
||||
|
||||
## 🛠️ 常用命令
|
||||
|
||||
```bash
|
||||
# 查看今天的Info日志
|
||||
cat logs/$(date +%Y-%m-%d)/info.log | grep "表格\|JSON"
|
||||
|
||||
# 查看最近的转换日志(最后50行)
|
||||
tail -n 50 logs/$(date +%Y-%m-%d)/info.log
|
||||
|
||||
# 搜索特定产品的转换日志
|
||||
grep "product_id.*xxx" logs/$(date +%Y-%m-%d)/info.log
|
||||
|
||||
# 查看所有错误
|
||||
grep "ERROR" logs/$(date +%Y-%m-%d)/error.log
|
||||
|
||||
# 统计转换次数
|
||||
grep "表格数据已转换为JSON格式" logs/$(date +%Y-%m-%d)/info.log | wc -l
|
||||
```
|
||||
|
||||
## 💡 调试技巧
|
||||
|
||||
1. **查看完整JSON数据**:如果JSON数据超过1000字符,查看debug.log获取完整内容
|
||||
2. **追踪转换流程**:使用 `field_type` 字段过滤特定字段的转换日志
|
||||
3. **定位错误**:查看error.log中的 `content_preview` 字段了解原始数据
|
||||
4. **性能监控**:统计转换次数和耗时,优化转换逻辑
|
||||
|
||||
## 📌 注意事项
|
||||
|
||||
- Debug级别日志可能包含大量数据,注意日志文件大小
|
||||
- 生产环境建议使用 `info` 级别,减少日志量
|
||||
- JSON预览限制在1000字符,完整数据请查看debug日志
|
||||
- 日志文件按日期自动分包,便于管理和查找
|
||||
|
||||
@@ -552,6 +552,9 @@ func (r *DatabaseTableReader) getContentPreview(content string, maxLen int) stri
|
||||
if len(content) <= maxLen {
|
||||
return content
|
||||
}
|
||||
if maxLen > len(content) {
|
||||
maxLen = len(content)
|
||||
}
|
||||
return content[:maxLen] + "..."
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,10 @@ func (r *DatabaseTableRenderer) RenderTable(pdf *gofpdf.Fpdf, tableData *TableDa
|
||||
r.logger.Warn("表格数据为空,跳过渲染")
|
||||
return nil
|
||||
}
|
||||
// 避免表格绘制在页眉区,防止遮挡 logo
|
||||
if pdf.GetY() < ContentStartYBelowHeader {
|
||||
pdf.SetY(ContentStartYBelowHeader)
|
||||
}
|
||||
|
||||
// 检查表头是否有有效内容
|
||||
hasValidHeader := false
|
||||
@@ -67,8 +71,13 @@ func (r *DatabaseTableRenderer) RenderTable(pdf *gofpdf.Fpdf, tableData *TableDa
|
||||
// 即使没有数据行,也渲染表头(单行表格)
|
||||
// 但如果没有表头也没有数据,则不渲染
|
||||
|
||||
// 设置字体
|
||||
r.fontManager.SetFont(pdf, "", 9)
|
||||
// 表格线细线(返回字段说明等表格线不要太粗)
|
||||
savedLineWidth := pdf.GetLineWidth()
|
||||
pdf.SetLineWidth(0.2)
|
||||
defer pdf.SetLineWidth(savedLineWidth)
|
||||
|
||||
// 正文字体:宋体小四 12pt
|
||||
r.fontManager.SetBodyFont(pdf, "", BodyFontSizeXiaosi)
|
||||
_, lineHt := pdf.GetFontSize()
|
||||
|
||||
// 计算页面可用宽度
|
||||
@@ -87,6 +96,7 @@ func (r *DatabaseTableRenderer) RenderTable(pdf *gofpdf.Fpdf, tableData *TableDa
|
||||
if currentY+estimatedHeaderHeight > pageHeight-bottomMargin {
|
||||
r.logger.Debug("表头前需要分页", zap.Float64("current_y", currentY))
|
||||
pdf.AddPage()
|
||||
pdf.SetY(ContentStartYBelowHeader)
|
||||
}
|
||||
|
||||
// 绘制表头
|
||||
@@ -111,7 +121,7 @@ func (r *DatabaseTableRenderer) RenderTable(pdf *gofpdf.Fpdf, tableData *TableDa
|
||||
// 策略:先确保每列最短内容能完整显示(不换行),然后根据内容长度分配剩余空间
|
||||
func (r *DatabaseTableRenderer) calculateColumnWidths(pdf *gofpdf.Fpdf, tableData *TableData, availableWidth float64) []float64 {
|
||||
numCols := len(tableData.Headers)
|
||||
r.fontManager.SetFont(pdf, "", 9)
|
||||
r.fontManager.SetBodyFont(pdf, "", BodyFontSizeXiaosi)
|
||||
|
||||
// 第一步:找到每列中最短的内容(包括表头),计算其完整显示所需的最小宽度
|
||||
colMinWidths := make([]float64, numCols)
|
||||
@@ -361,8 +371,8 @@ func (r *DatabaseTableRenderer) renderHeader(pdf *gofpdf.Fpdf, headers []string,
|
||||
|
||||
// 绘制表头背景和文本
|
||||
pdf.SetFillColor(74, 144, 226) // 蓝色背景
|
||||
pdf.SetTextColor(0, 0, 0) // 黑色文字
|
||||
r.fontManager.SetFont(pdf, "B", 9)
|
||||
pdf.SetTextColor(0, 0, 0) // 黑色文字
|
||||
r.fontManager.SetBodyFont(pdf, "B", BodyFontSizeXiaosi)
|
||||
|
||||
currentX := 15.0
|
||||
for i, header := range headers {
|
||||
@@ -395,8 +405,7 @@ func (r *DatabaseTableRenderer) renderHeader(pdf *gofpdf.Fpdf, headers []string,
|
||||
pdf.SetXY(currentX+2, textStartY)
|
||||
// 确保颜色为深黑色(在渲染前再次设置,防止被覆盖)
|
||||
pdf.SetTextColor(0, 0, 0) // 表头是黑色文字
|
||||
// 设置字体,确保颜色不会变淡
|
||||
r.fontManager.SetFont(pdf, "B", 9)
|
||||
r.fontManager.SetBodyFont(pdf, "B", BodyFontSizeXiaosi)
|
||||
// 再次确保颜色为深黑色(在渲染前最后一次设置)
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
// 使用正常的行高,文本已经垂直居中(减少内边距,给文本更多空间)
|
||||
@@ -416,7 +425,7 @@ func (r *DatabaseTableRenderer) renderRows(pdf *gofpdf.Fpdf, rows [][]string, co
|
||||
numCols := len(colWidths)
|
||||
pdf.SetFillColor(245, 245, 220) // 米色背景
|
||||
pdf.SetTextColor(0, 0, 0) // 深黑色文字,确保清晰
|
||||
r.fontManager.SetFont(pdf, "", 9)
|
||||
r.fontManager.SetBodyFont(pdf, "", BodyFontSizeXiaosi)
|
||||
|
||||
// 获取页面尺寸和边距
|
||||
_, pageHeight := pdf.GetPageSize()
|
||||
@@ -448,6 +457,7 @@ func (r *DatabaseTableRenderer) renderRows(pdf *gofpdf.Fpdf, rows [][]string, co
|
||||
zap.Float64("current_y", currentY),
|
||||
zap.Float64("page_height", pageHeight))
|
||||
pdf.AddPage()
|
||||
pdf.SetY(ContentStartYBelowHeader)
|
||||
// 在新页面上重新绘制表头
|
||||
if len(headers) > 0 && len(headerColWidths) > 0 {
|
||||
newHeaderStartY := pdf.GetY()
|
||||
@@ -523,6 +533,7 @@ func (r *DatabaseTableRenderer) renderRows(pdf *gofpdf.Fpdf, rows [][]string, co
|
||||
zap.Int("row_index", rowIndex),
|
||||
zap.Float64("row_height", maxCellHeight))
|
||||
pdf.AddPage()
|
||||
pdf.SetY(ContentStartYBelowHeader)
|
||||
startY = pdf.GetY()
|
||||
}
|
||||
|
||||
@@ -572,8 +583,7 @@ func (r *DatabaseTableRenderer) renderRows(pdf *gofpdf.Fpdf, rows [][]string, co
|
||||
pdf.SetXY(currentX+2, textStartY)
|
||||
// 再次确保颜色为深黑色(防止被其他设置覆盖)
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
// 设置字体,确保颜色不会变淡
|
||||
r.fontManager.SetFont(pdf, "", 9)
|
||||
r.fontManager.SetBodyFont(pdf, "", BodyFontSizeXiaosi)
|
||||
// 再次确保颜色为深黑色(在渲染前最后一次设置)
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
// 安全地渲染文本,使用正常的行高
|
||||
|
||||
@@ -16,6 +16,8 @@ type FontManager struct {
|
||||
chineseFontLoaded bool
|
||||
watermarkFontName string
|
||||
watermarkFontLoaded bool
|
||||
bodyFontName string
|
||||
bodyFontLoaded bool
|
||||
}
|
||||
|
||||
// NewFontManager 创建字体管理器
|
||||
@@ -24,6 +26,7 @@ func NewFontManager(logger *zap.Logger) *FontManager {
|
||||
logger: logger,
|
||||
chineseFontName: "ChineseFont",
|
||||
watermarkFontName: "WatermarkFont",
|
||||
bodyFontName: "BodyFont",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +76,21 @@ func (fm *FontManager) LoadWatermarkFont(pdf *gofpdf.Fpdf) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// LoadBodyFont 加载正文用宋体(用于描述、详情、说明、表格文字等)
|
||||
func (fm *FontManager) LoadBodyFont(pdf *gofpdf.Fpdf) bool {
|
||||
if fm.bodyFontLoaded {
|
||||
return true
|
||||
}
|
||||
fontPaths := fm.getBodyFontPaths()
|
||||
for _, fontPath := range fontPaths {
|
||||
if fm.tryAddFont(pdf, fontPath, fm.bodyFontName) {
|
||||
fm.bodyFontLoaded = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// tryAddFont 尝试添加字体(统一处理中文字体和水印字体)
|
||||
func (fm *FontManager) tryAddFont(pdf *gofpdf.Fpdf, fontPath, fontName string) bool {
|
||||
defer func() {
|
||||
@@ -205,13 +223,27 @@ func (fm *FontManager) getChineseFontPaths() []string {
|
||||
func (fm *FontManager) getWatermarkFontPaths() []string {
|
||||
// 水印字体文件名(尝试大小写变体)
|
||||
fontNames := []string{
|
||||
"YunFengFeiYunTi-2.ttf", // 优先尝试大写版本
|
||||
"yunfengfeiyunti-2.ttf", // 小写版本(兼容)
|
||||
// "XuanZongTi-v0.1.otf", //玄宗字体不支持otf
|
||||
"WenYuanSerifSC-Bold.ttf", //文渊雅黑
|
||||
// "YunFengFeiYunTi-2.ttf", // 毛笔字体
|
||||
// "yunfengfeiyunti-2.ttf", // 毛笔字体小写版本(兼容)
|
||||
}
|
||||
|
||||
return fm.buildFontPaths(fontNames)
|
||||
}
|
||||
|
||||
// getBodyFontPaths 获取正文宋体路径列表(小四对应 12pt)
|
||||
// 优先使用 resources/pdf/fonts/simsun.ttc(宋体)
|
||||
func (fm *FontManager) getBodyFontPaths() []string {
|
||||
fontNames := []string{
|
||||
// "simsun.ttc", // 宋体(项目内 resources/pdf/fonts)
|
||||
"simsun.ttf",
|
||||
"SimSun.ttf",
|
||||
"WenYuanSerifSC-Bold.ttf", // 文渊宋体风格,备选
|
||||
}
|
||||
return fm.buildFontPaths(fontNames)
|
||||
}
|
||||
|
||||
// buildFontPaths 构建字体文件路径列表(仅从resources/pdf/fonts加载)
|
||||
// 返回所有存在的字体文件的绝对路径
|
||||
func (fm *FontManager) buildFontPaths(fontNames []string) []string {
|
||||
@@ -295,6 +327,28 @@ func (fm *FontManager) SetWatermarkFont(pdf *gofpdf.Fpdf, style string, size flo
|
||||
}
|
||||
}
|
||||
|
||||
// BodyFontSizeXiaosi 正文小四字号(约 12pt)
|
||||
const BodyFontSizeXiaosi = 12.0
|
||||
|
||||
// SetBodyFont 设置正文字体(宋体小四:描述、详情、说明、表格文字等)
|
||||
func (fm *FontManager) SetBodyFont(pdf *gofpdf.Fpdf, style string, size float64) {
|
||||
if size <= 0 {
|
||||
size = BodyFontSizeXiaosi
|
||||
}
|
||||
if fm.bodyFontLoaded {
|
||||
pdf.SetFont(fm.bodyFontName, style, size)
|
||||
} else if fm.watermarkFontLoaded {
|
||||
pdf.SetFont(fm.watermarkFontName, style, size)
|
||||
} else {
|
||||
fm.SetFont(pdf, style, size)
|
||||
}
|
||||
}
|
||||
|
||||
// IsBodyFontAvailable 正文字体(宋体)是否已加载
|
||||
func (fm *FontManager) IsBodyFontAvailable() bool {
|
||||
return fm.bodyFontLoaded || fm.watermarkFontLoaded
|
||||
}
|
||||
|
||||
// IsChineseFontAvailable 检查中文字体是否可用
|
||||
func (fm *FontManager) IsChineseFontAvailable() bool {
|
||||
return fm.chineseFontLoaded
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -434,4 +434,3 @@ func (m *PDFCacheManager) GetCacheStats() (map[string]interface{}, error) {
|
||||
"max_size": m.maxSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -2078,69 +2078,55 @@ func (g *PDFGenerator) addHeader(pdf *gofpdf.Fpdf, chineseFontAvailable bool) {
|
||||
pdf.Line(15, 22, 75, 22)
|
||||
}
|
||||
|
||||
// addWatermark 添加水印(从左边开始向上倾斜45度,考虑可用区域)
|
||||
// addWatermark 添加水印:自左下角往右上角倾斜 45°,单条水印居中于页面,样式柔和
|
||||
func (g *PDFGenerator) addWatermark(pdf *gofpdf.Fpdf, chineseFontAvailable bool) {
|
||||
// 如果中文字体不可用,跳过水印(避免显示乱码)
|
||||
if !chineseFontAvailable {
|
||||
return
|
||||
}
|
||||
|
||||
// 保存当前图形状态
|
||||
pdf.TransformBegin()
|
||||
defer pdf.TransformEnd()
|
||||
|
||||
// 获取页面尺寸和边距
|
||||
_, pageHeight := pdf.GetPageSize()
|
||||
pageWidth, pageHeight := pdf.GetPageSize()
|
||||
leftMargin, topMargin, _, bottomMargin := pdf.GetMargins()
|
||||
|
||||
// 计算实际可用区域高度
|
||||
usableHeight := pageHeight - topMargin - bottomMargin
|
||||
usableWidth := pageWidth - leftMargin*2
|
||||
|
||||
// 设置水印样式(使用中文字体)
|
||||
fontSize := 45.0
|
||||
|
||||
fontSize := 42.0
|
||||
pdf.SetFont("ChineseFont", "", fontSize)
|
||||
|
||||
// 设置灰色和透明度(加深水印,使其更明显)
|
||||
pdf.SetTextColor(180, 180, 180) // 深一点的灰色
|
||||
pdf.SetAlpha(0.25, "Normal") // 增加透明度,让水印更明显
|
||||
pdf.SetTextColor(150, 150, 150)
|
||||
pdf.SetAlpha(0.32, "Normal")
|
||||
|
||||
// 计算文字宽度
|
||||
textWidth := pdf.GetStringWidth(g.watermarkText)
|
||||
if textWidth == 0 {
|
||||
// 如果无法获取宽度(字体未注册),使用估算值(中文字符大约每个 fontSize/3 mm)
|
||||
textWidth = float64(len([]rune(g.watermarkText))) * fontSize / 3.0
|
||||
}
|
||||
|
||||
// 从左边开始,计算起始位置
|
||||
// 起始X:左边距
|
||||
// 起始Y:考虑水印文字长度和旋转后需要的空间
|
||||
startX := leftMargin
|
||||
startY := topMargin + textWidth*0.5 // 为旋转留出空间
|
||||
|
||||
// 移动到起始位置
|
||||
pdf.TransformTranslate(startX, startY)
|
||||
|
||||
// 向上倾斜45度(顺时针旋转45度,即-45度,或逆时针315度)
|
||||
pdf.TransformRotate(-45, 0, 0)
|
||||
|
||||
// 检查文字是否会超出可用区域(旋转后的对角线长度)
|
||||
rotatedDiagonal := math.Sqrt(textWidth*textWidth + fontSize*fontSize)
|
||||
if rotatedDiagonal > usableHeight*0.8 {
|
||||
// 如果太大,缩小字体
|
||||
fontSize = fontSize * usableHeight * 0.8 / rotatedDiagonal
|
||||
if rotatedDiagonal > usableHeight*0.75 {
|
||||
fontSize = fontSize * usableHeight * 0.75 / rotatedDiagonal
|
||||
pdf.SetFont("ChineseFont", "", fontSize)
|
||||
textWidth = pdf.GetStringWidth(g.watermarkText)
|
||||
if textWidth == 0 {
|
||||
textWidth = float64(len([]rune(g.watermarkText))) * fontSize / 3.0
|
||||
}
|
||||
rotatedDiagonal = math.Sqrt(textWidth*textWidth + fontSize*fontSize)
|
||||
}
|
||||
|
||||
// 从左边开始绘制水印文字
|
||||
startX := leftMargin
|
||||
startY := pageHeight - bottomMargin
|
||||
diagW := rotatedDiagonal * math.Cos(45*math.Pi/180)
|
||||
offsetX := (usableWidth - diagW) * 0.5
|
||||
startX += offsetX
|
||||
startY -= rotatedDiagonal * 0.5
|
||||
|
||||
pdf.TransformTranslate(startX, startY)
|
||||
pdf.TransformRotate(45, 0, 0)
|
||||
|
||||
pdf.SetXY(0, 0)
|
||||
pdf.CellFormat(textWidth, fontSize, g.watermarkText, "", 0, "L", false, 0, "")
|
||||
|
||||
// 恢复透明度和颜色
|
||||
pdf.SetAlpha(1.0, "Normal")
|
||||
pdf.SetTextColor(0, 0, 0) // 恢复为黑色
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
}
|
||||
|
||||
@@ -151,20 +151,25 @@ func (g *PDFGeneratorRefactored) generatePDF(product *entities.Product, doc *ent
|
||||
|
||||
// 创建PDF文档 (A4大小,gofpdf v2 默认支持UTF-8)
|
||||
pdf := gofpdf.New("P", "mm", "A4", "")
|
||||
// 优化边距,减少空白
|
||||
pdf.SetMargins(15, 25, 15)
|
||||
// 上边距与 ContentStartYBelowHeader 一致,这样自动分页后新页内容从 logo 下方开始,不被遮挡
|
||||
pdf.SetMargins(15, ContentStartYBelowHeader, 15)
|
||||
// 开启自动分页并预留底边距,避免内容贴底;分页后由 SetHeaderFunc 绘制页眉,正文从 ContentStartYBelowHeader 起排
|
||||
pdf.SetAutoPageBreak(true, 18)
|
||||
|
||||
// 加载黑体字体(用于所有内容,除了水印)
|
||||
// 注意:此时工作目录应该是根目录(/),这样gofpdf处理路径时就能正确解析
|
||||
chineseFontAvailable := g.fontManager.LoadChineseFont(pdf)
|
||||
|
||||
// 加载水印字体(使用宋体或其他非黑体字体)
|
||||
// 加载水印字体
|
||||
watermarkFontAvailable := g.fontManager.LoadWatermarkFont(pdf)
|
||||
// 加载正文宋体(描述、详情、说明、表格文字等使用小四 12pt)
|
||||
bodyFontAvailable := g.fontManager.LoadBodyFont(pdf)
|
||||
|
||||
// 记录字体加载状态,便于诊断问题
|
||||
g.logger.Info("PDF字体加载状态",
|
||||
zap.Bool("chinese_font_loaded", chineseFontAvailable),
|
||||
zap.Bool("watermark_font_loaded", watermarkFontAvailable),
|
||||
zap.Bool("body_font_loaded", bodyFontAvailable),
|
||||
zap.String("watermark_text", g.watermarkText),
|
||||
)
|
||||
|
||||
@@ -176,9 +181,22 @@ func (g *PDFGeneratorRefactored) generatePDF(product *entities.Product, doc *ent
|
||||
// 创建页面构建器
|
||||
pageBuilder := NewPageBuilder(g.logger, g.fontManager, g.textProcessor, g.markdownProc, g.tableParser, g.tableRenderer, g.jsonProcessor, g.logoPath, g.watermarkText)
|
||||
|
||||
// 添加第一页(产品信息)
|
||||
// 页眉只绘制 logo 和横线;水印改到页脚绘制,确保水印在最上层不被表格等内容遮挡
|
||||
pdf.SetHeaderFunc(func() {
|
||||
pageBuilder.addHeader(pdf, chineseFontAvailable)
|
||||
})
|
||||
pdf.SetFooterFunc(func() {
|
||||
pageBuilder.addWatermark(pdf, chineseFontAvailable)
|
||||
})
|
||||
|
||||
// 添加第一页(封面:产品信息 + 产品描述 + 价格)
|
||||
pageBuilder.AddFirstPage(pdf, product, doc, chineseFontAvailable)
|
||||
|
||||
// 产品详情单独一页(左对齐,段前两空格)
|
||||
if product.Content != "" {
|
||||
pageBuilder.AddProductContentPage(pdf, product, chineseFontAvailable)
|
||||
}
|
||||
|
||||
// 如果是组合包,需要特殊处理:先渲染所有文档,最后统一添加二维码
|
||||
if product.IsPackage {
|
||||
// 如果有关联的文档,添加接口文档页面(但不包含二维码和说明,后面统一添加)
|
||||
|
||||
@@ -90,6 +90,158 @@ func (tp *TextProcessor) StripHTML(text string) string {
|
||||
return text
|
||||
}
|
||||
|
||||
// HTMLToPlainWithBreaks 将 HTML 转为纯文本并保留富文本换行效果(<p><br><div> 等变为换行)
|
||||
// 用于在 PDF 中还原段落与换行,避免内容挤成一团
|
||||
func (tp *TextProcessor) HTMLToPlainWithBreaks(text string) string {
|
||||
text = html.UnescapeString(text)
|
||||
// 块级结束标签转为换行
|
||||
text = regexp.MustCompile(`(?i)</(p|div|br|tr|li|h[1-6])>\s*`).ReplaceAllString(text, "\n")
|
||||
// <br> 自闭合
|
||||
text = regexp.MustCompile(`(?i)<br\s*/?>\s*`).ReplaceAllString(text, "\n")
|
||||
// 剩余标签移除
|
||||
text = regexp.MustCompile(`<[^>]+>`).ReplaceAllString(text, "")
|
||||
// 连续空白/换行压缩为最多两个换行(段间距)
|
||||
text = regexp.MustCompile(`[ \t]+`).ReplaceAllString(text, " ")
|
||||
text = regexp.MustCompile(`\n{3,}`).ReplaceAllString(text, "\n\n")
|
||||
return strings.TrimSpace(text)
|
||||
}
|
||||
|
||||
// HTMLSegment 用于 PDF 绘制的 HTML 片段:支持段落、换行、加粗、标题
|
||||
type HTMLSegment struct {
|
||||
Text string // 纯文本(已去标签、已解码实体)
|
||||
Bold bool // 是否加粗
|
||||
NewLine bool // 是否换行(如 <br>)
|
||||
NewParagraph bool // 是否新段落(如 </p>、</div>)
|
||||
HeadingLevel int // 1-3 表示 h1-h3,0 表示正文
|
||||
}
|
||||
|
||||
// ParseHTMLToSegments 将 HTML 解析为用于 PDF 绘制的片段序列,保留段落、换行、加粗与标题
|
||||
func (tp *TextProcessor) ParseHTMLToSegments(htmlStr string) []HTMLSegment {
|
||||
htmlStr = html.UnescapeString(htmlStr)
|
||||
var out []HTMLSegment
|
||||
blockSplit := regexp.MustCompile(`(?i)(</p>|</div>|</h[1-6]>|<br\s*/?>)\s*`)
|
||||
parts := blockSplit.Split(htmlStr, -1)
|
||||
tags := blockSplit.FindAllString(htmlStr, -1)
|
||||
for i, block := range parts {
|
||||
block = strings.TrimSpace(block)
|
||||
var prevTag string
|
||||
if i > 0 && i-1 < len(tags) {
|
||||
prevTag = strings.ToLower(strings.TrimSpace(tags[i-1]))
|
||||
}
|
||||
isNewParagraph := strings.Contains(prevTag, "</p>") || strings.Contains(prevTag, "</div>") ||
|
||||
strings.HasPrefix(prevTag, "</h")
|
||||
isNewLine := strings.Contains(prevTag, "<br")
|
||||
headingLevel := 0
|
||||
if strings.HasPrefix(prevTag, "</h1") {
|
||||
headingLevel = 1
|
||||
} else if strings.HasPrefix(prevTag, "</h2") {
|
||||
headingLevel = 2
|
||||
} else if strings.HasPrefix(prevTag, "</h3") {
|
||||
headingLevel = 3
|
||||
}
|
||||
segments := tp.parseInlineSegments(block)
|
||||
// 块前先输出段落/换行/标题标记(仅在第一段文本前输出一次)
|
||||
if i > 0 {
|
||||
if isNewParagraph || headingLevel > 0 {
|
||||
out = append(out, HTMLSegment{NewParagraph: true, HeadingLevel: headingLevel})
|
||||
} else if isNewLine {
|
||||
out = append(out, HTMLSegment{NewLine: true})
|
||||
}
|
||||
}
|
||||
for _, seg := range segments {
|
||||
if seg.Text != "" {
|
||||
out = append(out, HTMLSegment{Text: seg.Text, Bold: seg.Bold, HeadingLevel: headingLevel})
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// inlineSeg 内联片段(文本 + 是否加粗)
|
||||
type inlineSeg struct {
|
||||
Text string
|
||||
Bold bool
|
||||
}
|
||||
|
||||
// parseInlineSegments 解析块内文本,按 <strong>/<b> 拆成片段
|
||||
func (tp *TextProcessor) parseInlineSegments(block string) []inlineSeg {
|
||||
var segs []inlineSeg
|
||||
// 移除所有标签并收集加粗区间(按字符偏移)
|
||||
reBoldOpen := regexp.MustCompile(`(?i)<(strong|b)>`)
|
||||
reBoldClose := regexp.MustCompile(`(?i)</(strong|b)>`)
|
||||
plain := regexp.MustCompile(`<[^>]+>`).ReplaceAllString(block, "")
|
||||
plain = regexp.MustCompile(`[ \t]+`).ReplaceAllString(plain, " ")
|
||||
plain = strings.TrimSpace(plain)
|
||||
if plain == "" {
|
||||
return segs
|
||||
}
|
||||
// 在 block 上找加粗区间,再映射到 plain(去掉标签后的位置)
|
||||
// 注意:work 每次循环被截断,必须用相对 work 的索引切片,避免 work[:endInWork] 越界
|
||||
work := block
|
||||
var boldRanges [][2]int
|
||||
plainOffset := 0
|
||||
for {
|
||||
idxOpen := reBoldOpen.FindStringIndex(work)
|
||||
if idxOpen == nil {
|
||||
break
|
||||
}
|
||||
afterOpen := work[idxOpen[1]:]
|
||||
idxClose := reBoldClose.FindStringIndex(afterOpen)
|
||||
if idxClose == nil {
|
||||
break
|
||||
}
|
||||
closeLen := len(reBoldClose.FindString(afterOpen))
|
||||
// 使用相对当前 work 的字节偏移,保证 work[:endInWork] 不越界
|
||||
endInWork := idxOpen[1] + idxClose[0]
|
||||
workBefore := work[:idxOpen[1]]
|
||||
plainBefore := regexp.MustCompile(`<[^>]+>`).ReplaceAllString(workBefore, "")
|
||||
plainBefore = regexp.MustCompile(`[ \t]+`).ReplaceAllString(plainBefore, " ")
|
||||
startPlain := plainOffset + len([]rune(plainBefore))
|
||||
workUntil := work[:endInWork]
|
||||
plainUntil := regexp.MustCompile(`<[^>]+>`).ReplaceAllString(workUntil, "")
|
||||
plainUntil = regexp.MustCompile(`[ \t]+`).ReplaceAllString(plainUntil, " ")
|
||||
endPlain := plainOffset + len([]rune(plainUntil))
|
||||
boldRanges = append(boldRanges, [2]int{startPlain, endPlain})
|
||||
consumed := work[:endInWork+closeLen]
|
||||
strippedConsumed := regexp.MustCompile(`<[^>]+>`).ReplaceAllString(consumed, "")
|
||||
strippedConsumed = regexp.MustCompile(`[ \t]+`).ReplaceAllString(strippedConsumed, " ")
|
||||
plainOffset += len([]rune(strippedConsumed))
|
||||
work = work[endInWork+closeLen:]
|
||||
}
|
||||
// 按 boldRanges 切分 plain(限制区间在 [0,len(runes)] 内,防止越界)
|
||||
runes := []rune(plain)
|
||||
nr := len(runes)
|
||||
inBold := false
|
||||
var start int
|
||||
for i := 0; i <= nr; i++ {
|
||||
nowBold := false
|
||||
for _, r := range boldRanges {
|
||||
r0, r1 := r[0], r[1]
|
||||
if r0 < 0 {
|
||||
r0 = 0
|
||||
}
|
||||
if r1 > nr {
|
||||
r1 = nr
|
||||
}
|
||||
if r0 < r1 && i >= r0 && i < r1 {
|
||||
nowBold = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if nowBold != inBold || i == nr {
|
||||
if i > start {
|
||||
segs = append(segs, inlineSeg{Text: string(runes[start:i]), Bold: inBold})
|
||||
}
|
||||
start = i
|
||||
inBold = nowBold
|
||||
}
|
||||
}
|
||||
if len(segs) == 0 && plain != "" {
|
||||
segs = append(segs, inlineSeg{Text: plain, Bold: false})
|
||||
}
|
||||
return segs
|
||||
}
|
||||
|
||||
// RemoveMarkdownSyntax 移除markdown语法,保留纯文本
|
||||
func (tp *TextProcessor) RemoveMarkdownSyntax(text string) string {
|
||||
// 移除粗体标记 **text** 或 __text__
|
||||
|
||||
@@ -637,8 +637,8 @@ func validateEnterpriseName(fl validator.FieldLevel) bool {
|
||||
hasValidSuffix = true
|
||||
break
|
||||
}
|
||||
// 同时检查括号内的企业类型,如:(个体工商户)、(分公司)
|
||||
if strings.HasSuffix(trimmedName, "("+suffix+")") {
|
||||
// 同时检查括号内的企业类型,支持中英文括号,如:(个体工商户)、(个体工商户)、(分公司)、(分公司)
|
||||
if strings.HasSuffix(trimmedName, "("+suffix+")") || strings.HasSuffix(trimmedName, "("+suffix+")") {
|
||||
hasValidSuffix = true
|
||||
break
|
||||
}
|
||||
@@ -921,8 +921,8 @@ func ValidateEnterpriseName(enterpriseName string) error {
|
||||
hasValidSuffix = true
|
||||
break
|
||||
}
|
||||
// 同时检查括号内的企业类型,如:(个体工商户)、(分公司)
|
||||
if strings.HasSuffix(trimmedName, "("+suffix+")") {
|
||||
// 同时检查括号内的企业类型,支持中英文括号,如:(个体工商户)、(个体工商户)、(分公司)、(分公司)
|
||||
if strings.HasSuffix(trimmedName, "("+suffix+")") || strings.HasSuffix(trimmedName, "("+suffix+")") {
|
||||
hasValidSuffix = true
|
||||
break
|
||||
}
|
||||
|
||||
BIN
resources/pdf/fonts/WenYuanSerifSC-Bold.ttf
Normal file
BIN
resources/pdf/fonts/WenYuanSerifSC-Bold.ttf
Normal file
Binary file not shown.
@@ -11,7 +11,7 @@
|
||||
获得更多详情请访问 [https://www.tianyuanapi.com/]
|
||||
|
||||
2. 提供正式生产环境公网 IP
|
||||
在与商务团队沟通并了解清楚接入流程后,请您将正式生产环境的公网 IP 提供给天远数据。我们将依据您提供的公网 IP 进行 IP 访问设置,以确保后续接口调用的顺利进行。
|
||||
在与商务团队沟通并了解清楚接入流程后,请您将正式生产环境的公网 IP 提供给天远数据。可以自行在开发者中心添加白名单IP,我们将依据您提供的公网 IP 进行 IP 访问设置,以确保后续接口调用的顺利进行。
|
||||
|
||||
3. 构造并加密请求报文
|
||||
您需要构造 JSON 明文请求报文,然后使用 AES-128 算法(基于账户获得的16进制字符串密钥/Access Key)对该明文请求报文进行加密处理。加密时采用AES-CBC模式(密钥长度128位/16字节,填充方式PKCS7),每次加密随机生成16字节(128位)的IV(初始化向量),将IV与加密后的密文拼接在一起,最后通过Base64编码形成可传输的字符串,并将该Base64字符串放入请求体的data字段传参。此步骤中涉及的代码部分,您可参考我们提供的demo包,里面有详细的示例和说明,能帮助您顺利完成报文的构造、加密及Base64编码操作。
|
||||
@@ -23,5 +23,5 @@
|
||||
当您获得接口返回的结果后,需要先对Base64解码后的数据提取前16字节作为IV,再使用该IV通过AES-CBC模式解密剩余密文,最后去除PKCS7填充得到原始明文。同样,关于Base64解码及AES解密(含IV提取、填充去除)的代码实现,您可参考test包中的相关内容,以顺利完成返回结果的解密操作。
|
||||
|
||||
|
||||
若您在接入过程中有任何疑问或需要进一步的帮助,请随时与我们联系。您可以通过上述的商务邮箱和商务联系电话与我们的团队沟通,我们将竭诚为您服务。
|
||||
为方便开发者,我们在开发者中心中,在线测试页面右上角提供了不同语言的示例代码框架,您可以直接复制示例代码去调用相关的接口去进行调试,若您在接入过程中有任何疑问或需要进一步的帮助,请随时与我们联系。您可以通过上述的商务邮箱和商务联系电话与我们的团队沟通,我们将竭诚为您服务。
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,811 +0,0 @@
|
||||
[
|
||||
{
|
||||
"feature": {
|
||||
"featureName": "人企关系加强版",
|
||||
"sort": 1
|
||||
},
|
||||
"data": {
|
||||
"apiID": "QYGL3F8E",
|
||||
"data": {
|
||||
"items": [
|
||||
{
|
||||
"abnormal_info": {},
|
||||
"basicInfo": {
|
||||
"apprdate": "2025-11-27",
|
||||
"base": "han",
|
||||
"candate": "",
|
||||
"city": "xxxxx",
|
||||
"companyOrgType": "有限责任公司(自然人投资或控股)",
|
||||
"creditCode": "xxxxx",
|
||||
"district": "秀xxx区",
|
||||
"estiblishTime": "2024-06-20",
|
||||
"his_staffList": {
|
||||
"result": [
|
||||
{
|
||||
"name": "xxxx",
|
||||
"type": "2",
|
||||
"typeJoin": [
|
||||
"执行董事兼总经理"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"industry": "xxxxxx",
|
||||
"industry_code": "L",
|
||||
"legalPersonName": "xxxx",
|
||||
"name": "xxxxx有限公司",
|
||||
"nic_code": "L7281",
|
||||
"nic_name": "租赁和商务服务业-商务服务业-会议、展览及相关服务-科技会展服务",
|
||||
"opscope": "许可经营项目:在线数据处理与交易处理业务(经营类电子商务);互联网游戏服务;第二类增值电信业务;互联网信息服务(许可经营项目凭许可证件经营)一般经营项目:品牌管理;5G通信技术服务;人工智能应用软件开发;互联网安全服务;量子计算技术服务;技术服务、技术开发、技术咨询、技术交流、技术转让、技术推广;网络技术服务;专业设计服务;互联网数据服务;互联网销售(除销售需要许可的商品);食品互联网销售(仅销售预包装食品);软件开发;动漫游戏开发;计算机软硬件及辅助设备零售;计算机软硬件及辅助设备批发;计算器设备销售;机械设备销售;五金产品零售;五金产品批发;电子产品销售;人工智能硬件销售;通信设备销售;光通信设备销售;通信设备制造;信息系统集成服务;图文设计制作;广告设计、代理;广告发布;数字内容制作服务(不含出版发行);数字文化创意软件开发;软件销售;市场营销策划;企业管理咨询;信息咨询服务(不含许可类信息咨询服务);市场调查(不含涉外调查);工业设计服务;玩具销售;化妆品零售;化妆品批发;摄像及视频制作服务;平面设计;法律咨询(不含依法须律师事务所执业许可的业务);旅游开发项目策划咨询;体育用品及器材批发;体育用品及器材零售;户外用品销售;体育赛事策划;体育健康服务;组织体育表演活动;体育中介代理服务;信息技术咨询服务;数据处理服务;数据处理和存储支持服务;大数据服务;云计算装备技术服务;电子、机械设备维护(不含特种设备);智能机器人的研发;货物进出口;技术进出口;食品进出口(经营范围中的一般经营项目依法自主开展经营活动,通过国家企业信用信息公示系统(海南)向社会公示)",
|
||||
"province": "xxx省",
|
||||
"reccap": 0,
|
||||
"reccapcur": "人民币",
|
||||
"regCapital": "100.000000万人民币",
|
||||
"regCapitalCurrency": "人民币",
|
||||
"regNumber": "4601xxxxx1916",
|
||||
"regStatus": "存续(在营、开业、在册)",
|
||||
"regorg": "xxxxxx督管理局",
|
||||
"revdate": "",
|
||||
"type": "1"
|
||||
},
|
||||
"financing_history": {},
|
||||
"fsource": "1",
|
||||
"his_stockHolderItem": {
|
||||
"investDate": "2025-11-27",
|
||||
"investRate": "",
|
||||
"orgHolderName": "xxx",
|
||||
"orgHolderType": "xxxx人",
|
||||
"subscriptAmt": ""
|
||||
},
|
||||
"invest_history": {},
|
||||
"lawsuit_info": {
|
||||
"entout": {
|
||||
"msg": "没有找到"
|
||||
},
|
||||
"sxbzxr": {
|
||||
"msg": "没有找到"
|
||||
},
|
||||
"xgbzxr": {
|
||||
"msg": "没有找到"
|
||||
}
|
||||
},
|
||||
"orgName": "海xxxx限公司",
|
||||
"own_tax": {},
|
||||
"pName": "xxx",
|
||||
"punishment_info": {},
|
||||
"relationship": [
|
||||
"his_sh",
|
||||
"his_tm"
|
||||
],
|
||||
"tax_contravention": {}
|
||||
}
|
||||
],
|
||||
"total": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"feature": {
|
||||
"featureName": "全景雷达",
|
||||
"sort": 2
|
||||
},
|
||||
"data": {
|
||||
"apiID": "JRZQ7F1A",
|
||||
"data": {
|
||||
"apply_report_detail": {
|
||||
"A22160001": "503",
|
||||
"A22160002": "76",
|
||||
"A22160003": "38",
|
||||
"A22160004": "11",
|
||||
"A22160005": "13",
|
||||
"A22160006": "90",
|
||||
"A22160007": "2023-05",
|
||||
"A22160008": "15",
|
||||
"A22160009": "33",
|
||||
"A22160010": "48"
|
||||
},
|
||||
"behavior_report_detail": {
|
||||
"B22170001": "547",
|
||||
"B22170002": "1",
|
||||
"B22170003": "3",
|
||||
"B22170004": "7",
|
||||
"B22170005": "20",
|
||||
"B22170006": "32",
|
||||
"B22170007": "(0,500)",
|
||||
"B22170008": "[2000,3000)",
|
||||
"B22170009": "[10000,20000)",
|
||||
"B22170010": "[30000,50000)",
|
||||
"B22170011": "[50000,+)",
|
||||
"B22170012": "11",
|
||||
"B22170013": "7",
|
||||
"B22170014": "2",
|
||||
"B22170015": "0",
|
||||
"B22170016": "1",
|
||||
"B22170017": "2",
|
||||
"B22170018": "3",
|
||||
"B22170019": "6",
|
||||
"B22170020": "7",
|
||||
"B22170021": "6",
|
||||
"B22170022": "7",
|
||||
"B22170023": "0",
|
||||
"B22170024": "0",
|
||||
"B22170025": "4",
|
||||
"B22170026": "8",
|
||||
"B22170027": "9",
|
||||
"B22170028": "4",
|
||||
"B22170029": "5",
|
||||
"B22170030": "5",
|
||||
"B22170031": "[3000,5000)",
|
||||
"B22170032": "[5000,10000)",
|
||||
"B22170033": "[5000,10000)",
|
||||
"B22170034": "70%",
|
||||
"B22170035": "4",
|
||||
"B22170036": "14",
|
||||
"B22170037": "21",
|
||||
"B22170038": "35",
|
||||
"B22170039": "102",
|
||||
"B22170040": "(0,500)",
|
||||
"B22170041": "[500,1000)",
|
||||
"B22170042": "[500,1000)",
|
||||
"B22170043": "[10000,20000)",
|
||||
"B22170044": "[30000,50000)",
|
||||
"B22170045": "4",
|
||||
"B22170046": "6",
|
||||
"B22170047": "8",
|
||||
"B22170048": "35",
|
||||
"B22170049": "168",
|
||||
"B22170050": "(7,15]",
|
||||
"B22170051": "82",
|
||||
"B22170052": "24",
|
||||
"B22170053": "720",
|
||||
"B22170054": "2023-04"
|
||||
},
|
||||
"current_report_detail": {
|
||||
"C22180001": "0",
|
||||
"C22180002": "0",
|
||||
"C22180003": "0",
|
||||
"C22180004": "0",
|
||||
"C22180005": "0",
|
||||
"C22180006": "0",
|
||||
"C22180007": "5",
|
||||
"C22180008": "9",
|
||||
"C22180009": "12600",
|
||||
"C22180010": "6120",
|
||||
"C22180011": "10600",
|
||||
"C22180012": "80"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"feature": {
|
||||
"featureName": "特殊名单验证B",
|
||||
"sort": 3
|
||||
},
|
||||
"data": {
|
||||
"apiID": "JRZQ8A2D",
|
||||
"data": {
|
||||
"cell": {
|
||||
"nbank_bad": "0",
|
||||
"nbank_bad_allnum": "1",
|
||||
"nbank_bad_time": "2",
|
||||
"nbank_finlea_lost": "0",
|
||||
"nbank_finlea_lost_allnum": "4",
|
||||
"nbank_finlea_lost_time": "5",
|
||||
"nbank_lost": "0",
|
||||
"nbank_lost_allnum": "4",
|
||||
"nbank_lost_time": "5",
|
||||
"nbank_other_bad": "0",
|
||||
"nbank_other_bad_allnum": "1",
|
||||
"nbank_other_bad_time": "2"
|
||||
},
|
||||
"id": {
|
||||
"court_executed": "0",
|
||||
"court_executed_allnum": "5",
|
||||
"court_executed_time": "2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"feature": {
|
||||
"featureName": "个人司法涉诉查询",
|
||||
"sort": 4
|
||||
},
|
||||
"data": {
|
||||
"apiID": "FLXG7E8F",
|
||||
"data": {
|
||||
"judicial_data": {
|
||||
"breachCaseList": [
|
||||
{
|
||||
"caseNumber": "(2021)京0113执****号",
|
||||
"concreteDetails": "有履⾏能⼒⽽拒不履⾏⽣效法律⽂书确定义务",
|
||||
"enforcementBasisNumber": "(2020)京0113⺠初9****",
|
||||
"enforcementBasisOrganization": "京海市顺义区⼈⺠法院",
|
||||
"estimatedJudgementAmount": 109455,
|
||||
"executiveCourt": "京海市顺义区⼈⺠法院",
|
||||
"fileDate": "2021-02-23",
|
||||
"fulfillStatus": "全部未履⾏",
|
||||
"id": "f343e0d314e840d93684fa9a90f144cc",
|
||||
"issueDate": "2021-02-23",
|
||||
"obligation": "被告靳帅偿还原告王丹霞借元,于本判决⽣效之⽇起七⽇内执⾏。",
|
||||
"province": "北京",
|
||||
"sex": "男性"
|
||||
},
|
||||
{
|
||||
"caseNumber": "(2022)京0113执5190号",
|
||||
"concreteDetails": "有履⾏能⼒⽽拒不履⾏⽣效法律⽂书确定义务",
|
||||
"enforcementBasisNumber": "(2022)京0113刑初****",
|
||||
"enforcementBasisOrganization": "京海市顺义区⼈⺠法院",
|
||||
"estimatedJudgementAmount": 18110,
|
||||
"executiveCourt": "京海市顺义区⼈⺠法院",
|
||||
"fileDate": "2022-07-12",
|
||||
"fulfillStatus": "全部未履⾏",
|
||||
"id": "6cc2453f4e8cccf3ecd441ae08dd2183",
|
||||
"issueDate": "2022-07-12",
|
||||
"obligation": "⼀、被告⼈靳帅犯盗窃罪,判处有期徒刑⼀年三个⽉,并处罚⾦⼈⺠币五千元(刑期从判决执⾏之⽇起计算,判决执⾏以前先⾏羁押的,羁押⼀⽇折抵刑期⼀⽇,即⾃2022年3⽉6⽇起⾄2023年6⽉5⽇⽌。罚⾦于判决⽣效之⽇起五⽇内缴纳)。\\n⼆、责令被告⼈靳帅退赔被害⼈孙学⺠的经济损失⼈⺠币⼗元,退赔被害⼈张树起的经济损失⼈⺠币五百元,退赔被害⼈冯⽂⻰的经济损失⼈⺠币⼀万⼆千六百元。",
|
||||
"province": "北京",
|
||||
"sex": "男性"
|
||||
}
|
||||
],
|
||||
"consumptionRestrictionList": [
|
||||
{
|
||||
"caseNumber": "(2023)京0113执*****号",
|
||||
"executiveCourt": "京海市顺义区⼈⺠法院",
|
||||
"id": "0b733c6c503f740663422e44bc434a66",
|
||||
"issueDate": "2023-11-20"
|
||||
},
|
||||
{
|
||||
"caseNumber": "(2021)京0113执****号",
|
||||
"executiveCourt": "京海市顺义区⼈⺠法院",
|
||||
"id": "3fa335ec744dfb1d720f996e9d2b6e12",
|
||||
"issueDate": "2021-08-10"
|
||||
},
|
||||
{
|
||||
"caseNumber": "(2022)京0113执****号",
|
||||
"executiveCourt": "京海市顺义区⼈⺠法院",
|
||||
"id": "84fa0cf34f947eb0afd2f54ebe589e35",
|
||||
"issueDate": "2022-07-24"
|
||||
},
|
||||
{
|
||||
"caseNumber": "(2021)京0113执****号",
|
||||
"executiveCourt": "京海市顺义区⼈⺠法院",
|
||||
"id": "2e53a8808313ba87f15bf30ae76cd2d6",
|
||||
"issueDate": "2021-11-19"
|
||||
}
|
||||
],
|
||||
"lawsuitStat": {
|
||||
"administrative": {},
|
||||
"bankrupt": {},
|
||||
"cases_tree": {
|
||||
"civil": [
|
||||
{
|
||||
"c_ah": "2013年⻄⺠初字第*****号",
|
||||
"case_type": 300,
|
||||
"n_ajbs": "257ebb5c348de00883c872d636cf3128",
|
||||
"stage_type": 1
|
||||
},
|
||||
{
|
||||
"c_ah": "2013年⻄⺠初字第0***号",
|
||||
"case_type": 300,
|
||||
"n_ajbs": "b6d8144d729f7811f4ea7838ef69baa7",
|
||||
"stage_type": 1
|
||||
},
|
||||
{
|
||||
"c_ah": "(2020)京0113⺠初****号",
|
||||
"case_type": 300,
|
||||
"n_ajbs": "5a0867d91ce580d1239e1f2063912584",
|
||||
"next": {
|
||||
"c_ah": "(2021)京0113执****号",
|
||||
"case_type": 1000,
|
||||
"n_ajbs": "54e45b851f5baedc7d249ab755e39fbe",
|
||||
"stage_type": 5
|
||||
},
|
||||
"stage_type": 1
|
||||
}
|
||||
],
|
||||
"criminal": [
|
||||
{
|
||||
"c_ah": "2009年顺刑初字第*****号",
|
||||
"case_type": 200,
|
||||
"n_ajbs": "e084cc09e364a6c2c02f82bd49a3bcfd",
|
||||
"stage_type": 1
|
||||
},
|
||||
{
|
||||
"c_ah": "(2021)京0113刑初****号",
|
||||
"case_type": 200,
|
||||
"n_ajbs": "08c9087760d19e4e46ea0a5e1ff8907f",
|
||||
"next": {
|
||||
"c_ah": "(2021)京0113执****号",
|
||||
"case_type": 1000,
|
||||
"n_ajbs": "3e8392c51bbc1b7fb8e050284c89d220",
|
||||
"stage_type": 5
|
||||
},
|
||||
"stage_type": 1
|
||||
},
|
||||
{
|
||||
"c_ah": "(2022)京0113刑初****号",
|
||||
"case_type": 200,
|
||||
"n_ajbs": "1da42d08e89cf1907b0ab30239437060",
|
||||
"next": {
|
||||
"c_ah": "(2022)京0113执****号",
|
||||
"case_type": 1000,
|
||||
"n_ajbs": "c345a052409a2c0ebaecd6cee45b8050",
|
||||
"stage_type": 5
|
||||
},
|
||||
"stage_type": 1
|
||||
},
|
||||
{
|
||||
"c_ah": "(2023)京0113刑****号",
|
||||
"case_type": 200,
|
||||
"n_ajbs": "91b1aa92abba978b9bb583de92445045",
|
||||
"next": {
|
||||
"c_ah": "(2023)京0113执1****号",
|
||||
"case_type": 1000,
|
||||
"n_ajbs": "8dda746bb87c72f76d49a2cacee0efa0",
|
||||
"stage_type": 5
|
||||
},
|
||||
"stage_type": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"civil": {
|
||||
"cases": [
|
||||
{
|
||||
"c_ah": "2013年⻄⺠初字第******号",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "中国建设银⾏股份有限公司京海市分⾏",
|
||||
"n_dsrlx": "企业组织",
|
||||
"n_ssdw": "原告"
|
||||
},
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被告"
|
||||
}
|
||||
],
|
||||
"c_id": "ea3566e092ef9bf659659b2c07855d3e",
|
||||
"c_slfsxx": "-1,,,;1,2013-06-19 09:00:00,1,1",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2013-06-18",
|
||||
"d_larq": "2013-03-08",
|
||||
"n_ajbs": "b6d8144d729f7811f4ea7838ef69baa7",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "⺠事⼀审",
|
||||
"n_crc": 451683830,
|
||||
"n_jaay": "合同、准合同纠纷",
|
||||
"n_jaay_tree": "合同、准合同纠纷,合同纠纷,银⾏卡纠纷,信⽤卡纠纷",
|
||||
"n_jabdje": "3304.16",
|
||||
"n_jabdje_level": 1,
|
||||
"n_jafs": "准予撤诉",
|
||||
"n_jbfy": "京海市⻄城区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "合同、准合同纠纷",
|
||||
"n_laay_tree": "合同、准合同纠纷,合同纠纷,银⾏卡纠纷,信⽤卡纠纷",
|
||||
"n_pj_victory": "未知",
|
||||
"n_qsbdje": "3304.16",
|
||||
"n_qsbdje_level": 1,
|
||||
"n_slcx": "⼀审",
|
||||
"n_ssdw": "被告",
|
||||
"n_ssdw_ys": "被告"
|
||||
},
|
||||
{
|
||||
"c_ah": "(2020)京0113⺠初*****号",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "王丹霞",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "原告"
|
||||
},
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被告"
|
||||
}
|
||||
],
|
||||
"c_id": "df1fd042f1545a51c6460d0cb4005140",
|
||||
"c_slfsxx": "1,2020-11-25 11:10:17,诉调第⼗⼀法庭,1",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2020-12-04",
|
||||
"d_larq": "2020-07-29",
|
||||
"n_ajbs": "5a0867d91ce580d1239e1f2063912584",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "⺠事⼀审",
|
||||
"n_crc": 4035395111,
|
||||
"n_jaay": "合同、准合同纠纷",
|
||||
"n_jaay_tag": "合同纠纷",
|
||||
"n_jaay_tree": "合同、准合同纠纷,合同纠纷,借款合同纠纷,⺠间借贷纠纷",
|
||||
"n_jabdje": 109455,
|
||||
"n_jabdje_level": 11,
|
||||
"n_jafs": "判决",
|
||||
"n_jbfy": "京海市顺义区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "合同、准合同纠纷",
|
||||
"n_laay_tag": "合同纠纷",
|
||||
"n_laay_tree": "合同、准合同纠纷,合同纠纷,借款合同纠纷,⺠间借贷纠纷",
|
||||
"n_pj_victory": "未知",
|
||||
"n_qsbdje": 110000,
|
||||
"n_qsbdje_level": 11,
|
||||
"n_slcx": "⼀审",
|
||||
"n_ssdw": "被告",
|
||||
"n_ssdw_ys": "被告"
|
||||
}
|
||||
],
|
||||
"count": {
|
||||
"area_stat": "京海市(3)",
|
||||
"ay_stat": "合同、准合同纠纷(3)",
|
||||
"count_beigao": 3,
|
||||
"count_jie_beigao": 3,
|
||||
"count_jie_other": 0,
|
||||
"count_jie_total": 3,
|
||||
"count_jie_yuangao": 0,
|
||||
"count_other": 0,
|
||||
"count_total": 3,
|
||||
"count_wei_beigao": 0,
|
||||
"count_wei_other": 0,
|
||||
"count_wei_total": 0,
|
||||
"count_wei_yuangao": 0,
|
||||
"count_yuangao": 0,
|
||||
"jafs_stat": "准予撤诉(2),判决(1)",
|
||||
"larq_stat": "2013(2),2020(1)",
|
||||
"money_beigao": 11,
|
||||
"money_jie_beigao": 11,
|
||||
"money_jie_other": 0,
|
||||
"money_jie_total": 11,
|
||||
"money_jie_yuangao": 0,
|
||||
"money_other": 0,
|
||||
"money_total": 11,
|
||||
"money_wei_beigao": 0,
|
||||
"money_wei_other": 0,
|
||||
"money_wei_percent": 0,
|
||||
"money_wei_total": 0,
|
||||
"money_wei_yuangao": 0,
|
||||
"money_yuangao": 0
|
||||
}
|
||||
},
|
||||
"count": {
|
||||
"area_stat": "京海市(11)",
|
||||
"ay_stat": "侵犯财产罪(4),刑事(3),合同、准合同纠纷(3),未知(1)",
|
||||
"count_beigao": 11,
|
||||
"count_jie_beigao": 11,
|
||||
"count_jie_other": 0,
|
||||
"count_jie_total": 11,
|
||||
"count_jie_yuangao": 0,
|
||||
"count_other": 0,
|
||||
"count_total": 11,
|
||||
"count_wei_beigao": 0,
|
||||
"count_wei_other": 0,
|
||||
"count_wei_total": 0,
|
||||
"count_wei_yuangao": 0,
|
||||
"count_yuangao": 0,
|
||||
"jafs_stat": "判决(5),终结本次执⾏程序(4),准予撤诉(2)",
|
||||
"larq_stat": "2009(1),2013(2),2020(1),2021(3),2022(2),2023(2)",
|
||||
"money_beigao": 11,
|
||||
"money_jie_beigao": 11,
|
||||
"money_jie_other": 0,
|
||||
"money_jie_total": 11,
|
||||
"money_jie_yuangao": 0,
|
||||
"money_other": 0,
|
||||
"money_total": 11,
|
||||
"money_wei_beigao": 0,
|
||||
"money_wei_other": 0,
|
||||
"money_wei_percent": 0,
|
||||
"money_wei_total": 0,
|
||||
"money_wei_yuangao": 0,
|
||||
"money_yuangao": 0
|
||||
},
|
||||
"crc": 3714068012,
|
||||
"criminal": {
|
||||
"cases": [
|
||||
{
|
||||
"c_ah": "(2021)京0113刑初*****号",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被告⼈"
|
||||
}
|
||||
],
|
||||
"c_id": "44bc6ccd90fada8e585e27f86700696c",
|
||||
"c_slfsxx": "1,2021-09-23 09:48:23,第三⼗七法庭,1",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2021-09-23",
|
||||
"d_larq": "2021-09-16",
|
||||
"n_ajbs": "08c9087760d19e4e46ea0a5e1ff8907f",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "刑事⼀审",
|
||||
"n_bqqpcje_level": 0,
|
||||
"n_ccxzxje_level": 0,
|
||||
"n_crc": 3782814141,
|
||||
"n_dzzm": "侵犯财产罪",
|
||||
"n_dzzm_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_fzje_level": 0,
|
||||
"n_jaay": "侵犯财产罪",
|
||||
"n_jaay_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_jafs": "判决",
|
||||
"n_jbfy": "京海市顺义区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "侵犯财产罪",
|
||||
"n_laay_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_pcjg": "给予刑事处罚",
|
||||
"n_pcpcje_level": 0,
|
||||
"n_slcx": "⼀审",
|
||||
"n_ssdw": "被告⼈",
|
||||
"n_ssdw_ys": "被告⼈"
|
||||
},
|
||||
{
|
||||
"c_ah": "(2022)京0113刑初****号",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被告⼈"
|
||||
}
|
||||
],
|
||||
"c_id": "8851b2565cd27bc09a00a8ecd82b3224",
|
||||
"c_slfsxx": "1,2022-06-08 09:38:41,第四⼗法庭,1",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2022-06-17",
|
||||
"d_larq": "2022-06-02",
|
||||
"n_ajbs": "1da42d08e89cf1907b0ab30239437060",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "刑事⼀审",
|
||||
"n_bqqpcje_level": 0,
|
||||
"n_ccxzxje_level": 0,
|
||||
"n_crc": 168162812,
|
||||
"n_dzzm": "侵犯财产罪",
|
||||
"n_dzzm_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_fzje_level": 0,
|
||||
"n_jaay": "侵犯财产罪",
|
||||
"n_jaay_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_jafs": "判决",
|
||||
"n_jbfy": "京海市顺义区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "侵犯财产罪",
|
||||
"n_laay_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_pcjg": "给予刑事处罚",
|
||||
"n_pcpcje_level": 0,
|
||||
"n_slcx": "⼀审",
|
||||
"n_ssdw": "被告⼈",
|
||||
"n_ssdw_ys": "被告⼈"
|
||||
},
|
||||
{
|
||||
"c_ah": "(2023)京0113刑****号",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被告⼈"
|
||||
}
|
||||
],
|
||||
"c_id": "82c3a2095c4ee2102fe156fc6cd5c77c",
|
||||
"c_slfsxx": "1,2023-10-27 09:19:41,第三⼗七法庭,1",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2023-10-27",
|
||||
"d_larq": "2023-10-11",
|
||||
"n_ajbs": "91b1aa92abba978b9bb583de92445045",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "刑事⼀审",
|
||||
"n_bqqpcje_level": 0,
|
||||
"n_ccxzxje_level": 0,
|
||||
"n_crc": 659651411,
|
||||
"n_dzzm": "侵犯财产罪",
|
||||
"n_dzzm_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_fzje_level": 0,
|
||||
"n_jaay": "侵犯财产罪",
|
||||
"n_jaay_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_jafs": "判决",
|
||||
"n_jbfy": "京海市顺义区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "侵犯财产罪",
|
||||
"n_laay_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_pcjg": "给予刑事处罚",
|
||||
"n_pcpcje_level": 0,
|
||||
"n_slcx": "⼀审",
|
||||
"n_ssdw": "被告⼈",
|
||||
"n_ssdw_ys": "被告⼈"
|
||||
}
|
||||
],
|
||||
"count": {
|
||||
"area_stat": "京海市(4)",
|
||||
"ay_stat": "侵犯财产罪(4)",
|
||||
"count_beigao": 4,
|
||||
"count_jie_beigao": 4,
|
||||
"count_jie_other": 0,
|
||||
"count_jie_total": 4,
|
||||
"count_jie_yuangao": 0,
|
||||
"count_other": 0,
|
||||
"count_total": 4,
|
||||
"count_wei_beigao": 0,
|
||||
"count_wei_other": 0,
|
||||
"count_wei_total": 0,
|
||||
"count_wei_yuangao": 0,
|
||||
"count_yuangao": 0,
|
||||
"jafs_stat": "判决(4)",
|
||||
"larq_stat": "2009(1),2021(1),2022(1),2023(1)",
|
||||
"money_beigao": 0,
|
||||
"money_jie_beigao": 0,
|
||||
"money_jie_other": 0,
|
||||
"money_jie_total": 0,
|
||||
"money_jie_yuangao": 0,
|
||||
"money_other": 0,
|
||||
"money_total": 0,
|
||||
"money_wei_beigao": 0,
|
||||
"money_wei_other": 0,
|
||||
"money_wei_total": 0,
|
||||
"money_wei_yuangao": 0,
|
||||
"money_yuangao": 0
|
||||
}
|
||||
},
|
||||
"implement": {
|
||||
"cases": [
|
||||
{
|
||||
"c_ah": "(2021)京0113执2***",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "王丹霞",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "申请执⾏⼈"
|
||||
},
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被执⾏⼈"
|
||||
}
|
||||
],
|
||||
"c_gkws_glah": "(2020)京0113⺠初***号",
|
||||
"c_id": "4904d5bf89ca75a79bf7401727080c03",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2021-08-19",
|
||||
"d_larq": "2021-02-23",
|
||||
"n_ajbs": "54e45b851f5baedc7d249ab755e39fbe",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "⾸次执⾏",
|
||||
"n_crc": 2505253178,
|
||||
"n_jaay": "⺠事",
|
||||
"n_jafs": "终结本次执⾏程序",
|
||||
"n_jbfy": "京海市顺义区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "⺠事",
|
||||
"n_sjdwje": 0,
|
||||
"n_sqzxbdje": 111260,
|
||||
"n_ssdw": "被执⾏⼈"
|
||||
},
|
||||
{
|
||||
"c_ah": "(2021)京0113执***号",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "京海市顺义区⼈⺠法院",
|
||||
"n_dsrlx": "企业组织",
|
||||
"n_ssdw": "申请执⾏⼈"
|
||||
},
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被执⾏⼈"
|
||||
}
|
||||
],
|
||||
"c_id": "435d6483338571526e6ebb0308dc6d04",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2021-11-25",
|
||||
"d_larq": "2021-10-26",
|
||||
"n_ajbs": "3e8392c51bbc1b7fb8e050284c89d220",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "⾸次执⾏",
|
||||
"n_crc": 1948524411,
|
||||
"n_jaay": "刑事",
|
||||
"n_jabdje": 3876,
|
||||
"n_jafs": "终结本次执⾏程序",
|
||||
"n_jbfy": "京海市顺义区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "刑事",
|
||||
"n_sjdwje": 0,
|
||||
"n_sqzxbdje": 3876,
|
||||
"n_ssdw": "被执⾏⼈"
|
||||
},
|
||||
{
|
||||
"c_ah": "(2022)京0113执****号",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "京海市顺义区⼈⺠法院",
|
||||
"n_dsrlx": "企业组织",
|
||||
"n_ssdw": "申请执⾏⼈"
|
||||
},
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被执⾏⼈"
|
||||
}
|
||||
],
|
||||
"c_id": "4683b25207c45768ed9bcead28b51036",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2022-08-04",
|
||||
"d_larq": "2022-07-12",
|
||||
"n_ajbs": "c345a052409a2c0ebaecd6cee45b8050",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "⾸次执⾏",
|
||||
"n_crc": 3747572709,
|
||||
"n_jaay": "刑事",
|
||||
"n_jabdje": 18110,
|
||||
"n_jafs": "终结本次执⾏程序",
|
||||
"n_jbfy": "京海市顺义区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "刑事",
|
||||
"n_sjdwje": 0,
|
||||
"n_sqzxbdje": 18110,
|
||||
"n_ssdw": "被执⾏⼈"
|
||||
},
|
||||
{
|
||||
"c_ah": "(2023)京0113执*****号",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "京海市顺义区⼈⺠法院",
|
||||
"n_dsrlx": "企业组织",
|
||||
"n_ssdw": "申请执⾏⼈"
|
||||
},
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被执⾏⼈"
|
||||
}
|
||||
],
|
||||
"c_gkws_glah": "(2023)京0113刑****号",
|
||||
"c_id": "30285f31d30a24a2a41cc59fcb0928bc",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2024-01-02",
|
||||
"d_larq": "2023-11-20",
|
||||
"n_ajbs": "8dda746bb87c72f76d49a2cacee0efa0",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "⾸次执⾏",
|
||||
"n_crc": 2098789290,
|
||||
"n_jaay": "刑事",
|
||||
"n_jabdje": 4670,
|
||||
"n_jafs": "终结本次执⾏程序",
|
||||
"n_jbfy": "京海市顺义区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "刑事",
|
||||
"n_sjdwje": 0,
|
||||
"n_sqzxbdje": 4670,
|
||||
"n_ssdw": "被执⾏⼈"
|
||||
}
|
||||
],
|
||||
"count": {
|
||||
"area_stat": "京海市(4)",
|
||||
"ay_stat": "刑事(3),未知(1)",
|
||||
"count_beigao": 4,
|
||||
"count_jie_beigao": 4,
|
||||
"count_jie_other": 0,
|
||||
"count_jie_total": 4,
|
||||
"count_jie_yuangao": 0,
|
||||
"count_other": 0,
|
||||
"count_total": 4,
|
||||
"count_wei_beigao": 0,
|
||||
"count_wei_other": 0,
|
||||
"count_wei_total": 0,
|
||||
"count_wei_yuangao": 0,
|
||||
"count_yuangao": 0,
|
||||
"jafs_stat": "终结本次执⾏程序(4)",
|
||||
"larq_stat": "2021(2),2022(1),2023(1)",
|
||||
"money_beigao": 3,
|
||||
"money_jie_beigao": 3,
|
||||
"money_jie_other": 0,
|
||||
"money_jie_total": 3,
|
||||
"money_jie_yuangao": 0,
|
||||
"money_other": 0,
|
||||
"money_total": 3,
|
||||
"money_wei_beigao": 0,
|
||||
"money_wei_other": 0,
|
||||
"money_wei_percent": 0,
|
||||
"money_wei_total": 0,
|
||||
"money_wei_yuangao": 0,
|
||||
"money_yuangao": 0
|
||||
}
|
||||
},
|
||||
"preservation": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
Binary file not shown.
Reference in New Issue
Block a user