Files
zt-dsc/docs/iWork集成说明.md
2026-01-28 16:34:51 +08:00

408 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# iWork 统一集成使用说明
本文档介绍如何在 System 模块中使用项目已实现的统一 iWork 流程发起能力controller + service + properties。内容包含配置项、调用方式内部 Java 调用 & 外部 HTTP 调用)、请求/响应示例、错误处理、缓存与 Token 生命周期、业务回调分发与重试、流程日志查询,以及典型问题与排查步骤。
---
## 概览
项目在 `system` 模块下实现了一套对外统一的 iWork 集成能力:
- 提供管理端接口REST路径前缀`/system/integration/iwork`
- 提供 Service 层 `IWorkIntegrationService`,供其它模块以 Spring Bean 注入方式直接调用。
- 使用 `IWorkProperties` 绑定 `application.yml``iwork` 的配置项。
- Token / 会话采用本地 Caffeine 缓存缓存(按 appId + operatorUserId 缓存 session并在到期前按配置提前刷新。
- 使用统一配置的 appId、公钥以及默认流程编号无需再维护多套凭证所有调用强制使用配置的 appId不再接受请求覆盖。
- 全链路以 requestId 作为唯一业务标识(发起返回、作废入参、日志查询、回调与重试均基于 requestIdworkflowId 仅用于指定 iWork 模板。
- 支持业务回调标识 bizCallbackKey字符串≤255发起时提交回调时按标识分发业务回调并带自动重试与手工重试入口。
- 回调与日志记录会保存截断后的原始回调请求/响应文本(无需脱敏),无业务附件或处理异常时标记失败。
---
## 配置YAML
`application.yml`(或 profile添加或修改如下项示例摘自 `zt-server/src/main/resources/application.yaml`
```yaml
iwork:
base-url: https://iwork.example.com
app-id: my-iwork-app # 固定使用的 iWork 应用编号
client-public-key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A... # 与 iWork 约定的客户端公钥Base64
user-id: system # 默认操作用户(当调用未指定 operatorUserId 时使用)
org:
token-seed: 5936562a-d47c-4a29-9b74-b310e6c971b7
paths:
subcompany-page: /api/hrm/resful/getHrmsubcompanyWithPage
department-page: /api/hrm/resful/getHrmdepartmentWithPage
job-title-page: /api/hrm/resful/getJobtitleInfoWithPage
user-page: /api/hrm/resful/getHrmUserInfoWithPage
sync-subcompany: /api/hrm/resful/synSubcompany
sync-department: /api/hrm/resful/synDepartment
sync-job-title: /api/hrm/resful/synJobtitle
sync-user: /api/hrm/resful/synHrmresource
workflow-id: 54 # 当调用方未传 workflowId 时使用的默认流程编号
paths:
register: /api/ec/dev/auth/regist
apply-token: /api/ec/dev/auth/applytoken
user-info: /api/workflow/paService/getUserInfo
create-workflow: /api/workflow/paService/doCreateRequest
void-workflow: /api/workflow/paService/doCancelRequest
token:
ttl-seconds: 3600 # token 有效期(秒)
refresh-ahead-seconds: 60 # 在到期前多少秒认为需要刷新
callback:
retry:
max-attempts: 3 # 业务回调自动重试次数(默认 3 次,可调整)
delay-seconds: 5 # 业务回调自动重试间隔秒数(默认 5 秒,可调整)
client:
connect-timeout: 5s
response-timeout: 30s
```
说明:
- `base-url` 为 iWork 网关的基础地址,不能留空。
- `app-id``client-public-key` 共同构成注册/申请 token 所需的凭据信息,由配置统一提供,不再支持多套切换,系统强制使用配置的 appId忽略请求中的 appId。
- `workflow-id` 提供全局默认流程编号,仅用于向 iWork 指定流程模板;流程标识、查询、补偿、回调与重试一律使用 requestId不再以 workflowId 标识业务。
- 请求头键名固定为 `app-id``client-public-key``secret``token``time``user-id`,无需在配置中重复声明。
- `org.*` 配置负责 iWork 人力组织 REST 代理:`token-seed` 为与 iWork 约定的标识,系统会自动将其与毫秒时间戳拼接并计算 MD5 生成 `key`,无需额外传递 token。
- `callback.retry.*` 控制业务回调的自动重试次数与间隔,默认为 3 次、5 秒,可按需调整。
---
## 典型调用路径Controller
Controller 暴露的 REST 接口:
- POST /system/integration/iwork/user/resolve
- 说明:根据外部识别信息查找 iWork 的用户编号userId
- 请求:见下方 `Resolve User` 示例。
- POST /system/integration/iwork/workflow/create
- 说明:在 iWork 中发起流程。
- 请求:见下方 `Create Workflow` 示例。
- POST /system/integration/iwork/workflow/void
- 说明:作废/干预流程。
- 请求:见下方 `Void Workflow` 示例。
这些接口的响应均使用项目的 `CommonResult` 封装,实际返回的业务对象在 `data` 字段。
---
### 人力组织 REST 接口key + ts
为对接 PDF 所述的人力组织 RESTFUL 接口Controller 额外暴露了以下代理端点,用于通过 `key + ts` 生成的 token 与 iWork 交互:
- POST `/system/integration/iwork/hr/subcompany/page` —— 请求体传入 `params`Map对应 `getHrmsubcompanyWithPage`
- POST `/system/integration/iwork/hr/department/page` —— 对应 `getHrmdepartmentWithPage`
- POST `/system/integration/iwork/hr/job-title/page` —— 对应 `getJobtitleInfoWithPage`
- POST `/system/integration/iwork/hr/user/page` —— 对应 `getHrmUserInfoWithPage`
- POST `/system/integration/iwork/hr/subcompany/sync` —— 请求体传入 `data`List<Map>),对应 `synSubcompany`
- POST `/system/integration/iwork/hr/department/sync` —— 对应 `synDepartment`
- POST `/system/integration/iwork/hr/job-title/sync` —— 对应 `synJobtitle`
- POST `/system/integration/iwork/hr/user/sync` —— 对应 `synHrmresource`
所有请求均自动封装为 `application/x-www-form-urlencoded`,并把 `token` 字段设置为 `{"key":"<md5>","ts":"<timestamp>"}`,无需调用方重复计算。
---
## 请求 VO 说明(重要字段)
- IWorkBaseReqVO公用字段
- `appId` (String):仅保留字段,系统强制使用配置项 `iwork.app-id`,忽略请求值。
- `operatorUserId` (String):在 iWork 内部代表操作人的用户编号(可为空,框架会使用 `properties.userId`)。
- `forceRefreshToken` (Boolean):是否强制刷新 token例如遇到 token 错误时强制刷新)。
- IWorkUserInfoReqVO用于解析用户
- `identifierKey` (String):外部标识 key必须例如 "loginid")。
- `identifierValue` (String):外部标识值(必须,例如用户名)。
- `payload` (Map):额外的请求载荷,会与 identifier 合并后发送到 iWork。
- `queryParams` (Map):如果需要通过查询参数传递额外信息,可使用此字段。
- IWorkUserInfoRespVO解析用户响应
- `userId` (String):从 iWork 响应中解析出的用户编号(如果能解析到)。
- `success` / `message`:调用成功标志与提示信息。
- IWorkWorkflowCreateReqVO统一用印流程发起
- `workflowId` (String):流程模板 ID必填通常由配置 `iwork.workflow-id` 提供;仅用于向 iWork 指定模板,不再作为业务标识。
- `jbr`用印申请人iWork 人员 ID必填
- `yybm`:用印部门 ID必填
- `fb`:用印单位/分部 ID必填
- `sqsj`申请时间yyyy-MM-dd必填
- `yyqx`:用印去向(必填)。
- `yyfkUrl`:用印依据附件 URL可选
- `yysy`:用印事由或摘要(可选)。
- `xyywjUrl`:用印材料附件 URL必填
- `yysx`:用印事项(必填)。
- `ywxtdjbh`:业务系统单据编号(必填,同时用于生成流程标题“用印-{ywxtdjbh}”)。
- `bizCallbackKey` (String)业务回调标识≤255 字符,回调时按该标识分发到对应业务回调入口(可选但推荐)。
- 额外字段不再支持Service 会根据以上字段自动补齐固定流程类型 (`lclx=2979600781334966993`) 与签署动作 (`qsdz=CORPORATE`)。
- IWorkWorkflowVoidReqVO作废
- `requestId` (String):流程请求编号(必填,唯一标识)。
- `reason``extraParams``formExtras` 等用于传递作废原因或额外字段。
- IWorkFormFieldVO表单字段
- `fieldName` (String):字段名(必填),与 iWork 表单字段 key 对应。
- `fieldValue` (String):字段值(必填)。
- IWorkDetailRecordVO明细记录
- `recordOrder` (Integer):可选记录序号(从 0 开始),用于 iWork 明细排序。
- `fields` (List&lt;IWorkFormFieldVO&gt;):该明细行下的字段集合(必填)。
- IWorkDetailTableVO明细表
- `tableDBName` (String)iWork 明细表表名(必填,如 `formtable_main_26_dt1`)。
- `records` (List&lt;IWorkDetailRecordVO&gt;):明细记录集合(必填)。
---
## Java内部调用示例
项目同时提供 `IWorkIntegrationService` Bean可直接注入并调用
```java
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOperationRespVO;
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkWorkflowCreateReqVO;
import com.zt.plat.module.system.service.integration.iwork.IWorkIntegrationService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@RequiredArgsConstructor
@Component
public class MyService {
private final IWorkIntegrationService iworkService;
public void startSealFlow() {
IWorkWorkflowCreateReqVO req = new IWorkWorkflowCreateReqVO();
req.setWorkflowId("54");
req.setJbr("1001");
req.setYybm("2001");
req.setFb("3001");
req.setSqsj("2025-01-01");
req.setYyqx("对外邮寄");
req.setYyfkUrl("https://files.example.com/evidence.pdf");
req.setYysy("与客户合同用印");
req.setXyywjUrl("https://files.example.com/contract.pdf");
req.setYysx("合同用印");
req.setYwxtdjbh("DJ-2025-0001");
IWorkOperationRespVO resp = iworkService.createWorkflow(req);
if (resp.isSuccess()) {
// 处理成功,例如记录 requestId
}
}
}
```
说明:
- 无需设置 appId系统强制使用配置项 `iwork.app-id`
- `workflowId` 通常来自配置 `iwork.workflow-id`,也可在请求中指定模板编号,但流程标识一律以返回的 requestId 为准。
- 可设置 `bizCallbackKey` 以便回调时分发到对应业务处理;建议在发起后立即记录响应中的 requestId 及 bizCallbackKey。
- 若希望以特定 iWork 操作人发起,可设置 `req.setOperatorUserId("1001")`
---
## HTTP外部调用示例cURL
1. Resolve user
```bash
curl -X POST \
-H "Content-Type: application/json" \
-d '{
"identifierKey":"loginid",
"identifierValue":"zhangsan"
}' \
https://your-zt-server/admin-api/system/integration/iwork/user/resolve
```
成功返回示例CommonResult 包装):
```json
{
"code": 0,
"msg": "success",
"data": {
"userId": "1001",
"success": true,
"payload": { ... },
"rawBody": "{...}"
}
}
```
1. Create workflow
```bash
curl -X POST -H "Content-Type: application/json" -d '{
"workflowId":"54",
"jbr":"1001",
"yybm":"2001",
"fb":"3001",
"sqsj":"2025-01-01",
"yyqx":"对外邮寄",
"yyfkUrl":"https://files.example.com/evidence.pdf",
"yysy":"与客户合同用印",
"xyywjUrl":"https://files.example.com/contract.pdf",
"yysx":"合同用印",
"ywxtdjbh":"DJ-2025-0001",
"bizCallbackKey":"seal-flow-callback"
}' https://your-zt-server/admin-api/system/integration/iwork/workflow/create
```
> 说明:外部仍以 JSON 请求调用本服务,系统在向 iWork 转发时会自动将负载转换为 `application/x-www-form-urlencoded` 表单(含 `requestName`、`workflowId`、`mainData` 等字段)。
> 响应的 `data.requestId` 为唯一业务标识请在业务侧保存appId 始终取配置;`xyywjFileName` 不存储。
1. Void workflow
```bash
curl -X POST -H "Content-Type: application/json" -d '{
"requestId":"REQ-001",
"reason":"作废原因"
}' https://your-zt-server/admin-api/system/integration/iwork/workflow/void
```
---
## 核心逻辑与细节
1. 基础参数解析
appId始终取配置 `iwork.app-id`,忽略请求值。
主标识:全链路仅使用 `requestId`(发起返回、作废入参、日志查询、回调分发、重试都基于 requestId
模板:`workflowId` 仅用于选择 iWork 模板,不作为业务标识。
业务回调标识:`bizCallbackKey`字符串≤255发起时提交回调按该标识分发到具体业务回调入口。
附件文件名:`xyywjFileName` 不存储也不参与处理;仅使用 `xyywjUrl`
调用时可在请求体中指定 `workflowId` 以选择模板;若未显式传入,则回退到全局 `iwork.workflow-id`,若仍为空则抛出 `IWORK_WORKFLOW_ID_MISSING`
POST `/system/integration/iwork/workflow/create`:发起用印流程,返回 `requestId`
POST `/system/integration/iwork/workflow/void`:作废流程,请求需携带 `requestId`
POST `/system/integration/iwork/callback/file`(上游回调):接收 iWork 回调,校验 `requestId`,若缺 `bizCallbackKey` 则记录失败但不中断附件保存;下载并落库附件,记录回调日志(截断原文),并在提供 `bizCallbackKey` 时通过 RocketMQ 分发业务回调(自动/手工重试)。
POST `/system/integration/iwork/log/page`分页查询用印回调日志requestId / 业务单号 / bizCallbackKey / 状态 / 时间段)。
POST `/system/integration/iwork/log/retry`:手工重试业务回调,需权限,入参 `requestId`(状态为失败/超重试时可用)。
1. 向 iWork 的 `register` 接口发起请求Headers 包含 appId 与 clientPublicKey
2. 从注册响应中获取 `secret``spk`(服务端公钥),使用本地的 client 公钥做 RSA 加密(`spk` 用于加密),得到加密后的 secret 与 encryptedUserId。
发起:`workflowId``jbr``yybm``fb``sqsj``yyqx``xyywjUrl``yysx``ywxtdjbh` 必填;`bizCallbackKey` 可选但建议提供appId 忽略。
作废:`requestId` 必填;`reason` 可选。
回调:若无法找到业务附件(或不存在),标记用印失败并记日志,不抛出未捕获异常;记录原始回调文本(截断)。
- 当 token 接近到期(`refresh-ahead-seconds`)时会在下一次请求触发刷新。
1) 发起:校验必填 → 使用配置 appId 与模板 workflowId → 申请/缓存 token → 以 form-urlencoded 调 iWork → 返回 `requestId`,记录发起日志状态。
2) 回调(/callback/file校验 requestId + bizCallbackKey → 校验租户/必备字段 → 下载/保存附件(若缺业务附件则直接标记失败并记日志)→ 写入回调日志,保存原始回调请求/响应(截断)→ 通过 RocketMQ 发送业务回调消息topic=`SYSTEM_IWORK_BIZ_CALLBACK`tag=`bizCallbackKey`)。
3) 业务处理 & 结果上报(跨模块/跨进程):业务模块订阅 `SYSTEM_IWORK_BIZ_CALLBACK` 对应 tag 处理后,将结果发布到 `SYSTEM_IWORK_BIZ_CALLBACK_RESULT`tag 同 bizCallbackKey携带 requestId / bizCallbackKey / success / errorMessage / payload / attempt / maxAttemptssystem 模块消费结果并更新日志,失败且未超限时由 system 端按配置延迟重试再次投递回调消息。
4) 重试:默认 3 次、间隔 5 秒,可配置(`iwork.callback.retry.*`),手工重试仍使用 `/log/retry`,由 system 模块重新投递 MQ 消息。
5) 手工重试:需具备 iWork 模块权限;根据 requestId 将任务重新投递至回调分发器,不增加自动重试计数,可在日志详情中发起。
1. 响应解析
`CREATE_PENDING` / `CREATE_SUCCESS` / `CREATE_FAILED`
`CALLBACK_PENDING` / `CALLBACK_SUCCESS` / `CALLBACK_FAILED`
`CALLBACK_RETRYING` / `CALLBACK_RETRY_FAILED`
---
## 用印流程日志与业务回调
### 范围与标识
- appId始终取配置 `iwork.app-id`,忽略请求值。
- 主标识:全链路仅使用 `requestId`(发起返回、作废入参、日志查询、回调分发、重试都基于 requestId
- 模板:`workflowId` 仅用于选择 iWork 模板,不作为业务标识。
- 业务回调标识:`bizCallbackKey`字符串≤255发起时提交回调按该标识分发到具体业务回调入口。
- 附件文件名:`xyywjFileName` 不存储也不参与处理;仅使用 `xyywjUrl`
### 入口REST
- POST `/system/integration/iwork/workflow/create`:发起用印流程,返回 `requestId`
- POST `/system/integration/iwork/workflow/void`:作废流程,请求需携带 `requestId`
- POST `/system/integration/iwork/callback/file`(上游回调):接收 iWork 回调,校验 `requestId`,若缺 `bizCallbackKey` 则记录失败但不中断附件保存;下载并落库附件,记录回调日志(截断原文),并在提供 `bizCallbackKey` 时通过 RocketMQ 分发业务回调(自动/手工重试)。
- POST `/system/integration/iwork/log/page`分页查询用印回调日志requestId / 业务单号 / bizCallbackKey / 状态 / 时间段)。
- POST `/system/integration/iwork/log/retry`:手工重试业务回调,需权限,入参 `requestId`(状态为失败/超重试时可用)。
### 必填/校验要点
- 发起:`workflowId``jbr``yybm``fb``sqsj``yyqx``xyywjUrl``yysx``ywxtdjbh` 必填;`bizCallbackKey` 可选但建议提供appId 忽略。
- 作废:`requestId` 必填;`reason` 可选。
- 回调:若无法找到业务附件(或不存在),标记用印失败并记日志,不抛出未捕获异常;记录原始回调文本(截断)。
### 处理流程(摘要)
1) 发起:校验必填 → 使用配置 appId 与模板 workflowId → 申请/缓存 token → 以 form-urlencoded 调 iWork → 返回 `requestId`,记录发起日志状态。
2) 回调(/callback/file校验 requestId + bizCallbackKey → 校验租户/必备字段 → 下载/保存附件(若缺业务附件则直接标记失败并记日志)→ 写入回调日志,保存原始回调请求/响应(截断)→ 通过 RocketMQ 发送业务回调消息topic=`SYSTEM_IWORK_BIZ_CALLBACK`tag=`bizCallbackKey`)。
3) 业务处理 & 结果上报(跨模块/跨进程):业务模块订阅 `SYSTEM_IWORK_BIZ_CALLBACK` 对应 tag 处理后,将结果发布到 `SYSTEM_IWORK_BIZ_CALLBACK_RESULT`tag 同 bizCallbackKey携带 requestId / bizCallbackKey / success / errorMessage / payload / attempt / maxAttemptssystem 模块消费结果并更新日志,失败且未超限时由 system 端按配置延迟重试再次投递回调消息。
4) 重试:默认 3 次、间隔 5 秒,可配置(`iwork.callback.retry.*`),手工重试仍使用 `/log/retry`,由 system 模块重新投递 MQ 消息。
5) 手工重试:需具备 iWork 模块权限;根据 requestId 将任务重新投递至回调分发器,不增加自动重试计数,可在日志详情中发起。
### 状态字面量(示例)
- `CREATE_PENDING` / `CREATE_SUCCESS` / `CREATE_FAILED`
- `CALLBACK_PENDING` / `CALLBACK_SUCCESS` / `CALLBACK_FAILED`
- `CALLBACK_RETRYING` / `CALLBACK_RETRY_FAILED`
> 实际实现可根据需要细化,唯一标识均为 requestId。
### 重试与配置
- 自动重试:默认 `maxAttempts=3``delaySeconds=5`,可配置(示例键:`iwork.callback.retry.max-attempts``iwork.callback.retry.delay-seconds`)。
- 手工重试:权限校验(沿用 iWork 模块权限前缀);仅在失败/超重试状态下开放。
- 幂等:按 requestId + bizCallbackKey 进行幂等检查,成功后不再重复分发,除非手工强制重试。
### 日志存储
- 表:`system_iwork_seal_callback_log`(示例字段)
- `requestId`PK`businessCode`(ywxtdjbh)、`bizCallbackKey``status``retryCount``lastErrorMessage``fileUrl``fileId``businessFileId``requestBody``responseBody``rawCallback`(截断保存原文)、`lastCallbackTime``creator``createTime``updateTime`
- 字段要点:
- `rawCallback`:保存回调原始文本,截断存储(无需脱敏,不注明上限)。
- 无业务附件或保存失败:写 `status=CREATE_FAILED` / `CALLBACK_FAILED` 并记录错误原因。
### 查询与展示
- 分页:沿用 `PageParam` 约束,`pageNo` 默认 1、`pageSize` 默认 10、上限 10000仅分页浏览不支持导出。
- 查询条件:`requestId``businessCode`(ywxtdjbh)、`bizCallbackKey``status`、时间范围createTime / lastCallbackTime
- 列表字段requestId、业务单号、bizCallbackKey、状态、重试次数、最后错误、更新时间。
- 详情:展示截断的回调原文(请求/响应)、错误原因、附件信息;提供“手工重试”按钮(需权限)。
---
## 常见错误与排查
- baseUrl 未配置IWORK_BASE_URL_MISSING
- 处理:确保 `iwork.base-url` 配置正确。
- 配置缺失IWORK_CONFIGURATION_INVALID
- 场景:`app-id``client-public-key``user-id` 等关键字段没有配置或只包含空白字符。
- 处理:在 `application.yml` 或配置中心中补充对应字段,确保它们与 iWork 侧一致。
- 用印必填字段缺失IWORK_SEAL_REQUIRED_FIELD_MISSING
- 场景:`workflowId``jbr``yybm``fb``sqsj``yyqx``xyywjUrl``yysx``ywxtdjbh` 等任一字段为空。
- 处理:根据返回的字段名称补齐相应值后重新发起。
- RSA 加密/注册/申请 token 失败IWORK_REGISTER_FAILED / IWORK_APPLY_TOKEN_FAILED / IWORK_REMOTE_REQUEST_FAILED
- 处理:通过日志查看 iWork 返回的 HTTP 状态码与 body确认请求头/路径/参数是否匹配 iWork 网关要求。
- 用户解析失败
- 确认 `identifierKey`/`identifierValue` 是否正确填写并与 iWork 的查询接口契合;可启用 `forceRefreshToken` 触发 session 刷新以排除 token 过期造成的问题。
---
## 进阶主题
- 并发与缓存
- `sessionCache` 最大条目数为 256若高并发/多凭证/多操作人场景,可能需调整容量。
- 超时与 HTTP 客户端
- `IWorkProperties.client.response-timeout` 可用于设定响应超时;连接超时通常由 Reactor Netty 全局配置控制。
- 单元测试
- 项目中已有 MockWebServer 测试样例(`IWorkIntegrationServiceImplTest`),可参考测试用例模拟 iWork 的注册、申请 token、用户查询、创建/作废流程的交互。
---
## 小结与建议
- 在配置中补齐 `iwork.app-id``iwork.client-public-key``iwork.user-id``iwork.workflow-id` 等关键字段,并按需配置回调重试参数(默认 3 次5 秒间隔)。
- 优先在本地通过 `IWorkIntegrationService` Java API 调试,成功后再通过 Controller 的 REST 接口对外暴露;发起后请记录返回的 requestId唯一标识与 bizCallbackKey。
- 若遇到请求失败,查看应用日志(`[iWork]` 前缀的日志)与 iWork 网关返回 body若业务回调失败可在日志页面查看截断原文并按权限发起手工重试。
文档已生成并保存到:`docs/iWork集成说明.md`