1. 新增 iwork 同步用户组织信息接口
2. 修复错误设置版本信息在 zt-dependencies 的 bug
This commit is contained in:
340
docs/iWork集成说明.md
Normal file
340
docs/iWork集成说明.md
Normal file
@@ -0,0 +1,340 @@
|
||||
# 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 响应中解析出的用户编号(如果能解析到)。
|
||||
- `payload` / `rawBody`:原始返回信息。
|
||||
- `success` / `message`:调用成功标志与提示信息。
|
||||
|
||||
- IWorkWorkflowCreateReqVO(发起流程)
|
||||
- `requestName` (String):流程标题。
|
||||
- `workflowId` (Long):流程模板 ID(可选,缺省时使用配置的默认值)。
|
||||
- `mainFields` (`List<IWorkFormFieldVO>`):主表字段集合。
|
||||
- `detailTables` (`List<IWorkDetailTableVO>`):明细表集合(可选)。
|
||||
- `otherParams` / `formExtras`:额外参数,`formExtras` 会以 form-data 方式追加。
|
||||
|
||||
- IWorkWorkflowVoidReqVO(作废)
|
||||
- `requestId` (String):流程请求编号(必填)。
|
||||
- `reason`、`extraParams`、`formExtras` 等用于传递作废原因或额外字段。
|
||||
|
||||
- IWorkFormFieldVO(表单字段)
|
||||
- `fieldName` (String):字段名(必填),与 iWork 表单字段 key 对应。
|
||||
- `fieldValue` (String):字段值(必填)。
|
||||
|
||||
- IWorkDetailRecordVO(明细记录)
|
||||
- `recordOrder` (Integer):可选记录序号(从 0 开始),用于 iWork 明细排序。
|
||||
- `fields` (List<IWorkFormFieldVO>):该明细行下的字段集合(必填)。
|
||||
|
||||
- IWorkDetailTableVO(明细表)
|
||||
- `tableDBName` (String):iWork 明细表表名(必填,如 `formtable_main_26_dt1`)。
|
||||
- `records` (List<IWorkDetailRecordVO>):明细记录集合(必填)。
|
||||
|
||||
---
|
||||
|
||||
## Java(内部)调用示例
|
||||
|
||||
项目同时提供 `IWorkIntegrationService` Bean,可直接注入并调用:
|
||||
|
||||
```java
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkDetailRecordVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkDetailTableVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFormFieldVO;
|
||||
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;
|
||||
import java.util.List;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
public class MyService {
|
||||
private final IWorkIntegrationService iworkService;
|
||||
|
||||
public void startFlow() {
|
||||
IWorkWorkflowCreateReqVO req = new IWorkWorkflowCreateReqVO();
|
||||
// 使用 application.yml 中配置的 app-id,无需额外指定
|
||||
req.setRequestName("测试-创建流程");
|
||||
// 若需要覆盖配置的默认流程,可显式设置 workflowId
|
||||
// req.setWorkflowId(54L);
|
||||
|
||||
// 主表字段
|
||||
IWorkFormFieldVO nameField = new IWorkFormFieldVO();
|
||||
nameField.setFieldName("name");
|
||||
nameField.setFieldValue("张三");
|
||||
|
||||
IWorkFormFieldVO amountField = new IWorkFormFieldVO();
|
||||
amountField.setFieldName("amount");
|
||||
amountField.setFieldValue("1000");
|
||||
req.setMainFields(List.of(nameField, amountField));
|
||||
|
||||
// 明细表(可选)
|
||||
IWorkFormFieldVO detailField = new IWorkFormFieldVO();
|
||||
detailField.setFieldName("itemName");
|
||||
detailField.setFieldValue("办公用品");
|
||||
|
||||
IWorkDetailRecordVO record = new IWorkDetailRecordVO();
|
||||
record.setRecordOrder(0);
|
||||
record.setFields(List.of(detailField));
|
||||
|
||||
IWorkDetailTableVO detailTable = new IWorkDetailTableVO();
|
||||
detailTable.setTableDBName("formtable_main_26_dt1");
|
||||
detailTable.setRecords(List.of(record));
|
||||
req.setDetailTables(List.of(detailTable));
|
||||
|
||||
IWorkOperationRespVO resp = iworkService.createWorkflow(req);
|
||||
if (resp.isSuccess()) {
|
||||
// 处理成功,例如记录 requestId
|
||||
} else {
|
||||
// 日志或重试
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- 若需使用特定凭证,可设置 `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 '{
|
||||
"requestName":"测试REST创建流程",
|
||||
"workflowId":54,
|
||||
"mainFields":[{"fieldName":"name","fieldValue":"张三"}],
|
||||
"appId":"my-iwork-app"
|
||||
}' https://your-zt-server/admin-api/system/integration/iwork/workflow/create
|
||||
```
|
||||
|
||||
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. 使用注册返回的密钥申请 token(apply-token),token 会被按 `ttl-seconds` 缓存。
|
||||
|
||||
- `IWorkIntegrationServiceImpl` 中维护一个 Caffeine `sessionCache`,缓存 key 为 `appId::operatorUserId`。
|
||||
- 当 token 接近到期(`refresh-ahead-seconds`)时会在下一次请求触发刷新。
|
||||
|
||||
1. 请求构造
|
||||
|
||||
- JSON 请求使用 `application/json`,表单请求(如创建流程/作废)使用 `application/x-www-form-urlencoded`。
|
||||
- 认证 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_WORKFLOW_ID_MISSING)
|
||||
- 场景:请求体、凭证与全局配置均未提供流程模板编号。
|
||||
- 处理:在请求中指定 `workflowId`,或在配置中设置 `workflow-id` / 凭证级 `default-workflow-id`。
|
||||
|
||||
- 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`。
|
||||
Reference in New Issue
Block a user