fadd
This commit is contained in:
443
docs/企业入驻-e签宝功能分析.md
Normal file
443
docs/企业入驻-e签宝功能分析.md
Normal file
@@ -0,0 +1,443 @@
|
||||
# 企业入驻 × e签宝 功能分析
|
||||
|
||||
> 文档基于 `hyapi-server` 与 `hyapi-consoleweb` 代码梳理,最后更新:2026-07-13
|
||||
|
||||
## 1. 功能概述
|
||||
|
||||
企业入驻是独立的**认证域(certification)**,通过自研 Go SDK(`internal/shared/esign`)对接 e签宝 OpenAPI,引导企业用户完成:
|
||||
|
||||
1. 填写并提交企业信息(含人工审核)
|
||||
2. e签宝机构实名认证
|
||||
3. 基于模板生成合作协议并电子签署
|
||||
4. 入驻完成(激活钱包、API 用户等)
|
||||
|
||||
**核心实现文件:**
|
||||
|
||||
| 层级 | 路径 |
|
||||
|------|------|
|
||||
| 业务编排 | `internal/application/certification/certification_application_service_impl.go` |
|
||||
| 状态机实体 | `internal/domains/certification/entities/certification.go` |
|
||||
| e签宝 SDK | `internal/shared/esign/` |
|
||||
| HTTP 路由 | `internal/infrastructure/http/routes/certification_routes.go` |
|
||||
| 前端主流程 | `hyapi-consoleweb/src/pages/certification/` |
|
||||
|
||||
---
|
||||
|
||||
## 2. 涉及数据表
|
||||
|
||||
表结构通过 GORM `AutoMigrate` 自动创建(`internal/app/app.go`),无独立 SQL migration。
|
||||
|
||||
### 2.1 认证域(核心)
|
||||
|
||||
#### `certifications` — 认证主表(聚合根)
|
||||
|
||||
每个用户最多一条认证记录(`user_id` 唯一),贯穿全流程并存储 e签宝 关键 ID/链接。
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | varchar(64) | 认证申请 ID |
|
||||
| `user_id` | varchar(36) | 用户 ID(唯一) |
|
||||
| `status` | varchar(50) | 认证状态(见第 3 节) |
|
||||
| `info_submitted_at` | datetime | 企业信息审核通过时间 |
|
||||
| `enterprise_verified_at` | datetime | e签宝 企业认证完成时间 |
|
||||
| `contract_applied_at` | datetime | 申请签署时间 |
|
||||
| `contract_signed_at` | datetime | 签署完成时间 |
|
||||
| `completed_at` | datetime | 入驻完成时间 |
|
||||
| `contract_file_created_at` | datetime | 合同文件生成时间(用于 50 分钟过期判断) |
|
||||
| `auth_flow_id` | varchar(500) | e签宝 企业认证流程 ID |
|
||||
| `auth_url` | varchar(500) | e签宝 企业认证页面链接 |
|
||||
| `contract_code` | varchar(100) | 合作协议编号(如 `HYDATA-20260713-R123456`) |
|
||||
| `contract_file_id` | varchar(500) | e签宝 合同文件 ID |
|
||||
| `esign_flow_id` | varchar(500) | e签宝 签署流程 ID |
|
||||
| `contract_url` | varchar(500) | 合同预览/下载 URL |
|
||||
| `contract_sign_url` | varchar(500) | 合同签署链接 |
|
||||
| `failure_reason` / `failure_message` | varchar/text | 失败原因与详情 |
|
||||
| `retry_count` | int | 重试次数(上限 3) |
|
||||
| `last_transition_*` | — | 状态转换审计字段 |
|
||||
|
||||
#### `enterprise_info_submit_records` — 企业信息提交与人工审核
|
||||
|
||||
用户每次提交企业信息时写入,承载审核材料与审核结果。
|
||||
|
||||
| 字段分组 | 主要字段 | 说明 |
|
||||
|----------|----------|------|
|
||||
| 企业四要素 | `company_name`、`unified_social_code`、`legal_person_*`、`enterprise_address` | 企业基本信息 |
|
||||
| 授权代表 | `authorized_rep_name/id/phone`、`authorized_rep_id_image_urls` | 授权代表信息(身份证图片为 JSON 数组) |
|
||||
| 资质材料 | `business_license_image_url`、`office_place_image_urls`、`api_usage`、`scenario_attachment_urls` | 营业执照、办公场地、业务场景说明及附件 |
|
||||
| 前置校验 | `status` | `submitted` / `verified` / `failed`(西部数据三真等) |
|
||||
| 人工审核 | `manual_review_status` | `pending` / `approved` / `rejected` |
|
||||
| 审核人 | `manual_reviewer_id`、`manual_review_remark`、`manual_reviewed_at` | 管理员审核信息 |
|
||||
|
||||
#### `esign_contract_generate_records` — 合同生成记录
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `certification_id`、`user_id` | 关联认证与用户 |
|
||||
| `template_id` | e签宝 模板 ID |
|
||||
| `contract_file_id`、`contract_url`、`contract_name` | 生成结果 |
|
||||
| `status` | `success` / `failed` |
|
||||
| `fill_time` | 填单时间 |
|
||||
|
||||
#### `esign_contract_sign_records` — 合同签署记录
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `esign_flow_id`、`contract_file_id` | e签宝 流程与文件 |
|
||||
| `sign_url`、`sign_short_url` | 签署链接 |
|
||||
| `signed_file_url` | 已签署文件 URL |
|
||||
| `status` | `pending` / `signing` / `success` / `failed` / `expired` |
|
||||
| `signer_name/phone/id_card` | 签署人信息 |
|
||||
| `request_at`、`signed_at`、`expired_at` | 时间戳(默认 7 天过期) |
|
||||
|
||||
### 2.2 用户域(认证完成后写入)
|
||||
|
||||
#### `enterprise_infos` — 已认证企业信息
|
||||
|
||||
认证通过后从提交记录同步,供充值、开票、API 调用等使用;与用户一对一。
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `user_id` | 用户 ID(唯一索引) |
|
||||
| `company_name` | 企业名称 |
|
||||
| `unified_social_code` | 统一社会信用代码 |
|
||||
| `legal_person_name/id/phone` | 法人信息 |
|
||||
| `enterprise_address` | 企业地址 |
|
||||
|
||||
#### `contract_infos` — 已签署合同
|
||||
|
||||
签署完成后写入,一个企业可有多份合同。
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `enterprise_info_id`、`user_id` | 关联企业与用户 |
|
||||
| `contract_code` | 合同编号(唯一) |
|
||||
| `contract_name` | 合同名称 |
|
||||
| `contract_type` | `cooperation`(入驻)/ `resign`(补签) |
|
||||
| `contract_file_id` | e签宝 文件 ID |
|
||||
| `contract_file_url` | 七牛云永久存储链接 |
|
||||
|
||||
### 2.3 入驻完成后关联表(非 e签宝 直接写入,但属完成动作)
|
||||
|
||||
| 表名 | 说明 |
|
||||
|------|------|
|
||||
| `wallets` | 创建用户钱包 |
|
||||
| `api_users` | 创建 API 调用身份 |
|
||||
| `users` | 标记用户已完成认证(`CompleteCertification`) |
|
||||
|
||||
---
|
||||
|
||||
## 3. 认证状态机
|
||||
|
||||
```
|
||||
pending
|
||||
└─► info_pending_review(提交企业信息,等待人工审核)
|
||||
├─► info_rejected(审核拒绝 / e签宝 认证失败)
|
||||
└─► info_submitted(审核通过,持有 auth_url)
|
||||
└─► enterprise_verified(e签宝 机构实名成功,已生成合同)
|
||||
└─► contract_applied(已申请签署,持有 contract_sign_url)
|
||||
├─► contract_signed ──► completed(入驻完成)
|
||||
├─► contract_rejected(拒签,可重试)
|
||||
└─► contract_expired(超时,可重试)
|
||||
```
|
||||
|
||||
| 状态值 | 中文 | 进度 | 用户操作 |
|
||||
|--------|------|------|----------|
|
||||
| `pending` | 待认证 | 0% | 填写企业信息 |
|
||||
| `info_pending_review` | 企业信息待审核 | 15% | 等待管理员 |
|
||||
| `info_submitted` | 已提交企业信息 | 25% | 完成 e签宝 企业认证 |
|
||||
| `enterprise_verified` | 已企业认证 | 50% | 申请签署合同 |
|
||||
| `contract_applied` | 已申请签署 | 75% | 在 iframe 中签署 |
|
||||
| `contract_signed` | 已签署合同 | 100% | 等待系统处理 |
|
||||
| `completed` | 认证完成 | 100% | 可使用全部功能 |
|
||||
| `info_rejected` | 企业信息被拒绝 | — | 修正后重新提交 |
|
||||
| `contract_rejected` | 合同被拒签 | — | 重新申请 |
|
||||
| `contract_expired` | 合同签署超时 | — | 重新申请 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 主要业务流程
|
||||
|
||||
### 4.1 流程总览
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[用户注册/登录] --> B[POST /enterprise-info 提交企业信息]
|
||||
B --> C[短信验证 + 西部数据三真校验]
|
||||
C --> D[状态: info_pending_review]
|
||||
D --> E{管理员审核}
|
||||
E -->|拒绝| F[info_rejected]
|
||||
E -->|通过| G[调用 e签宝 /v3/org-auth-url]
|
||||
G --> H[状态: info_submitted<br/>写入 auth_url]
|
||||
H --> I[iframe 完成机构实名]
|
||||
I --> J{确认方式}
|
||||
J -->|回调 AUTH_PASS| K[enterprise_verified]
|
||||
J -->|轮询 GET /details 或 POST /confirm-auth| K
|
||||
K --> L[模板填单生成合同 PDF]
|
||||
L --> M[POST /apply-contract 申请签署]
|
||||
M --> N[状态: contract_applied]
|
||||
N --> O[iframe 签署合同]
|
||||
O --> P{确认方式}
|
||||
P -->|POST /confirm-sign 轮询| Q[contract_signed → completed]
|
||||
Q --> R[下载已签文件 → 七牛 → contract_infos]
|
||||
R --> S[创建钱包 + API 用户]
|
||||
```
|
||||
|
||||
### 4.2 步骤一:提交企业信息
|
||||
|
||||
**接口:** `POST /api/v1/certifications/enterprise-info`(JWT + 日限流)
|
||||
|
||||
**处理逻辑(`SubmitEnterpriseInfo`):**
|
||||
|
||||
1. 防重复:若已有 `info_pending_review` 记录则拒绝重复提交
|
||||
2. 短信验证码校验(场景 `SMSSceneCertification`)
|
||||
3. 统一社会信用代码唯一性检查(`enterprise_infos` + `enterprise_info_submit_records`)
|
||||
4. 字段格式校验 + **西部数据三真**(`ValidateWithWestdex`)
|
||||
5. 写入 `enterprise_info_submit_records`(状态 `verified`)
|
||||
6. 认证状态转为 `info_pending_review`
|
||||
7. 企业微信通知管理员待审核
|
||||
|
||||
> **此阶段不调用 e签宝。**
|
||||
|
||||
**辅助接口:**
|
||||
|
||||
- `POST /ocr/business-license` — 营业执照 OCR
|
||||
- `POST /upload` — 认证材料上传七牛云
|
||||
|
||||
### 4.3 步骤二:管理员人工审核
|
||||
|
||||
**接口:**
|
||||
|
||||
- `GET /api/v1/certifications/admin/submit-records` — 列表
|
||||
- `GET /api/v1/certifications/admin/submit-records/:id` — 详情
|
||||
- `POST /api/v1/certifications/admin/submit-records/:id/approve` — 通过
|
||||
- `POST /api/v1/certifications/admin/submit-records/:id/reject` — 拒绝
|
||||
|
||||
**审核通过(`AdminApproveSubmitRecord`):**
|
||||
|
||||
1. 调用 `esignClient.GenerateEnterpriseAuth()` → e签宝 `POST /v3/org-auth-url`
|
||||
2. 若企业已在 e签宝 实名(错误含「已实名」),则 `QueryOrgIdentityInfo` 后直接 `completeEnterpriseVerification`
|
||||
3. 否则状态 → `info_submitted`,写入 `auth_flow_id`、`auth_url`
|
||||
4. 提交记录 `manual_review_status` → `approved`
|
||||
|
||||
**经办人信息:** 当前使用**法人**作为 e签宝 经办人(`TransactorName = LegalPersonName`),未使用授权代表字段。
|
||||
|
||||
### 4.4 步骤三:e签宝 企业实名认证
|
||||
|
||||
**前端:** `EnterpriseVerify.vue` 通过 iframe 加载 `metadata.auth_url`
|
||||
|
||||
**完成确认(双路径):**
|
||||
|
||||
| 路径 | 触发 | 处理 |
|
||||
|------|------|------|
|
||||
| 服务端回调 | `POST /api/v1/certifications/callbacks/esign`,`action=AUTH_PASS` 且 `authType=ORG` | `HandleEsignCallback` → `completeEnterpriseVerification` |
|
||||
| 前端轮询 | `GET /details` 或 `POST /confirm-auth` | 查询 `GET /v3/organizations/identity-info`,`realnameStatus==1` 时推进 |
|
||||
|
||||
**Redirect:** 用户完成后跳转 `auth.redirect_url`(如 `/certification/callback/auth`),`IframeCallback.vue` 通过 `postMessage` 通知父页面刷新状态。
|
||||
|
||||
**`completeEnterpriseVerification` 动作:**
|
||||
|
||||
1. 状态 → `enterprise_verified`
|
||||
2. 企业信息写入 `enterprise_infos`
|
||||
3. 调用 `FillTemplate` 填控件生成合同(见 4.5)
|
||||
4. 保存 `contract_file_id`、`contract_url`、`contract_code`
|
||||
|
||||
### 4.5 步骤四:生成合作协议
|
||||
|
||||
**e签宝 API:** `POST /v3/files/create-by-doc-template`
|
||||
|
||||
**模板控件映射:**
|
||||
|
||||
| 控件 key | 填入内容 |
|
||||
|----------|----------|
|
||||
| `jfqym` / `jfqym2` | 企业名称 |
|
||||
| `jfsqdb` | 授权代表姓名(无则用法人的) |
|
||||
| `jftyshxydm` | 统一社会信用代码 |
|
||||
| `jflxdz` | 企业地址 |
|
||||
| `xybh` | 协议编号 |
|
||||
| `qsrq1` / `qsrq2` / `qsrq3` | 签署日期 |
|
||||
|
||||
**合同编号规则:** `HYDATA-YYYYMMDD-R{6位随机数}`(R = 入驻)
|
||||
|
||||
**过期策略:** 合同文件生成后 **50 分钟**过期,`GET /details` 时会自动重新生成(`IsContractFileNeedUpdate`)。
|
||||
|
||||
### 4.6 步骤五:申请并签署合同
|
||||
|
||||
**接口:** `POST /api/v1/certifications/apply-contract`
|
||||
|
||||
**前置条件:** 状态为 `enterprise_verified`
|
||||
|
||||
**处理逻辑:**
|
||||
|
||||
1. `CreateSignFlow` → `POST /v3/sign-flow/create-by-file`(甲方手动签 + 乙方自动盖章)
|
||||
2. `GetSignURL` → `POST /v3/sign-flow/{id}/sign-url`
|
||||
3. 状态 → `contract_applied`,保存 `esign_flow_id`、`contract_sign_url`
|
||||
|
||||
**签署确认:**
|
||||
|
||||
- 前端 iframe 加载 `contract_sign_url`,完成后 redirect 到 `sign.redirect_url`
|
||||
- `POST /confirm-sign` 或 `GET /details` 轮询 `QuerySignFlowDetail`
|
||||
|
||||
**签署状态码(业务使用):**
|
||||
|
||||
| SignFlowStatus | 含义 | 系统动作 |
|
||||
|----------------|------|----------|
|
||||
| `2` | 签署完成 | → `contract_signed` → `completed` |
|
||||
| `7` | 拒签 | → `contract_rejected` |
|
||||
| `5` | 过期 | → `contract_expired` |
|
||||
| 其他 | 签署中 | 继续轮询 |
|
||||
|
||||
### 4.7 步骤六:入驻完成
|
||||
|
||||
**`handleContractAfterSignComplete`:**
|
||||
|
||||
1. `DownloadSignedFile` 从 e签宝 下载已签 PDF
|
||||
2. 上传七牛云获取永久 URL
|
||||
3. 写入 `contract_infos`
|
||||
4. `completeUserActivationWithoutContract`:
|
||||
- 创建 `wallets`
|
||||
- 创建 `api_users`
|
||||
- 用户域标记认证完成
|
||||
- 企业微信通知「企业认证成功」
|
||||
|
||||
---
|
||||
|
||||
## 5. API 端点汇总
|
||||
|
||||
### 5.1 本系统 REST API
|
||||
|
||||
前缀:`/api/v1/certifications`
|
||||
|
||||
| 方法 | 路径 | 认证 | 说明 |
|
||||
|------|------|------|------|
|
||||
| GET | `/details` | JWT | 获取/创建认证详情,自动同步 e签宝 状态 |
|
||||
| POST | `/enterprise-info` | JWT + 日限流 | 提交企业信息 |
|
||||
| POST | `/ocr/business-license` | JWT | 营业执照 OCR |
|
||||
| POST | `/upload` | JWT | 认证材料上传 |
|
||||
| POST | `/confirm-auth` | JWT | 轮询确认企业实名 |
|
||||
| POST | `/apply-contract` | JWT | 申请合同签署 |
|
||||
| POST | `/confirm-sign` | JWT | 轮询确认签署状态 |
|
||||
| POST | `/callbacks/esign` | 无 | e签宝 服务端回调 |
|
||||
| GET | `` | JWT 管理员 | 认证列表 |
|
||||
| POST | `/admin/complete-without-contract` | 管理员 | 跳过合同直接完成认证 |
|
||||
| POST | `/admin/transition-status` | 管理员 | 手动变更状态 |
|
||||
| GET/POST | `/admin/submit-records[...]` | 管理员 | 提交记录 CRUD + 审核 |
|
||||
|
||||
### 5.2 e签宝 OpenAPI(SDK 封装)
|
||||
|
||||
| 方法 | 路径 | 用途 |
|
||||
|------|------|------|
|
||||
| POST | `/v3/org-auth-url` | 获取机构认证页面链接 |
|
||||
| GET | `/v3/organizations/identity-info` | 查询机构是否已实名 |
|
||||
| POST | `/v3/files/create-by-doc-template` | 模板填单生成 PDF |
|
||||
| POST | `/v3/sign-flow/create-by-file` | 创建签署流程 |
|
||||
| POST | `/v3/sign-flow/{id}/sign-url` | 获取签署链接 |
|
||||
| GET | `/v3/sign-flow/{id}/detail` | 查询签署状态 |
|
||||
| GET | `/v3/sign-flow/{id}/file-download-url` | 下载已签署文件 |
|
||||
|
||||
### 5.3 前端路由
|
||||
|
||||
| 路由 | 说明 |
|
||||
|------|------|
|
||||
| `/profile/certification` | 企业入驻主流程(5 步) |
|
||||
| `/certification/callback/auth` | e签宝 认证完成 redirect |
|
||||
| `/certification/callback/sign` | e签宝 签署完成 redirect |
|
||||
| `/admin/certification-reviews` | 管理端审核列表 |
|
||||
|
||||
---
|
||||
|
||||
## 6. e签宝 配置项
|
||||
|
||||
配置文件:`config.yaml` / `configs/env.{development,production}.yaml`
|
||||
|
||||
```yaml
|
||||
esign:
|
||||
app_id: "..." # 应用 ID
|
||||
app_secret: "..." # 应用密钥(API 签名)
|
||||
server_url: "..." # 沙箱: https://smlopenapi.esign.cn
|
||||
# 生产: https://openapi.esign.cn
|
||||
template_id: "..." # 合作协议模板 ID
|
||||
contract:
|
||||
name: "海宇数据-合作协议"
|
||||
expire_days: 7 # 签署链接有效期(天)
|
||||
retry_count: 3
|
||||
auth:
|
||||
org_auth_modes: ["PSN_MOBILE3"]
|
||||
default_auth_mode: "PSN_MOBILE3"
|
||||
psn_auth_modes: ["PSN_MOBILE3", "PSN_IDCARD"]
|
||||
willingness_auth_modes: ["CODE_SMS"]
|
||||
redirect_url: "https://console.haiyudata.com/certification/callback/auth"
|
||||
sign:
|
||||
auto_finish: true
|
||||
sign_field_style: 1
|
||||
client_type: "ALL"
|
||||
redirect_url: "https://console.haiyudata.com/certification/callback/sign"
|
||||
```
|
||||
|
||||
Go 结构:`config.EsignConfig` → 注入为 `esign.Config`(`container.go`)。
|
||||
|
||||
---
|
||||
|
||||
## 7. 架构与代码组织
|
||||
|
||||
```
|
||||
hyapi-server/
|
||||
├── internal/
|
||||
│ ├── application/certification/ # 应用服务(主业务编排)
|
||||
│ ├── domains/certification/ # 领域模型、状态机、枚举
|
||||
│ ├── domains/user/entities/ # enterprise_infos, contract_infos
|
||||
│ ├── infrastructure/
|
||||
│ │ ├── http/handlers/certification_handler.go
|
||||
│ │ ├── http/routes/certification_routes.go
|
||||
│ │ └── database/repositories/certification/
|
||||
│ └── shared/esign/ # e签宝 SDK
|
||||
│ ├── client.go # 统一入口
|
||||
│ ├── orgauth_service.go # 机构认证
|
||||
│ ├── org_identity.go # 实名查询
|
||||
│ ├── template_service.go # 模板填单
|
||||
│ ├── signflow_service.go # 签署流程
|
||||
│ └── fileops_service.go # 文件下载/状态查询
|
||||
|
||||
hyapi-consoleweb/
|
||||
└── src/pages/certification/
|
||||
├── index.vue # 主流程容器
|
||||
├── IframeCallback.vue # e签宝 redirect 回调
|
||||
└── components/
|
||||
├── EnterpriseInfo.vue # 填写企业信息
|
||||
├── ManualReviewPending.vue # 人工审核等待
|
||||
├── EnterpriseVerify.vue # e签宝 认证 iframe
|
||||
├── ContractPreview.vue / ContractSign.vue
|
||||
└── CertificationComplete.vue
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 注意事项与已知问题
|
||||
|
||||
1. **回调验签未启用**
|
||||
`HandleEsignCallback` 中 `esign.VerifySignature` 已被注释,生产环境存在安全风险。
|
||||
|
||||
2. **遗留代码未接入**
|
||||
`internal/infrastructure/external/esign/certification_esign_service.go` 为模拟实现,未注册到 DI;`CertificationWorkflowOrchestrator` 部分方法为 stub。
|
||||
|
||||
3. **双路径状态同步**
|
||||
企业认证支持「e签宝 回调」与「前端轮询」两条路径;合同签署主要依赖 `confirm-sign` / `GET /details` 主动查询,回调仅处理 `AUTH_PASS`。
|
||||
|
||||
4. **经办人默认法人**
|
||||
生成认证链接时经办人使用法人信息,授权代表字段仅用于合同模板 `jfsqdb` 控件。
|
||||
|
||||
5. **签署状态码注释不一致**
|
||||
SDK 注释与业务代码对 `SignFlowStatus` 的含义存在差异,排查问题时以 `checkAndUpdateSignStatus` 中的判断为准(`2`=成功,`7`=拒签,`5`=过期)。
|
||||
|
||||
6. **与 API 实名产品无关**
|
||||
`IVYZSQ0E` / `IVYZFO5K` 等数据 API 的身份证实名校验属于 API 调用产品,与本入驻流程无关。
|
||||
|
||||
---
|
||||
|
||||
## 9. 入驻完成后的系统能力
|
||||
|
||||
认证状态为 `completed` 后,用户可使用:
|
||||
|
||||
- 钱包充值(`Wallet.vue`)
|
||||
- 开票申请(`Invoice.vue`)
|
||||
- API 调用(需 `api_users`)
|
||||
- IP 白名单等需认证页面(`useCertification` / `CertificationBanner` 检查 `isCertified`)
|
||||
619
docs/多平台签署整合实现方案.md
Normal file
619
docs/多平台签署整合实现方案.md
Normal file
@@ -0,0 +1,619 @@
|
||||
# 多平台签署整合实现方案(e签宝 + 法大大)
|
||||
|
||||
> 基于现有企业入驻流程的改造设计,供评审后决定实施范围与优先级。
|
||||
> 关联文档:[企业入驻-e签宝功能分析.md](./企业入驻-e签宝功能分析.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
### 1.1 现状
|
||||
|
||||
当前企业入驻的**企业实名认证 + 合同签署**全流程绑定在 **e签宝** 单一平台:
|
||||
|
||||
- 管理员审核通过后,直接调用 e签宝 `/v3/org-auth-url` 生成 `auth_url`
|
||||
- 企业认证、模板填单、签署流程、回调/轮询均硬编码在 `CertificationApplicationServiceImpl` + `internal/shared/esign`
|
||||
|
||||
### 1.2 目标
|
||||
|
||||
1. `certifications` 主表新增 **`sign_platform`(签署平台)** 字段
|
||||
2. 管理员审核通过后,**由用户选择签署平台**(e签宝 / 法大大)
|
||||
3. 根据所选平台发放对应的**企业认证链接**与**合同签署链接**
|
||||
4. 尽量复用现有状态机,降低对已完成用户的影响
|
||||
|
||||
### 1.3 非目标(本期可不做)
|
||||
|
||||
- 同一用户在不同平台各签一份合同
|
||||
- 签署中途切换平台(选定后锁定,除非管理员重置)
|
||||
- 替换掉 e签宝 的全部历史数据
|
||||
|
||||
---
|
||||
|
||||
## 2. 改造后流程设计
|
||||
|
||||
### 2.1 新增步骤:选择签署平台
|
||||
|
||||
在「人工审核通过」与「企业认证 iframe」之间插入一步:
|
||||
|
||||
```
|
||||
填写企业信息
|
||||
→ 人工审核(info_pending_review)
|
||||
→ 【新增】选择签署平台(info_submitted 前/后,见 2.2)
|
||||
→ 企业实名认证(info_submitted)
|
||||
→ 合同预览与签署(enterprise_verified → contract_applied → completed)
|
||||
```
|
||||
|
||||
### 2.2 平台选择时机(推荐方案)
|
||||
|
||||
| 方案 | 时机 | 优点 | 缺点 |
|
||||
|------|------|------|------|
|
||||
| **A(推荐)** | 审核通过后、生成认证链接前,用户主动选择 | 符合需求;认证与签署同一平台;不浪费第三方调用 | 多一个前端步骤 |
|
||||
| B | 提交企业信息时一并选择 | 步骤少 | 审核拒绝后需重选;用户尚不清楚平台差异 |
|
||||
| C | 仅合同阶段选择,认证仍固定 e签宝 | 改动小 | 法大大合同要求法大大侧企业实名,链路不完整 |
|
||||
|
||||
**推荐采用方案 A**,状态流转如下:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[info_pending_review] --> B[管理员审核通过]
|
||||
B --> C{sign_platform 已选择?}
|
||||
C -->|否| D[前端展示平台选择页<br/>POST /select-sign-platform]
|
||||
D --> E[写入 sign_platform]
|
||||
E --> F[按平台生成 auth_url]
|
||||
C -->|是| F
|
||||
F --> G[info_submitted + auth_url]
|
||||
G --> H[iframe 企业认证]
|
||||
H --> I[enterprise_verified + 生成合同]
|
||||
I --> J[apply-contract 按平台发起签署]
|
||||
J --> K[completed]
|
||||
```
|
||||
|
||||
### 2.3 平台锁定规则
|
||||
|
||||
| 规则 | 说明 |
|
||||
|------|------|
|
||||
| 可选时机 | `info_pending_review` 审核通过后 ~ `info_submitted` 之前(`sign_platform` 为空) |
|
||||
| 锁定时机 | 用户确认平台后写入 `sign_platform`,并生成该平台认证链接 |
|
||||
| 不可变更 | 进入 `info_submitted` 后不允许用户自行切换 |
|
||||
| 管理员重置 | 仅管理员可将 `sign_platform` 置空并重置到待选平台状态(见 4.3) |
|
||||
|
||||
---
|
||||
|
||||
## 3. 数据库变更
|
||||
|
||||
### 3.1 `certifications` 主表
|
||||
|
||||
新增字段:
|
||||
|
||||
```sql
|
||||
ALTER TABLE certifications
|
||||
ADD COLUMN sign_platform VARCHAR(20) DEFAULT NULL
|
||||
COMMENT '签署平台: esign | fadada,NULL 表示尚未选择';
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `sign_platform` | varchar(20), nullable | `esign` / `fadada` / `NULL` |
|
||||
|
||||
**现有 e签宝 字段复用策略(推荐不重命名):**
|
||||
|
||||
| 现有字段 | 多平台语义 |
|
||||
|----------|------------|
|
||||
| `auth_flow_id` | 第三方企业认证流程 ID(平台无关) |
|
||||
| `auth_url` | 第三方企业认证页面 URL |
|
||||
| `contract_file_id` | 第三方合同文件 ID |
|
||||
| `esign_flow_id` | 第三方签署流程 ID(建议后续迭代改名为 `sign_flow_id`) |
|
||||
| `contract_sign_url` | 第三方签署页面 URL |
|
||||
|
||||
> 首期可保留 `esign_flow_id` 列名不动,避免大面积迁移;在代码层用 `SignFlowID` 别名理解即可。
|
||||
|
||||
### 3.2 子记录表(建议同步加平台字段)
|
||||
|
||||
| 表 | 变更 |
|
||||
|----|------|
|
||||
| `esign_contract_generate_records` | 新增 `sign_platform varchar(20)`;后期可重命名为 `contract_generate_records` |
|
||||
| `esign_contract_sign_records` | 新增 `sign_platform varchar(20)`;后期可重命名为 `contract_sign_records` |
|
||||
| `contract_infos` | 新增 `sign_platform varchar(20)`,便于历史合同溯源 |
|
||||
|
||||
### 3.3 枚举定义(Go)
|
||||
|
||||
```go
|
||||
// internal/domains/certification/enums/sign_platform.go
|
||||
type SignPlatform string
|
||||
|
||||
const (
|
||||
SignPlatformEsign SignPlatform = "esign"
|
||||
SignPlatformFadada SignPlatform = "fadada"
|
||||
)
|
||||
|
||||
func IsValidSignPlatform(p SignPlatform) bool { ... }
|
||||
```
|
||||
|
||||
### 3.4 历史数据迁移
|
||||
|
||||
```sql
|
||||
-- 已有认证记录默认归属 e签宝
|
||||
UPDATE certifications SET sign_platform = 'esign' WHERE sign_platform IS NULL;
|
||||
|
||||
UPDATE esign_contract_generate_records SET sign_platform = 'esign' WHERE sign_platform IS NULL;
|
||||
UPDATE esign_contract_sign_records SET sign_platform = 'esign' WHERE sign_platform IS NULL;
|
||||
UPDATE contract_infos SET sign_platform = 'esign' WHERE sign_platform IS NULL;
|
||||
```
|
||||
|
||||
对于**进行中**且 `sign_platform IS NULL` 的记录:迁移后视为已选 e签宝,与当前行为一致。
|
||||
|
||||
---
|
||||
|
||||
## 4. 后端架构改造
|
||||
|
||||
### 4.1 核心思路:签署平台抽象层(Strategy + Registry)
|
||||
|
||||
将 e签宝 特有逻辑从 `CertificationApplicationServiceImpl` 中抽离,定义统一接口:
|
||||
|
||||
```
|
||||
internal/domains/certification/ports/sign_platform_provider.go # 接口(端口)
|
||||
internal/infrastructure/external/esign/esign_provider.go # e签宝 实现
|
||||
internal/infrastructure/external/fadada/fadada_provider.go # 法大大 实现
|
||||
internal/infrastructure/external/signplatform/registry.go # 按 sign_platform 路由
|
||||
```
|
||||
|
||||
**接口草案:**
|
||||
|
||||
```go
|
||||
type SignPlatformProvider interface {
|
||||
Platform() enums.SignPlatform
|
||||
|
||||
// 企业实名
|
||||
GenerateEnterpriseAuth(ctx context.Context, req *EnterpriseAuthRequest) (*AuthLinkResult, error)
|
||||
QueryOrgVerified(ctx context.Context, req *OrgIdentityQuery) (bool, error)
|
||||
|
||||
// 合同
|
||||
GenerateContractFile(ctx context.Context, req *ContractGenerateRequest) (*ContractFileResult, error)
|
||||
CreateSignFlow(ctx context.Context, req *SignFlowCreateRequest) (*SignFlowResult, error)
|
||||
QuerySignStatus(ctx context.Context, flowID string) (*SignStatusResult, error)
|
||||
DownloadSignedFiles(ctx context.Context, flowID string) ([]*SignedFile, error)
|
||||
|
||||
// 回调
|
||||
ParseCallback(ctx context.Context, raw []byte, headers map[string]string) (*CallbackEvent, error)
|
||||
VerifyCallback(ctx context.Context, raw []byte, headers map[string]string) error
|
||||
}
|
||||
```
|
||||
|
||||
`CertificationApplicationServiceImpl` 中所有 `s.esignClient.XXX()` 改为:
|
||||
|
||||
```go
|
||||
provider := s.platformRegistry.Get(cert.SignPlatform)
|
||||
provider.GenerateEnterpriseAuth(...)
|
||||
```
|
||||
|
||||
### 4.2 需改造的后端触点(按优先级)
|
||||
|
||||
| 方法 | 现状 | 改造 |
|
||||
|------|------|------|
|
||||
| `AdminApproveSubmitRecord` | 直接 `esignClient.GenerateEnterpriseAuth` | 审核通过后**不立即**生成链接;仅将状态保持在「待选平台」或新增中间状态 |
|
||||
| **新增** `SelectSignPlatform` | 无 | 用户选择平台 → 写 `sign_platform` → 调 provider 生成 `auth_url` → `info_submitted` |
|
||||
| `ConfirmAuth` / `checkAndCompleteEnterpriseVerification` | 查 e签宝 identity | 调当前 provider 的 `QueryOrgVerified` |
|
||||
| `generateAndAddContractFile` | e签宝 FillTemplate | 调 provider 的 `GenerateContractFile` |
|
||||
| `ApplyContract` / `generateContractAndSignURL` | e签宝 SignFlow | 调 provider 的 `CreateSignFlow` |
|
||||
| `checkAndUpdateSignStatus` | e签宝 QuerySignFlowDetail | 调 provider 的 `QuerySignStatus` |
|
||||
| `handleContractAfterSignComplete` | e签宝 DownloadSignedFile | 调 provider 的 `DownloadSignedFiles` |
|
||||
| `HandleEsignCallback` | 仅处理 e签宝 | 拆为 `/callbacks/esign` + `/callbacks/fadada`,或统一入口按 header/字段分发 |
|
||||
|
||||
### 4.3 状态机微调(可选两种做法)
|
||||
|
||||
**做法 1(最小改动,推荐):不新增状态**
|
||||
|
||||
- 管理员审核通过后,认证状态仍为 `info_pending_review` 或新增语义:`info_submitted` 但 `sign_platform=NULL` 且 `auth_url` 为空
|
||||
- 前端根据 `metadata.need_select_platform=true` 展示平台选择页
|
||||
- 用户选完平台后才进入 `info_submitted`(有 `auth_url`)
|
||||
|
||||
**做法 2:新增状态 `platform_pending`**
|
||||
|
||||
- 语义更清晰,但需改 `certification_status.go`、前端步骤映射、管理端筛选
|
||||
- 适合后续平台数量继续增加时
|
||||
|
||||
**推荐做法 1**,通过 metadata 驱动前端,避免状态机膨胀:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "info_pending_review",
|
||||
"metadata": {
|
||||
"manual_review_status": "approved",
|
||||
"need_select_platform": true,
|
||||
"available_platforms": ["esign", "fadada"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
或审核通过后直接将状态推到 `info_submitted`,但 `auth_url` 为空、`need_select_platform=true`。
|
||||
|
||||
### 4.4 新增 API
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | `/api/v1/certifications/sign-platforms` | 返回可选平台列表(名称、图标、说明、是否可用) |
|
||||
| POST | `/api/v1/certifications/select-sign-platform` | 用户选择平台,生成认证链接 |
|
||||
| POST | `/api/v1/certifications/callbacks/fadada` | 法大大回调(与 esign 并列) |
|
||||
| POST | `/api/v1/certifications/admin/reset-sign-platform` | 管理员重置平台选择(可选) |
|
||||
|
||||
**`SelectSignPlatform` 请求体:**
|
||||
|
||||
```json
|
||||
{
|
||||
"sign_platform": "fadada"
|
||||
}
|
||||
```
|
||||
|
||||
**响应(与现有 details 结构对齐):**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "info_submitted",
|
||||
"sign_platform": "fadada",
|
||||
"metadata": {
|
||||
"auth_url": "https://...",
|
||||
"next_action": "请完成企业认证"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**`apply-contract` / `confirm-sign` / `confirm-auth`**:无需改路径,内部按 `cert.SignPlatform` 路由。
|
||||
|
||||
### 4.5 管理员审核逻辑调整
|
||||
|
||||
当前 `AdminApproveSubmitRecord` 审核通过后会**立即**调用 e签宝。改造后:
|
||||
|
||||
```
|
||||
审核通过 → 仅更新 submit_record.manual_review_status = approved
|
||||
→ 认证状态:保持 info_pending_review(或推到 info_submitted 但无 auth_url)
|
||||
→ 不调用任何第三方
|
||||
用户选择平台 → SelectSignPlatform → 调对应 provider → 写入 auth_url → info_submitted
|
||||
```
|
||||
|
||||
管理员后台可展示 `sign_platform` 列,便于客服排查。
|
||||
|
||||
---
|
||||
|
||||
## 5. 法大大接入方案
|
||||
|
||||
### 5.1 平台与文档
|
||||
|
||||
- 产品:法大大 **FASC OpenAPI v5.1**(公有云)
|
||||
- 开放平台:`https://cloud.fadada.com` / 开发文档 `https://dev.fadada.com`
|
||||
- 参考 SDK:`fasc-openapi-go-sdk`(官方 Go SDK,建议优先使用)
|
||||
|
||||
### 5.2 需准备的法大大侧资源
|
||||
|
||||
| 资源 | 用途 |
|
||||
|------|------|
|
||||
| AppID / AppSecret | API 鉴权 |
|
||||
| openCorpId | 接入企业(海宇数据自身)在法大大侧的企业 ID |
|
||||
| 应用模板 / 签署模板 | 对应合作协议 PDF 模板 |
|
||||
| 测试环境域名 | 沙箱联调 |
|
||||
| 回调地址 | `https://{domain}/api/v1/certifications/callbacks/fadada` |
|
||||
|
||||
### 5.3 法大大 API 与 e签宝 映射
|
||||
|
||||
| 业务步骤 | e签宝(现有) | 法大大 FASC(待接) |
|
||||
|----------|---------------|---------------------|
|
||||
| 获取企业认证链接 | `POST /v3/org-auth-url` | `Corp` 模块 - 获取企业认证/授权 URL |
|
||||
| 查询企业是否已实名 | `GET /v3/organizations/identity-info` | `Corp` 模块 - 查询企业认证状态 |
|
||||
| 模板生成合同文件 | `POST /v3/files/create-by-doc-template` | `AppTemplate` + `Doc` 模块 - 填写模板生成文档 |
|
||||
| 创建签署流程 | `POST /v3/sign-flow/create-by-file` | `fadada.CreateSignFlow` → `/sign-task/create` 或 `/sign-task/create-with-template` |
|
||||
| 获取签署链接 | `POST /v3/sign-flow/{id}/sign-url` | `fadada.GetSignURL` → `/sign-task/actor/get-url` |
|
||||
| 查询签署状态 | `GET /v3/sign-flow/{id}/detail` | `fadada.QuerySignStatus` → `/sign-task/app/get-detail`(`task_finished`/`sign_completed`=完成) |
|
||||
| 下载已签文件 | `GET /v3/sign-flow/{id}/file-download-url` | `fadada.DownloadSignedFiles` → `/sign-task/owner/get-download-url` |
|
||||
| 回调 | `AUTH_PASS` 等 | `fadada.VerifyCallback` + `ParseCallback`(`X-FASC-*` HMAC-SHA256) |
|
||||
|
||||
> 具体接口路径以法大大 v5.1 文档为准;上表为模块级映射,实施时按官方 SDK 方法名落地。
|
||||
|
||||
### 5.4 法大大合同模板控件
|
||||
|
||||
模板 ID:`1784013372537192273`(`config.yaml` → `fadada.template_id`)
|
||||
|
||||
| 业务字段 | e签宝 控件 key | 法大大控件编码 (fieldId) | 配置键 |
|
||||
|----------|----------------|--------------------------|--------|
|
||||
| 协议编号 | `xybh` | 多控件同值(见 `config.yaml` `agreement_no` 列表) | `agreement_no` |
|
||||
| 签订日期 | (系统) | `2039326319` | `contract_date` |
|
||||
| 甲方企业名 | `jfqym` / `jfqym2` | `6920634255` | `party_a_name` |
|
||||
| 甲方统一信用代码 | `jftyshxydm` | `8702060291` | `party_a_uscc` |
|
||||
| 甲方联系地址 | `jflxdz` | `4557539440` | `party_a_address` |
|
||||
| 甲方授权代表/法人 | `jfsqdb` | `8627432823` | `party_a_rep` |
|
||||
| 甲方签署日期 | `qsrq1` / `qsrq3` | `6047306418` | `party_a_sign_date` |
|
||||
| 乙方签署日期 | `qsrq2` | `3849218908` | `party_b_sign_date` |
|
||||
|
||||
日期格式与现网一致:`2006年01月02日`。实现:`fadada.Client.FillTemplate` / `FillSignTaskFields`。
|
||||
|
||||
### 5.5 法大大 SDK 目录规划
|
||||
|
||||
```
|
||||
hyapi-server/internal/shared/fadada/ # 或 infrastructure/external/fadada/
|
||||
├── client.go # 封装官方 SDK Client
|
||||
├── config.go
|
||||
├── corp_service.go # 企业认证
|
||||
├── signtask_service.go # 签署任务
|
||||
├── template_service.go # 模板/文档
|
||||
├── callback.go # 回调解析与验签
|
||||
└── provider.go # 实现 SignPlatformProvider
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 配置变更
|
||||
|
||||
### 6.1 config.yaml 新增法大大段
|
||||
|
||||
```yaml
|
||||
# 签署平台全局开关
|
||||
sign_platform:
|
||||
enabled: ["esign", "fadada"] # 前端可选列表
|
||||
default: "" # 空=强制用户选择
|
||||
|
||||
esign:
|
||||
# 保持现有配置不变
|
||||
...
|
||||
|
||||
fadada:
|
||||
app_id: ""
|
||||
app_secret: ""
|
||||
server_url: "https://api.fadada.com" # 以官方文档为准
|
||||
open_corp_id: "" # 海宇数据在法大大的企业 ID
|
||||
template_id: "" # 应用签署模板 ID
|
||||
contract:
|
||||
name: "海宇数据-合作协议"
|
||||
expire_days: 7
|
||||
auth:
|
||||
redirect_url: "https://console.haiyudata.com/certification/callback/fadada/auth"
|
||||
sign:
|
||||
redirect_url: "https://console.haiyudata.com/certification/callback/fadada/sign"
|
||||
callback:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
### 6.2 Go Config 结构
|
||||
|
||||
```go
|
||||
type SignPlatformConfig struct {
|
||||
Enabled []string `mapstructure:"enabled"`
|
||||
Default string `mapstructure:"default"`
|
||||
}
|
||||
|
||||
type FadadaConfig struct {
|
||||
AppID string `mapstructure:"app_id"`
|
||||
AppSecret string `mapstructure:"app_secret"`
|
||||
ServerURL string `mapstructure:"server_url"`
|
||||
OpenCorpID string `mapstructure:"open_corp_id"`
|
||||
TemplateID string `mapstructure:"template_id"`
|
||||
Contract ContractConfig `mapstructure:"contract"`
|
||||
Auth AuthConfig `mapstructure:"auth"`
|
||||
Sign SignConfig `mapstructure:"sign"`
|
||||
Callback CallbackConfig `mapstructure:"callback"`
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 前端改造
|
||||
|
||||
### 7.1 新增步骤与组件
|
||||
|
||||
```
|
||||
certification/components/
|
||||
├── PlatformSelect.vue # 【新增】签署平台选择
|
||||
├── EnterpriseVerify.vue # 改造:支持不同平台 iframe / 跳转方式
|
||||
├── ContractSign.vue # 改造:按平台展示签署页
|
||||
└── ...
|
||||
```
|
||||
|
||||
**步骤条调整:**
|
||||
|
||||
```javascript
|
||||
const certificationSteps = [
|
||||
{ key: 'enterprise_info', title: '填写企业信息', ... },
|
||||
{ key: 'manual_review', title: '人工审核', ... },
|
||||
{ key: 'platform_select', title: '选择签署平台', ... }, // 新增
|
||||
{ key: 'enterprise_verify', title: '企业认证', ... },
|
||||
{ key: 'contract_sign', title: '签署合同', ... },
|
||||
{ key: 'completed', title: '完成', ... },
|
||||
]
|
||||
```
|
||||
|
||||
### 7.2 步骤路由逻辑(`setCurrentStepByStatus`)
|
||||
|
||||
```javascript
|
||||
// 审核已通过,但未选平台
|
||||
if (status === 'info_pending_review' && metadata.manual_review_status === 'approved' && !data.sign_platform) {
|
||||
currentStep.value = 'platform_select'
|
||||
return
|
||||
}
|
||||
// 或:status === 'info_submitted' && !metadata.auth_url && metadata.need_select_platform
|
||||
```
|
||||
|
||||
### 7.3 PlatformSelect.vue 交互
|
||||
|
||||
- 调用 `GET /sign-platforms` 获取可选平台卡片(e签宝 / 法大大)
|
||||
- 每张卡片展示:平台名称、说明、预计耗时、合规提示
|
||||
- 用户点击「确认选择」→ `POST /select-sign-platform`
|
||||
- 成功后跳转 `enterprise_verify`,iframe 加载返回的 `auth_url`
|
||||
|
||||
### 7.4 API 封装(`src/api/index.js`)
|
||||
|
||||
```javascript
|
||||
export const certificationApi = {
|
||||
// ...existing
|
||||
getSignPlatforms: () => request.get('/certifications/sign-platforms'),
|
||||
selectSignPlatform: (data) => request.post('/certifications/select-sign-platform', data),
|
||||
}
|
||||
```
|
||||
|
||||
### 7.5 回调路由扩展
|
||||
|
||||
现有:
|
||||
|
||||
- `/certification/callback/auth`
|
||||
- `/certification/callback/sign`
|
||||
|
||||
建议扩展(便于各平台 redirect_url 分离统计):
|
||||
|
||||
- `/certification/callback/esign/auth`
|
||||
- `/certification/callback/esign/sign`
|
||||
- `/certification/callback/fadada/auth`
|
||||
- `/certification/callback/fadada/sign`
|
||||
|
||||
`IframeCallback.vue` 可增加 `platform` 路由参数,回调后 `postMessage` 携带 `{ type: 'CHECK_STATUS', scene, platform }`。
|
||||
|
||||
### 7.6 管理端
|
||||
|
||||
`certification-reviews/index.vue` 列表增加「签署平台」列;详情展示平台与第三方 flow ID,方便客服排查。
|
||||
|
||||
---
|
||||
|
||||
## 8. 回调与轮询策略
|
||||
|
||||
| 环节 | e签宝(现有) | 法大大(新增) | 统一策略 |
|
||||
|------|---------------|----------------|----------|
|
||||
| 企业认证完成 | 回调 `AUTH_PASS` + 轮询 identity | 法大大认证回调 + 轮询认证状态 | 两平台均保留「回调 + 轮询」双保险 |
|
||||
| 合同签署完成 | 主要依赖 `confirm-sign` 轮询 | 同上 | `GET /details` 时按平台自动同步 |
|
||||
| 验签 | 当前已注释 | **必须实现** | 生产环境两个平台都开启验签 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 实施分期建议
|
||||
|
||||
### Phase 0 — 准备(1~2 天)
|
||||
|
||||
- [ ] 法大大开放平台开户、创建应用、获取 AppID/AppSecret/openCorpId
|
||||
- [ ] 在法大大创建合作协议模板(控件与海宇业务字段对齐)
|
||||
- [ ] 确认沙箱环境与回调公网可达
|
||||
|
||||
### Phase 1 — 抽象层 + 数据字段(3~5 天)
|
||||
|
||||
- [x] `certifications.sign_platform` 及子表字段
|
||||
- [x] `SignPlatform` 枚举 + `SignPlatformProvider` 接口
|
||||
- [x] 将现有 e签宝 逻辑迁入 `EsignProvider`(行为不变)
|
||||
- [x] `PlatformRegistry` 注入容器
|
||||
- [ ] 历史数据迁移脚本(AutoMigrate 已加列;存量可按需刷 `esign`)
|
||||
- [ ] 回归测试:现有 e签宝 全流程无回归
|
||||
|
||||
### Phase 2 — 平台选择流程(2~3 天)
|
||||
|
||||
- [x] 后端 `SelectSignPlatform` + `GetSignPlatforms`
|
||||
- [x] 调整 `AdminApproveSubmitRecord`(审核后不立即调第三方)
|
||||
- [x] 前端 `PlatformSelect.vue` + 步骤条改造
|
||||
- [ ] 仅 e签宝 模式下验证「先选平台 → 再认证 → 再签署」
|
||||
|
||||
### Phase 3 — 法大大 Provider(5~8 天)
|
||||
|
||||
- [x] 接入 HTTP/SDK 封装,实现 `FadadaProvider`(骨架 + OpenAPI 封装)
|
||||
- [x] 企业认证链接、实名查询(代码层)
|
||||
- [x] 模板填单、签署任务、状态查询、文件下载(代码层)
|
||||
- [x] `/callbacks/fadada` + 验签钩子
|
||||
- [ ] 法大大沙箱端到端联调(需有效凭证与模板)
|
||||
|
||||
### Phase 4 — 收尾(2~3 天)
|
||||
|
||||
- [x] 管理端展示 `sign_platform` / 待选平台
|
||||
- [ ] 启用 e签宝 回调验签(安全债补齐)
|
||||
- [ ] 文档与 Swagger 更新
|
||||
- [ ] 生产配置与灰度发布
|
||||
|
||||
**预估总工期:12~18 个工作日**(1 人全职,含联调;法大大审核/模板配置时间另计)
|
||||
|
||||
---
|
||||
|
||||
## 10. 风险与注意事项
|
||||
|
||||
| 风险 | 说明 | 缓解 |
|
||||
|------|------|------|
|
||||
| 两平台企业实名不互通 | 选法大大则必须在法大大完成企业认证 | 平台选择页明确提示;选定后锁定 |
|
||||
| 模板不一致 | 两份模板需法务分别审核 | 上线前做法务对齐检查清单 |
|
||||
| 签署状态码差异 | e签宝 `2`=完成;法大大状态码不同 | 封装在各自 Provider 内,对外统一 `SignStatusResult` |
|
||||
| 经办人信息 | 当前 e签宝 用法人作经办人 | 法大大侧同步规则;授权代表字段映射需确认 |
|
||||
| 合同文件过期 | e签宝 50 分钟过期重生成 | 法大大侧确认是否有过期策略,在 Provider 实现 |
|
||||
| 进行中的用户 | 迁移时默认 `esign` | 仅新用户或管理员重置后才出现平台选择 |
|
||||
|
||||
---
|
||||
|
||||
## 11. 待你决策的问题
|
||||
|
||||
在动手写代码前,建议确认以下事项:
|
||||
|
||||
### Q1. 平台选择时机
|
||||
|
||||
- [ ] **A. 审核通过后由用户选择**(推荐)
|
||||
- [ ] B. 提交企业信息时选择
|
||||
- [ ] C. 其他:___________
|
||||
|
||||
### Q2. 是否新增状态 `platform_pending`
|
||||
|
||||
- [ ] **A. 不新增,用 metadata 驱动**(推荐,改动小)
|
||||
- [ ] B. 新增独立状态
|
||||
|
||||
### Q3. e签宝 是否继续保留
|
||||
|
||||
- [ ] **A. 双平台并存,用户可选**(推荐)
|
||||
- [ ] B. 新用户仅法大大,e签宝 仅维护存量
|
||||
- [ ] C. 全量迁移到法大大
|
||||
|
||||
### Q4. 管理员审核后行为
|
||||
|
||||
- [ ] **A. 审核通过不生成链接,等用户选平台**(推荐)
|
||||
- [ ] B. 审核通过默认 e签宝 并生成链接,用户可在认证前改选法大大
|
||||
|
||||
### Q5. 法大大接入方式
|
||||
|
||||
- [ ] **A. 官方 Go SDK**(推荐)
|
||||
- [ ] B. 自研 HTTP 封装(与 esign 包一致)
|
||||
|
||||
### Q6. 合同模板
|
||||
|
||||
- [ ] A. 法大大新建一份等价模板
|
||||
- [ ] B. 非法大大模板,改用上传 PDF 方式发起签署
|
||||
|
||||
---
|
||||
|
||||
## 12. 文件改动清单(实施时参考)
|
||||
|
||||
### 后端
|
||||
|
||||
| 文件 | 改动类型 |
|
||||
|------|----------|
|
||||
| `domains/certification/entities/certification.go` | 新增 `SignPlatform` 字段 |
|
||||
| `domains/certification/enums/sign_platform.go` | 新增 |
|
||||
| `domains/certification/ports/sign_platform_provider.go` | 新增接口 |
|
||||
| `infrastructure/external/esign/esign_provider.go` | 从 service_impl 抽取 |
|
||||
| `infrastructure/external/fadada/*` | 新增 |
|
||||
| `application/certification/certification_application_service_impl.go` | 改为调 registry |
|
||||
| `application/certification/dto/commands/` | 新增 `SelectSignPlatformCommand` |
|
||||
| `infrastructure/http/routes/certification_routes.go` | 新增路由 |
|
||||
| `infrastructure/http/handlers/certification_handler.go` | 新增 handler |
|
||||
| `config/config.go` + `config.yaml` | 新增 fadada / sign_platform 配置 |
|
||||
| `container/container.go` | 注册 FadadaProvider + Registry |
|
||||
|
||||
### 前端
|
||||
|
||||
| 文件 | 改动类型 |
|
||||
|------|----------|
|
||||
| `pages/certification/index.vue` | 步骤与状态映射 |
|
||||
| `pages/certification/components/PlatformSelect.vue` | 新增 |
|
||||
| `pages/certification/IframeCallback.vue` | 支持多平台 |
|
||||
| `src/api/index.js` | 新增 API |
|
||||
| `src/router/index.js` | 扩展 callback 路由 |
|
||||
| `pages/admin/certification-reviews/index.vue` | 展示 sign_platform |
|
||||
|
||||
---
|
||||
|
||||
## 13. 总结
|
||||
|
||||
本次改造的核心不是「替换 e签宝」,而是:
|
||||
|
||||
1. **主表增加 `sign_platform`**,记录用户选择的签署平台
|
||||
2. **抽象 `SignPlatformProvider`**,将 e签宝 现有实现原样迁入
|
||||
3. **审核通过后增加平台选择步骤**,再按平台发放认证/签署链接
|
||||
4. **新增法大大 Provider**,与 e签宝 并行
|
||||
|
||||
这样对存量 e签宝 用户零影响,新用户可自由选择;后续若再接入第三方,只需新增 Provider 实现并加入 `enabled` 列表。
|
||||
|
||||
---
|
||||
|
||||
*请确认第 11 节中的决策项后,可据此进入具体开发。*
|
||||
Reference in New Issue
Block a user