Files
zt-dsc/docs/iWork集成说明.md
2025-11-28 18:12:00 +08:00

334 lines
14 KiB
Markdown
Raw Permalink 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、公钥以及默认流程编号无需再维护多套凭证。
---
## 配置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 # 在到期前多少秒认为需要刷新
client:
connect-timeout: 5s
response-timeout: 30s
```
说明:
- `base-url` 为 iWork 网关的基础地址,不能留空。
- `app-id``client-public-key` 共同构成注册/申请 token 所需的凭据信息,由配置统一提供,不再支持多套切换。
- `workflow-id` 提供全局默认流程编号,单次调用也可通过 `workflowId` 覆盖。
- 请求头键名固定为 `app-id``client-public-key``secret``token``time``user-id`,无需在配置中重复声明。
- `org.*` 配置负责 iWork 人力组织 REST 代理:`token-seed` 为与 iWork 约定的标识,系统会自动将其与毫秒时间戳拼接并计算 MD5 生成 `key`,无需额外传递 token。
---
## 典型调用路径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必填不再回退到配置。
- `jbr`用印申请人iWork 人员 ID必填
- `yybm`:用印部门 ID必填
- `fb`:用印单位/分部 ID必填
- `sqsj`申请时间yyyy-MM-dd必填
- `yyqx`:用印去向(必填)。
- `yyfkUrl`:用印依据附件 URL可选
- `yysy`:用印事由或摘要(可选)。
- `xyywjUrl`:用印材料附件 URL必填
- `yysx`:用印事项(必填)。
- `ywxtdjbh`:业务系统单据编号(必填,同时用于生成流程标题“用印-{ywxtdjbh}”)。
- 额外字段不再支持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
}
}
}
```
说明:
- 若需使用特定凭证,可设置 `req.setAppId("my-iwork-app")`
- 若需覆盖默认流程模板,可调用 `req.setWorkflowId(123L)` 指定。
- 若希望以特定 iWork 操作人发起,可设置 `req.setOperatorUserId("1001")`
---
## HTTP外部调用示例cURL
1. Resolve user
```bash
curl -X POST \
-H "Content-Type: application/json" \
-d '{
"appId":"my-iwork-app",
"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"
}' https://your-zt-server/admin-api/system/integration/iwork/workflow/create
```
> 说明:外部仍以 JSON 请求调用本服务,系统在向 iWork 转发时会自动将负载转换为 `application/x-www-form-urlencoded` 表单(含 `requestName`、`workflowId`、`mainData` 等字段)。
1. Void workflow
```bash
curl -X POST -H "Content-Type: application/json" -d '{
"requestId":"REQ-001",
"reason":"作废原因",
"appId":"my-iwork-app"
}' https://your-zt-server/admin-api/system/integration/iwork/workflow/void
```
---
## 核心逻辑与细节
1. 基础参数解析
系统始终使用 `application.yml` 中配置的 `app-id``client-public-key` 与 iWork 通信。
请求体中的 `appId` 字段仅为兼容历史调用而保留,框架内部不会使用该值做切换。
1. Workflow 模板解析
调用时优先使用请求体中的 `workflowId`
若未显式传入,则回退到全局 `iwork.workflow-id`,若仍为空则抛出 `IWORK_WORKFLOW_ID_MISSING`
1. 注册 + RSA + Token
- 在首次或 token 过期时,会按以下步骤获取 session
1. 向 iWork 的 `register` 接口发起请求Headers 包含 appId 与 clientPublicKey
2. 从注册响应中获取 `secret``spk`(服务端公钥),使用本地的 client 公钥做 RSA 加密(`spk` 用于加密),得到加密后的 secret 与 encryptedUserId。
3. 使用注册返回的密钥申请 tokenapply-tokentoken 会被按 `ttl-seconds` 缓存。
- `IWorkIntegrationServiceImpl` 中维护一个 Caffeine `sessionCache`,缓存 key 为 `appId::operatorUserId`
- 当 token 接近到期(`refresh-ahead-seconds`)时会在下一次请求触发刷新。
1. 请求构造
- 用户解析、作废等场景继续以 `application/json` 调用 iWork。
- 用印流程发起在转发至 iWork 时改为 `application/x-www-form-urlencoded` 表单,请求正文包含 `requestName``workflowId` 及字符串化后的 `mainData`,与 iWork 网关当前要求保持一致。
- 认证 Header`IWorkProperties.Headers` 中的常量控制,固定键名为 `app-id``client-public-key``secret``token``time``user-id`
1. 响应解析
- 实现里对响应成功的判定比较宽松:检查 `code``status``success``errno` 等字段(支持布尔、字符串 0/1/success以判断是否成功并解析常见的 message 字段 `msg|message|errmsg`
---
## 常见错误与排查
- 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` 等关键字段。
- 优先在本地通过 `IWorkIntegrationService` Java API 调试,成功后再通过 Controller 的 REST 接口对外暴露。
- 若遇到请求失败,查看应用日志(`[iWork]` 前缀的日志)与 iWork 网关返回 body定位是注册、申请 token还是业务接口user-info/create/void失败。
文档已生成并保存到:`docs/iWork集成说明.md`