____ _ ____ _
/ ___| ___ __ _| | / ___| _ _ ___| |_ ___ _ __ ___
\___ \ / _ \/ _` | | \___ \| | | / __| __/ _ \ '_ ` _ \
___) | __/ (_| | | ___) | |_| \__ \ || __/ | | | | |
|____/ \___|\__,_|_| |____/ \__, |___/\__\___|_| |_| |_|
|___/ | 技术 | 版本 | 说明 |
|---|---|---|
| Spring Boot | 2.7.x | 基础框架 |
| MyBatis-Plus | 3.5.x | ORM 框架 |
| MySQL | 5.7+ / 8.0+ | 数据库 |
| Redis | 6.0+ | 缓存 & 短链接存储 |
| iText | 5.5.13 | PDF 处理与数字签名 |
┌─────────────────────────────────────────────────────────┐
│ 应用层 (Application) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │
│ │ Admin UI │ │seal-core │ │mini-cont.│ │Portal │ │
│ │ (管理后台) │ │ (签署引擎) │ │(小程序/H5)│ │(PC门户) │ │
│ └──────────┘ └──────────┘ └──────────┘ └────────┘ │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 接口层 (Controller) │
│ ┌────────────┐ ┌────────────┐ ┌──────────────────┐ │
│ │ Admin API │ │ App API │ │ Portal API │ │
│ │ (管理端) │ │ (签署/认证) │ │ (PC门户端) │ │
│ └────────────┘ └────────────┘ └──────────────────┘ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Open API(第三方开放接口) │ │
│ │ 认证 → 签署任务 → Webhook 回调 │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 业务层 (Service) │
│ ┌──────────┐ ┌──────────┐ ┌────────────┐ │
│ │SealSign │ │Seal │ │Notification│ │
│ │TaskSvc │ │ConfigSvc │ │Service │ │
│ │(签署任务) │ │(配置读取) │ │(通知&短链接) │ │
│ └──────────┘ └──────────┘ └────────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌────────────┐ │
│ │Signature │ │IntentVer.│ │Template │ │
│ │Service │ │Service │ │Service │ │
│ │(PDF签名) │ │(意愿验证) │ │(模板管理) │ │
│ └──────────┘ └──────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 扩展层 (SPI / Starter) │
│ ┌────────────────────┐ ┌──────────────────────────┐ │
│ │ UserInfoProvider │ │ QuotaValidator │ │
│ │ (用户信息获取) │ │ (配额校验) │ │
│ └────────────────────┘ └──────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 数据层 (DAO / Mapper) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ MySQL │ │ Redis │ │ OSS │ │
│ │ (业务数据) │ │ (短链接/缓存)│ │ (文件存储) │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────┘yudao-module-seal/
├── yudao-module-seal-api/ # 公共 API(DTO、枚举、SPI 接口)
│ └── api/
│ ├── auth/ # 认证 SPI(SealAuthProvider)
│ ├── storage/ # 存储 SPI(SealStorageProvider)
│ └── validator/ # 校验 SPI(SealQuotaValidator、SealUserValidator)
├── yudao-module-seal-biz/ # 业务实现(Controller、Service、DAL)
│ └── service/user/ # 用户信息 SPI(UserInfoProvider、UserInfoAdapter)
└── yudao-module-seal-spring-boot-starter/ # 产品化 Starter(SPI 适配、License)
├── adapter/ # SPI 适配器实现(auth、storage、user、enterprise)
└── validator/ # SPI 校验器实现(Ruoyi 适配 + NoOp 降级)| 项目 | 说明 | 路径 |
|---|---|---|
| seal-core | 签署引擎 H5(签署页面、短链接路由) | yudao-ui/seal-core |
| mini-contract | uni-app 用户端(小程序 / H5 / APP) | mini-contract/ |
| contract-portal-pc | PC 端合同管理门户 | yudao-ui/contract-portal-pc |
| yudao-ui-admin-vue3 | 管理后台 | yudao-ui/yudao-ui-admin-vue3 |
yudao-module-seal-biz/src/main/java/cn/iocoder/yudao/module/seal/
├── controller/
│ ├── admin/ # 管理端 API
│ │ ├── sealsigntask/ # 签署任务管理
│ │ └── vo/ # 管理端 VO
│ ├── app/ # App 端 API(小程序/H5 直接调用)
│ │ ├── signtask/ # 签署任务(含 auth-by-ref 认证)
│ │ ├── seal/ # 印章操作
│ │ ├── shortlink/ # 短链接信息
│ │ └── template/ # 模板操作
│ ├── portal/ # Portal 端 API(PC 门户调用)
│ └── open/ # Open API(第三方开放接口)
│ ├── OpenSealAuthController # 认证(AppKey/Secret → Token)
│ ├── OpenSignTaskController # 签署任务 CRUD
│ ├── OpenWebhookController # Webhook 回调配置
│ └── vo/ # Open API 请求/响应 VO
├── service/
│ ├── sealsigntask/ # 签署任务核心业务
│ ├── config/ # 配置服务(SealConfigService)
│ ├── notification/ # 通知 & 短链接生成
│ ├── signature/ # PDF 数字签名
│ ├── intentverification/ # 签署意愿验证
│ └── webhook/ # Webhook 回调服务
│ ├── SealWebhookService # 接口
│ └── SealWebhookServiceImpl # 实现(WebClient + 异步 + 重试)
├── dal/
│ ├── dataobject/ # 数据对象(DO)
│ │ └── webhook/ # Webhook 相关 DO
│ │ ├── SealWebhookConfigDO # 回调配置
│ │ └── SealWebhookLogDO # 回调日志
│ ├── mysql/ # Mapper
│ │ └── webhook/ # Webhook Mapper
│ └── redis/ # Redis Key 常量
├── framework/
│ └── security/ # Seal Token 认证体系
└── util/
└── jwt/ # JWT 工具┌─────────────────────────────────────────────────────────────────┐
│ seal-biz(业务层) │
│ │
│ SealSignTaskServiceImpl │
│ ├── @Resource UserInfoProvider ← 获取用户手机号等 │
│ ├── @Resource SealUserValidator ← 校验用户/实名认证 │
│ ├── @Autowired(required=false) │
│ │ SealQuotaValidator ← 配额校验(可选) │
│ └── @Autowired(required=false) │
│ EnterpriseAccessValidator ← 企业权限校验(可选) │
│ │
│ PdfDigitalSignatureService │
│ └── @Resource SealStorageProvider ← 文件上传/下载 │
│ │
│ SealTokenAdapter │
│ └── @Resource SealAuthProvider ← 用户认证 │
└────────────────────────┬────────────────────────────────────────┘
│ 接口调用(无编译依赖)
▼
┌─────────────────────────────────────────────────────────────────┐
│ seal-spring-boot-starter(适配层) │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌───────────────┐ │
│ │ RuoyiAuthAdapter │ │RuoyiQuotaValid. │ │UserInfoAdapter│ │
│ │ JwtAuthAdapter │ │RuoyiUserValid. │ │ Impl │ │
│ │ OAuth2AuthAdapter│ │NoOpQuotaValid. │ │NoOpUserInfo │ │
│ └─────────────────┘ │NoOpUserValid. │ │ Adapter │ │
│ └──────────────────┘ └───────────────┘ │
│ ┌──────────────────────┐ ┌──────────────────────────────┐ │
│ │ RuoyiFileClientAdapter│ │EnterpriseAccessValidatorImpl │ │
│ │ OssStorageAdapter │ │NoOpEnterpriseAccessValidator │ │
│ │ MinioStorageAdapter │ └──────────────────────────────┘ │
│ │ LocalStorageAdapter │ │
│ └──────────────────────┘ │
│ │
│ 通过 @Conditional 注解自动选择实现 │
└─────────────────────────────────────────────────────────────────┘| SPI 接口 | 所在模块 | 职责 | 是否必需 | 默认实现 |
|---|---|---|---|---|
SealAuthProvider | seal-api | 用户认证(从请求中提取用户信息) | 是 | 拒绝所有请求(需配置) |
SealStorageProvider | seal-api | 文件存储(上传/下载/删除) | 是 | 抛异常(需配置) |
SealQuotaValidator | seal-api | 配额校验与扣减 | 否(可选) | 跳过配额检查 |
SealUserValidator | seal-api | 用户存在性与实名认证校验 | 是 | RuoyiUserValidator |
UserInfoProvider | seal-biz | 获取用户手机号、昵称等详细信息 | 是 | 通过 Bridge 桥接 |
UserInfoAdapter | seal-biz | UserInfoProvider 的适配器接口 | 是 | NoOpUserInfoAdapter(降级) |
EnterpriseAccessValidator | seal-biz | 验证用户是否有企业访问权限 | 否(可选) | NoOpEnterpriseAccessValidator(放行) |
seal-api):| 实现类 | 激活条件 | 说明 |
|---|---|---|
RuoyiAuthAdapter | classpath 存在 SecurityFrameworkUtils | 从 Ruoyi 的 ThreadLocal 中获取登录用户 |
JwtAuthAdapter | yudao.seal.auth.type=jwt | 解析 JWT Token |
OAuth2AuthAdapter | yudao.seal.auth.type=oauth2 | OAuth2 Token 验证 |
seal-api):| 实现类 | 说明 |
|---|---|
RuoyiFileClientAdapter | 适配 Ruoyi 的 FileClient(推荐,复用 Ruoyi 文件配置) |
OssStorageAdapter | 阿里云 OSS |
MinioStorageAdapter | MinIO 对象存储 |
LocalStorageAdapter | 本地文件系统 |
seal-api):| 实现类 | 激活条件 | 说明 |
|---|---|---|
RuoyiQuotaValidator | yudao.seal.quota-validator=ruoyi(默认) | 读取 member_quota_account 表,使用乐观锁扣减 |
NoOpQuotaValidator | yudao.seal.quota-validator=noop | 不做任何校验,始终返回 true(测试用) |
seal-api):| 实现类 | 激活条件 | 说明 |
|---|---|---|
RuoyiUserValidator | yudao.seal.user-validator=ruoyi(默认) | 查询 system_user 和 member_user 表 |
NoOpUserValidator | yudao.seal.user-validator=noop | 始终返回成功(开发/测试用,生产会告警) |
seal-biz 定义
┌──────────────┐ ┌──────────────────┐ ┌──────────────┐
│ Service 层 │───▶│ UserInfoProvider │◀──│UserInfoPro- │
│ (业务代码调用) │ │ (接口) │ │viderBridge │
└──────────────┘ └──────────────────┘ └──────┬───────┘
│ 委托
▼
┌──────────────────┐ ┌──────────────┐
│ UserInfoAdapter │◀──│UserInfoAdap- │ seal-starter 实现
│ (接口) │ │terImpl │
└──────────────────┘ └──────────────┘UserInfoProvider:seal-biz 内部使用的标准接口,业务代码只依赖它UserInfoAdapter:供 Starter 模块实现的适配器接口,包含 getAdapterType() 等管理方法UserInfoProviderBridge:桥接器,将 Starter 的 Adapter 实现桥接为 Biz 的 Providerseal-biz):| 实现 类 | 激活条件 | 说明 |
|---|---|---|
UserInfoAdapterImpl | classpath 存在 MemberUserApi | 通过反射调用 member 模块,避免编译依赖 |
NoOpUserInfoAdapter | 无其他 Adapter 实现时 | 返回 null,功能降级,日志告警 |
seal-biz):| 实现类 | 激活条件 | 说明 |
|---|---|---|
EnterpriseAccessValidatorImpl | classpath 存在 MemberEnterpriseUserService | 通过反射调用 member 模块 |
NoOpEnterpriseAccessValidator | 无其他实现时 | 始终放行,日志告警 |
@Resource):@Autowired(required = false)):META-INF/spring.factories 声明 SealAutoConfigurationyudao.seal.mode=starter 时激活自动配置@Conditional* 注解控制装配条件spring.factories
└── SealAutoConfiguration (@ConditionalOnProperty: yudao.seal.mode=starter)
├── @ComponentScan 扫描 seal.biz + seal.starter 包
├── RuoyiAuthAdapter (@ConditionalOnClass: SecurityFrameworkUtils)
├── RuoyiQuotaValidator (@ConditionalOnProperty: quota-validator=ruoyi, 默认)
├── RuoyiUserValidator (@ConditionalOnProperty: user-validator=ruoyi, 默认)
├── UserInfoAdapterImpl (@ConditionalOnClass: MemberUserApi)
├── UserInfoProviderBridge(@ConditionalOnBean: UserInfoAdapter)
├── NoOpQuotaValidator (@ConditionalOnProperty: quota-validator=noop)
├── NoOpUserValidator (@ConditionalOnProperty: user-validator=noop)
├── NoOpUserInfoAdapter (@ConditionalOnMissingBean: UserInfoAdapter)
└── NoOpEnterpriseAccess (@ConditionalOnMissingBean: EnterpriseAccessValidator)| 配置键 | 可选值 | 默认值 | 说明 |
|---|---|---|---|
yudao.seal.mode | starter / 不设置 | 不启用 | 总开关,必须设为 starter 才激活自动配置 |
yudao.seal.auth.type | ruoyi / jwt / oauth2 | ruoyi | 认证方式 |
yudao.seal.storage.type | local / oss / minio | - | 存储方式 |
yudao.seal.quota-validator | ruoyi / noop | ruoyi | 配额校验器 |
yudao.seal.user-validator | ruoyi / noop | ruoyi | 用户校验器 |
@Component 或 @Bean)application.yml 中设置对应值注意:自定义实现会自动覆盖内置的默认实现(通过 @ConditionalOnMissingBean/@ConditionalOnProperty机制)。
infra_config 表中管理,管理员通过管理后台「基础设施 - 参数配置」操作,无需修改代码或重启服务。| 配置键 | 说明 | 示例值 |
|---|---|---|
seal.platform.h5-domain | seal-core H5 域名(签署引擎) | https://seal.example.com |
seal.platform.mini-contract-h5-domain | mini-contract H5 域名(登录签署用) | https://app.example.com |
seal.platform.miniprogram-appid | 微信小程序 AppID | wx22db0d6ebe81e2ee |
seal.platform.miniprogram-name | 小程序在引导页显示的名称 | MiniContract.Pro |
seal.platform.miniprogram-logo | 小程序在引导页显示的图标 URL | https://oss.example.com/logo.png |
infra_config 表的 visible 字段(管理后台显示为「是否可见」)控制功能开关:| visible 值 | 效果 |
|---|---|
| 是 (true) | 该配置启用,正常读取值 |
| 否 (false) | 该配置禁用,等同于未配置 |
| 小程序 AppID | Mini-Contract H5 域名 | 行为 |
|---|---|---|
| 启用 | 禁用 | 只走小程序签署 |
| 禁用 | 启用 | 只走 H5 登录签署 |
| 启用 | 启用 | 小程序优先,H5 作为回退 |
| 禁用 | 禁用 | 无法签署(异常) |
管理后台「参数配置」
↓ 写入
infra_config 表(config_key + value + visible)
↓ 读取
ConfigApi.getVisibleConfigValueByKey()
↓ visible=false 返回 null
SealConfigService.getVisibleConfig()
↓ null 或空 → 返回 ""(功能禁用)
业务代码调用(如 getSealCoreH5Domain()、isMiniprogramEnabled())ConfigApi.java / ConfigApiImpl.java - 基础配置 API(含 visible 感知方法)SealConfigService.java - 平台路由配置读取服务SealConfigChecker.java - 启动时配置检查,打印当前启用状态| 前缀 | 用途 | 场景 |
|---|---|---|
/s/{ref} | H5 签署链接 | H5 模式优先时生成 |
/m/{ref} | 小程序签署链接 | 小程序模式优先时生成 |
/p/{ref} | 预览链接 | 签署完成后查看合同 |
SealSignTaskNotificationServiceImpl.generateSignUrl():调用方未指定 platform 参数
↓
SealConfigService.isMiniprogramEnabled() ?
├── true → platform = "miniprogram" → 生成 /m/{ref}
└── false → platform = "h5" → 生成 /s/{ref}seal:sign:token:{ref},默认 7 天过期。SignRedirect.vue 负责路由分发:用户点击签署链接
↓
前端调用 /app-api/api/v1/seal/auth/by-ref?ref=xxx
↓
后端返回认证结果 + platformRouting
↓
┌─ linkType = QUICK_SIGN(快捷签署)
│ → 直接跳转到签署页面 /h5/sign/document
│
├─ linkType = PREVIEW_LINK(预览)
│ → 跳转到预览页面 /h5/preview/contract
│
└─ linkType = SECURE_SIGN(登录签署)
↓
┌─ /m/ 路径(小程序链接)
│ ├─ 微信内 → 显示小程序桥接页(打开小程序按钮)
│ └─ 非微信 → 显示「请使用微信打开」页面(复制链接按钮)
│ └─ 如果 H5 也启用 → 底部显示「使用网页签署」回退链接
│
└─ /s/ 路径(H5 链接)
├─ 微信内 + 小程序可用 → 显示小程序桥接页
├─ H5 可用 → 跳转 H5 登录页(mini-contract 登录页)
├─ 仅小程序(非微信)→ 显示「请使用微信打开」页面
└─ 都不可用 → 显示错误提示AuthByRefRespVO.PlatformRouting(SECURE_SIGN 模式下返回):{
"preferMiniprogram": true,
"miniprogram": {
"enabled": true,
"appId": "wx22db0d6ebe81e2ee",
"name": "MiniContract.Pro",
"logo": "https://...",
"urlScheme": "weixin://dl/business/?appid=wx...&path=..."
},
"h5LoginUrl": "https://app.example.com/#/pages/login/index?redirect=..."
}preferMiniprogram: 由 isMiniprogramEnabled() 决定miniprogram.enabled: 小程序 AppID 配置且 visible=是h5LoginUrl: mini-contract H5 域名配置且 visible=是 时拼接完整登录 URL,否则为空| 级别 | 值 | 说明 | 用户体验 |
|---|---|---|---|
| 快捷签署 | 0 | 无需登录,点击链接直接签署 | 用户点击链接 → 直接进入签署页面 |
| 登录签署 | 1 | 需要登录后才能签署 | 用户点击链接 → 引导登录 → 登录后跳转签署页面 |
seal.sign.default-security-level(0 或 1)signSecurityLevel,覆盖全局默认值seal.sign.link-expire-hours,默认 168 小时(7 天)创建签署链接时:
├─ 快捷签署 → Redis 存储完整 Seal-Token → 前端拿到 token 直接签署
└─ 登录签署 → Redis 存储元数据(taskId, participantId, tenantId)
→ 前端需要用户登录后,带着登录态再请求签署
前端认证时(/auth/by-ref):
├─ 快捷签署 → 直接返回 Seal-Token,linkType=QUICK_SIGN
└─ 登录签署 → 返回 requireLogin=true,linkType=SECURE_SIGN
→ 同时返回 platformRouting 告诉前端如何跳转┌──────────────────────────────────────────────────────────────────┐
│ 第三方系统 │
│ │
│ 1. 获取 Token 2. 创建签署任务 3. 嵌入签署页面 4. 接收回调 │
└──────┬───────────────┬──────────────────┬──────────────┬─────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌──────────────────────────────────────────────────────────────────┐
│ Open API 接口层 │
│ 路径前缀: /open-api(框架自动添加) │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌────────────────┐ │
│ │OpenSealAuth │ │OpenSignTask │ │OpenWebhook │ │
│ │Controller │ │Controller │ │Controller │ │
│ │ │ │ │ │ │ │
│ │POST /auth/token │ │POST /sign-tasks │ │POST /webhooks │ │
│ │(AppKey/Secret │ │GET /sign-tasks/ │ │GET /webhooks │ │
│ │ → Seal-Token) │ │ {taskId} │ │DELETE /webhooks│ │
│ │ │ │POST /sign-tasks/ │ │ │ │
│ │ │ │ {taskId}/ │ │ │ │
│ │ │ │ cancel │ │ │ │
│ │ │ │GET /sign-tasks/ │ │ │ │
│ │ │ │ {taskId}/ │ │ │ │
│ │ │ │ sign-links │ │ │ │
│ │ │ │GET /sign-tasks/ │ │ │ │
│ │ │ │ {taskId}/ │ │ │ │
│ │ │ │ download │ │ │ │
│ └─────────────────┘ └──────────────────┘ └────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────────┐
│ 业务层 (Service) │
│ │
│ ┌──────────────────────┐ ┌─────────────────────────────────┐ │
│ │SealSignTaskService │ │SealWebhookService │ │
│ │(复用已有签署任务服务) │ │ │ │
│ │ │ │ saveWebhookConfig() 配置回调 │ │
│ │ createSignTask() │ │ sendWebhook() 异步发送 │ │
│ │ generateSignLink() │ │ retryFailedWebhooks() 失败重试 │ │
│ │ cancelSignTask() │ │ │ │
│ └──────────────────────┘ │ HTTP 客户端: WebClient │ │
│ │ 签名算法: HMAC-SHA256 │ │
│ │ 重试策略: 指数退避 (1/5/30 min) │ │
│ └─────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────────┐
│ 数据层 (Database) │
│ │
│ ┌──────────────────────┐ ┌─────────────────────────────────┐ │
│ │seal_api_key │ │seal_webhook_config │ │
│ │(API 密钥表) │ │(回调配置表) │ │
│ │ │ │ │ │
│ │ id (Integer) │ │ id, api_key_id, callback_url │ │
│ │ app_name │ │ secret, events, status │ │
│ │ api_key, secret_key │ └─────────────────────────────────┘ │
│ │ status │ ┌─────────────────────────────────┐ │
│ └──────────────────────┘ │seal_webhook_log │ │
│ │(回调日志表) │ │
│ │ │ │
│ │ id, webhook_config_id, task_id │ │
│ │ event_type, request_body │ │
│ │ response_status, response_body │ │
│ │ status, retry_count │ │
│ │ next_retry_time │ │
│ └─────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘controller.open 包下,框架 WebProperties 自动添加 /open-api 前缀:@RequestMapping 不需要写 /open-api 前缀:| 方法 | 实际路径 | 说明 | 认证方式 |
|---|---|---|---|
| POST | /open-api/api/v1/seal/auth/token | 获取访问令牌 | AppKey + SecretKey |
| POST | /open-api/api/v1/seal/sign-tasks | 创建签署任务 | Seal-Token |
| GET | /open-api/api/v1/seal/sign-tasks/{taskId} | 查询任务详情 | Seal-Token |
| POST | /open-api/api/v1/seal/sign-tasks/{taskId}/cancel | 撤销任务 | Seal-Token |
| GET | /open-api/api/v1/seal/sign-tasks/{taskId}/sign-links | 获取签署链接 | Seal-Token |
| GET | /open-api/api/v1/seal/sign-tasks/{taskId}/download | 下载已签署文件 | Seal-Token |
| POST | /open-api/api/v1/seal/webhooks | 配置 Webhook | Seal-Token |
| GET | /open-api/api/v1/seal/webhooks | 查询 Webhook 配置 | Seal-Token |
| DELETE | /open-api/api/v1/seal/webhooks | 删除 Webhook 配置 | Seal-Token |
第三方系统 Contract.Pro 签署人
│ │ │
│ 1. POST /auth/token │ │
│ (apiKey + secretKey + nonce) │ │
│ ◀─── Seal-Token ─────────────────│ │
│ │ │
│ 2. POST /sign-tasks │ │
│ (模板ID + 参与人 + callbackUrl)│ │
│ ◀─── taskId + signUrl ───────────│ │
│ │ │
│ 3. 将 signUrl 嵌入 iframe/WebView │ │
│ ─────────────────────────────────────────── signUrl ──────────▶│
│ │ │
│ │ 4. 用户在 seal-core 签署 │
│ │ ◀──── 签署完成 ────────────│
│ │ │
│ 5. Webhook 回调 │ │
│ ◀─── POST callbackUrl ───────────│ │
│ {eventType: "sign_completed", │ │
│ taskId, signedFileUrl} │ │
│ │ │
│ 6. GET /sign-tasks/{id}/download │ │
│ ◀─── signedFileUrl ──────────────│ │
│ │ │| 事件 | 说明 | 触发时机 |
|---|---|---|
sign_completed | 签署完成 | 所有参与人完成签署 |
sign_rejected | 签署拒绝 | 任一参与人拒签 |
sign_cancelled | 签署撤销 | 发起人撤销任务 |
sign_expired | 签署过期 | 任务超过截止时间 |
POST {callbackUrl}
Headers:
Content-Type: application/json
X-Seal-Signature: {HMAC-SHA256 签名}
X-Seal-Event: {事件类型}
X-Seal-Timestamp: {时间戳}
Body:
{
"eventType": "sign_completed",
"taskId": 1024,
"taskName": "劳动合同",
"status": 3,
"timestamp": 1707724800000,
"signedFileUrl": "https://oss.example.com/signed/xxx.pdf"
}