Преглед на файлове

chore(openspec): archive Phase B 3 个专项 + 更新索引

openspec archive 统一归档:
- 2026-04-19-extend-yida-api-coverage      → specs/yida-form-atomic + yida-process-atomic
- 2026-04-19-extend-dingtalk-contacts-api  → specs/dingtalk-contacts-v2
- 2026-04-19-add-request-auth-replay-guard → specs/request-auth + replay-guard

三者代码均已落地、mvn compile + mvn package 全 reactor 通过,归档后稳态 spec 自动
合并到 openspec/specs/ 目录。

仍 active 的 3 个 change(有明确未完 tasks):
- add-observability-foundation  12/14 待生产冒烟
- add-mjava-pro                 15/30 TenantTaskDecorator/DynamicService 延后
- add-mjava-com                 19/30 首批 Action 注册待补

同步更新 CLAUDE.md 与 BACKLOG.md 的归档状态。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
malk преди 2 седмици
родител
ревизия
4fcc72ca6f
променени са 21 файла, в които са добавени 400 реда и са изтрити 13 реда
  1. 13 9
      CLAUDE.md
  2. 9 4
      openspec/BACKLOG.md
  3. 0 0
      openspec/changes/archive/2026-04-19-add-request-auth-replay-guard/design.md
  4. 0 0
      openspec/changes/archive/2026-04-19-add-request-auth-replay-guard/proposal.md
  5. 0 0
      openspec/changes/archive/2026-04-19-add-request-auth-replay-guard/specs/replay-guard/spec.md
  6. 0 0
      openspec/changes/archive/2026-04-19-add-request-auth-replay-guard/specs/request-auth/spec.md
  7. 0 0
      openspec/changes/archive/2026-04-19-add-request-auth-replay-guard/tasks.md
  8. 0 0
      openspec/changes/archive/2026-04-19-extend-dingtalk-contacts-api/design.md
  9. 0 0
      openspec/changes/archive/2026-04-19-extend-dingtalk-contacts-api/proposal.md
  10. 0 0
      openspec/changes/archive/2026-04-19-extend-dingtalk-contacts-api/specs/dingtalk-contacts-v2/spec.md
  11. 0 0
      openspec/changes/archive/2026-04-19-extend-dingtalk-contacts-api/tasks.md
  12. 0 0
      openspec/changes/archive/2026-04-19-extend-yida-api-coverage/design.md
  13. 0 0
      openspec/changes/archive/2026-04-19-extend-yida-api-coverage/proposal.md
  14. 0 0
      openspec/changes/archive/2026-04-19-extend-yida-api-coverage/specs/yida-form-atomic/spec.md
  15. 0 0
      openspec/changes/archive/2026-04-19-extend-yida-api-coverage/specs/yida-process-atomic/spec.md
  16. 0 0
      openspec/changes/archive/2026-04-19-extend-yida-api-coverage/tasks.md
  17. 99 0
      openspec/specs/dingtalk-contacts-v2/spec.md
  18. 62 0
      openspec/specs/replay-guard/spec.md
  19. 66 0
      openspec/specs/request-auth/spec.md
  20. 72 0
      openspec/specs/yida-form-atomic/spec.md
  21. 79 0
      openspec/specs/yida-process-atomic/spec.md

+ 13 - 9
CLAUDE.md

@@ -19,15 +19,19 @@ Java 后端基座 + 客户子项目仓库。Spring Boot 2.2.13 + MySQL,第一
 | `/opsx:apply` | 执行 tasks.md 的实现步骤 |
 | `/opsx:archive` | 完成后归档到 `openspec/changes/archive/` |
 
-现有 change 状态:
-- `changes/archive/2026-04-18-extract-dingtalk-standard-api/` — 已归档(crypto-utils 稳态 spec 合并)
-- `changes/add-observability-foundation/` — 代码已实施,待生产冒烟
-- `changes/init-project-baseline/` — 基线沉淀,仅 `mvn compile` 验证阻塞在 Maven 未装
-- `changes/extend-yida-api-coverage/` — **Phase B.1 最高优先级**:宜搭表单+流程 API 对齐
-- `changes/extend-dingtalk-contacts-api/` — Phase B.1:钉钉通讯录 API 对齐
-- `changes/add-request-auth-replay-guard/` — Phase B.2:基座请求鉴权 + 防重放(mjava-com 的前置依赖)
-- `changes/add-mjava-pro/` — Phase C:多客户单部署(待 B 完成后)
-- `changes/add-mjava-com/` — Phase C:通用能力 BaaS(待 B 完成后,依赖 add-request-auth-replay-guard)
+现有 change 状态(2026-04-19 更新):
+
+已归档(`changes/archive/`):
+- `2026-04-18-extract-dingtalk-standard-api` → `specs/crypto-utils/`
+- `2026-04-19-init-project-baseline` → `specs/project-baseline.md`
+- `2026-04-19-extend-yida-api-coverage` → `specs/yida-form-atomic/` + `specs/yida-process-atomic/`
+- `2026-04-19-extend-dingtalk-contacts-api` → `specs/dingtalk-contacts-v2/`
+- `2026-04-19-add-request-auth-replay-guard` → `specs/request-auth/` + `specs/replay-guard/`
+
+进行中(`changes/`):
+- `add-observability-foundation` 12/14 — 待生产冒烟
+- `add-mjava-pro` 15/30 — 骨架完成,TenantTaskDecorator / DynamicDDService 延后
+- `add-mjava-com` 19/30 — 骨架完成,DingtalkActionRegistry / AliworkActionRegistry 首批 action 待补
 
 ## 快速操作
 

+ 9 - 4
openspec/BACKLOG.md

@@ -74,11 +74,16 @@
 ✅ Phase B.1 extend-yida-api-coverage       代码落地 + mvn compile 通过
 ✅ Phase B.1 extend-dingtalk-contacts-api   代码落地 + mvn compile 通过
 ✅ Phase B.2 add-request-auth-replay-guard  代码落地 + mvn compile 通过
-✅ Phase C   add-mjava-pro                  骨架 + mvn compile 通过
-✅ Phase C   add-mjava-com                  骨架 + mvn compile 通过
+✅ Phase C   add-mjava-pro                  骨架 + mvn compile 通过(仍 active,15/30)
+✅ Phase C   add-mjava-com                  骨架 + mvn compile 通过(仍 active,19/30)
 ✅ 全 reactor 6 模块 mvn package -DskipTests 全部打包成功(2026-04-19)
-✅ Archive: extract-dingtalk-standard-api (2026-04-18)
-✅ Archive: init-project-baseline (2026-04-19)
+
+归档(6 changes,稳态 spec 已合并到 openspec/specs/):
+✅ 2026-04-18 extract-dingtalk-standard-api → crypto-utils
+✅ 2026-04-19 init-project-baseline         → project-baseline.md
+✅ 2026-04-19 extend-yida-api-coverage      → yida-form-atomic + yida-process-atomic
+✅ 2026-04-19 extend-dingtalk-contacts-api  → dingtalk-contacts-v2
+✅ 2026-04-19 add-request-auth-replay-guard → request-auth + replay-guard
 
 📋 剩余:
 - add-observability-foundation 2 项运行冒烟(java -jar 启动验证 actuator)

openspec/changes/add-request-auth-replay-guard/design.md → openspec/changes/archive/2026-04-19-add-request-auth-replay-guard/design.md


openspec/changes/add-request-auth-replay-guard/proposal.md → openspec/changes/archive/2026-04-19-add-request-auth-replay-guard/proposal.md


openspec/changes/add-request-auth-replay-guard/specs/replay-guard/spec.md → openspec/changes/archive/2026-04-19-add-request-auth-replay-guard/specs/replay-guard/spec.md


openspec/changes/add-request-auth-replay-guard/specs/request-auth/spec.md → openspec/changes/archive/2026-04-19-add-request-auth-replay-guard/specs/request-auth/spec.md


openspec/changes/add-request-auth-replay-guard/tasks.md → openspec/changes/archive/2026-04-19-add-request-auth-replay-guard/tasks.md


openspec/changes/extend-dingtalk-contacts-api/design.md → openspec/changes/archive/2026-04-19-extend-dingtalk-contacts-api/design.md


openspec/changes/extend-dingtalk-contacts-api/proposal.md → openspec/changes/archive/2026-04-19-extend-dingtalk-contacts-api/proposal.md


openspec/changes/extend-dingtalk-contacts-api/specs/dingtalk-contacts-v2/spec.md → openspec/changes/archive/2026-04-19-extend-dingtalk-contacts-api/specs/dingtalk-contacts-v2/spec.md


openspec/changes/extend-dingtalk-contacts-api/tasks.md → openspec/changes/archive/2026-04-19-extend-dingtalk-contacts-api/tasks.md


openspec/changes/extend-yida-api-coverage/design.md → openspec/changes/archive/2026-04-19-extend-yida-api-coverage/design.md


openspec/changes/extend-yida-api-coverage/proposal.md → openspec/changes/archive/2026-04-19-extend-yida-api-coverage/proposal.md


openspec/changes/extend-yida-api-coverage/specs/yida-form-atomic/spec.md → openspec/changes/archive/2026-04-19-extend-yida-api-coverage/specs/yida-form-atomic/spec.md


openspec/changes/extend-yida-api-coverage/specs/yida-process-atomic/spec.md → openspec/changes/archive/2026-04-19-extend-yida-api-coverage/specs/yida-process-atomic/spec.md


openspec/changes/extend-yida-api-coverage/tasks.md → openspec/changes/archive/2026-04-19-extend-yida-api-coverage/tasks.md


+ 99 - 0
openspec/specs/dingtalk-contacts-v2/spec.md

@@ -0,0 +1,99 @@
+# dingtalk-contacts-v2 Specification
+
+## Purpose
+TBD - created by archiving change extend-dingtalk-contacts-api. Update Purpose after archive.
+## Requirements
+### Requirement: 用户管理完整覆盖
+
+`DDClient_Contacts` SHALL 提供对钉钉官方通讯录用户管理 endpoint 的完整对齐。所有方法 MUST 遵守 `mjava-baseline §3.4.2` 的签名规则。
+
+#### Scenario: 创建用户(完整字段)
+
+- **WHEN** 调用 `createUser_v2(access_token, name, mobile, dept_id_list, body_ext)`
+- **THEN** `body_ext` 必须支持官方文档列出的所有可选字段(userid / hired_date / job_number / title / email / senior_mode / extension / ...)
+- **AND** javadoc 必须枚举这些字段并注明类型
+
+#### Scenario: 更新用户
+
+- **WHEN** 调用 `updateUser(access_token, userid, body_ext)`
+- **THEN** `userid` 必填,其他字段全部通过 `body_ext` 传入
+- **AND** 不传的字段保持钉钉侧原值不变
+
+#### Scenario: 按 unionId 查询
+
+- **WHEN** 调用 `getUserByUnionId(access_token, union_id)`
+- **THEN** 返回 userid(二次调用 `getUser_v2` 获取详情)
+
+#### Scenario: 查询管理员
+
+- **WHEN** 调用 `listAdmins(access_token)`
+- **THEN** 返回管理员 userid 列表,含主管理员与子管理员区分字段
+
+#### Scenario: 查询未激活员工
+
+- **WHEN** 调用 `listInactiveUsers(access_token, is_active, offset, size, body_ext)`
+- **THEN** 支持按激活状态过滤
+
+### Requirement: 部门管理完整覆盖
+
+`DDClient_Contacts` SHALL 提供部门 CRUD 与层级查询的完整对齐。所有方法 MUST 对应钉钉官方部门管理 endpoint。
+
+#### Scenario: 部门 CRUD
+
+- **WHEN** 依次调用 `createDepartment_v2` / `updateDepartment` / `deleteDepartment` / `getDepartment_v2`
+- **THEN** 每个方法参数完整对齐官方文档
+- **AND** `body_ext` 支持 `hide_dept` / `dept_permits` / `user_permits` / `outer_dept` / `source_identifier` 等全部可选项
+
+#### Scenario: 父部门链查询
+
+- **WHEN** 调用 `listParentByDept(access_token, dept_id)`
+- **THEN** 返回目标部门到根部门的父链
+
+### Requirement: 角色管理
+
+`DDClient_Contacts` SHALL 提供角色的增删改查与批量员工授权能力。
+
+#### Scenario: 新增角色
+
+- **WHEN** 调用 `addRole(access_token, roleName, groupId)`
+- **THEN** 返回 roleId
+
+#### Scenario: 批量分配角色
+
+- **WHEN** 调用 `addRolesForEmps(access_token, roleIds, userIds)`
+- **THEN** 一次调用同时为多员工授权多角色
+
+#### Scenario: 查询角色成员
+
+- **WHEN** 调用 `listRoleEmployees(access_token, role_id, size, offset)`
+- **THEN** 分页返回角色下员工列表
+
+### Requirement: 员工字段可见性
+
+`DDClient_Contacts` SHALL 提供员工档案字段隐藏规则的管理能力。
+
+#### Scenario: 隐藏字段设置
+
+- **WHEN** 调用 `upsertHideField(access_token, name, field, userIds, deptIds)`
+- **THEN** 对指定员工或部门隐藏指定员工档案字段
+
+#### Scenario: 查询隐藏字段
+
+- **WHEN** 调用 `listHideFields(access_token, size, offset)`
+- **THEN** 返回企业当前生效的字段隐藏规则列表
+
+### Requirement: 参数透传约束
+
+本 capability 所有方法 MUST 满足官方可选参数完整透传与文档化要求,不得因 Java 侧建模省事而过滤字段。
+
+#### Scenario: body_ext 不得过滤官方可选参数
+
+- **WHEN** 调用方通过 `body_ext` 传入官方文档支持的任意可选字段
+- **THEN** 实现必须原样透传到 HTTP 请求体,不得删除或改写
+- **AND** 方法 javadoc 必须列出所有已知 body_ext key 并注明类型
+
+#### Scenario: 方法必须有 apiNote
+
+- **WHEN** 新增任一 Client 方法
+- **THEN** javadoc 必须含 `@apiNote` 链到该 endpoint 的钉钉官方文档页
+

+ 62 - 0
openspec/specs/replay-guard/spec.md

@@ -0,0 +1,62 @@
+# replay-guard Specification
+
+## Purpose
+TBD - created by archiving change add-request-auth-replay-guard. Update Purpose after archive.
+## Requirements
+### Requirement: 时间窗校验
+
+mjava 基座 SHALL 拒绝时间戳超出配置窗口的请求,以限制重放攻击的有效窗口。
+
+#### Scenario: 时间戳在窗口内
+
+- **WHEN** `X-MJ-Timestamp` 与服务端当前时间差 ≤ `mjava.auth.window`(默认 300 秒)
+- **THEN** 进入后续 Nonce 与签名校验
+
+#### Scenario: 时间戳超窗
+
+- **WHEN** 时间差 > `window`(无论提前还是滞后)
+- **THEN** 返回 `401 { code: "AUTH_TIMESTAMP_OUT_OF_WINDOW" }`
+- **AND** 审计日志记录 `clientTimestamp` 与 `serverTimestamp` 供排查
+
+### Requirement: Nonce 去重
+
+mjava 基座 SHALL 通过 Nonce 缓存实现在时间窗内的一次性请求保证。同一 Nonce 在窗口期内只允许通过一次。
+
+#### Scenario: 首次 Nonce 被接受
+
+- **WHEN** 请求携带之前未见过的 `X-MJ-Nonce`
+- **THEN** `NonceCache.putIfAbsent(nonce)` 成功
+- **AND** 请求进入签名校验
+
+#### Scenario: Nonce 被重放
+
+- **WHEN** 请求携带之前已见过的 Nonce(仍在 TTL 内)
+- **THEN** `NonceCache.putIfAbsent` 返回 false
+- **AND** 返回 `401 { code: "AUTH_NONCE_REPLAYED" }`
+
+#### Scenario: Nonce 缓存容量上限
+
+- **WHEN** `NonceCache` 达到 `mjava.auth.nonce-cache-size` 上限
+- **THEN** 按 LRU 策略淘汰最旧条目
+- **AND** 淘汰不影响未淘汰 Nonce 的判定正确性
+
+### Requirement: Nonce 缓存 TTL 设置
+
+`NonceCache` 的 TTL MUST 略长于 `mjava.auth.window`,以避免边界时间戳被错误接受。
+
+#### Scenario: TTL 等于窗口 + 30s
+
+- **WHEN** NonceCache 初始化
+- **THEN** TTL = `mjava.auth.window + 30` 秒
+- **AND** 例:window=300 → TTL=330
+
+### Requirement: 单实例内存限制声明
+
+Phase 1 的 Nonce 去重 SHALL 在单 JVM 实例内生效;多实例部署时同一 Nonce 可能被两个实例各接受一次。这是已知限制,不在本 change 范围。
+
+#### Scenario: 多实例部署告警
+
+- **WHEN** 生产部署判定需要多实例横向扩展
+- **THEN** 运维必须评估是否升级为分布式 Nonce(Redis),独立走新 change 提案
+- **AND** 本 change 的 NonceCache 配置 `nonce-cache-size` 建议单实例足够承载高峰 QPS × window 秒的组合
+

+ 66 - 0
openspec/specs/request-auth/spec.md

@@ -0,0 +1,66 @@
+# request-auth Specification
+
+## Purpose
+TBD - created by archiving change add-request-auth-replay-guard. Update Purpose after archive.
+## Requirements
+### Requirement: HMAC 签名校验
+
+mjava 基座 SHALL 提供可配置的 HMAC-SHA256 请求签名校验机制。当 `mjava.auth.enabled=true` 时,所有非豁免请求 MUST 通过签名校验才能进入业务 Controller。
+
+#### Scenario: 合法签名请求通过
+
+- **WHEN** 请求携带完整四个 Header(`X-MJ-Key` / `X-MJ-Timestamp` / `X-MJ-Nonce` / `X-MJ-Signature`)
+- **AND** 签名 = `HMAC-SHA256(secret, timestamp + "\n" + nonce + "\n" + method + "\n" + path + "\n" + bodyHash)`
+- **THEN** 请求被放行到 Controller
+- **AND** MDC 写入 `authKey` 字段供日志追溯
+
+#### Scenario: 签名不匹配
+
+- **WHEN** 计算出的签名与 Header 中 `X-MJ-Signature` 不一致
+- **THEN** 返回 `403 { code: "AUTH_SIGNATURE_INVALID" }`
+- **AND** 响应 message 不包含期望签名值(避免泄露线索)
+- **AND** 审计日志记录 `authKey + path + latencyMs`
+
+#### Scenario: Header 缺失
+
+- **WHEN** 四个必备 Header 中任一缺失
+- **THEN** 返回 `401 { code: "AUTH_HEADER_MISSING" }`
+- **AND** 响应不说明具体缺失哪个
+
+### Requirement: 豁免机制
+
+本能力 SHALL 支持三级豁免策略,优先级从上到下:全局开关 > 路径豁免 > 注解豁免。
+
+#### Scenario: 全局关闭
+
+- **WHEN** `mjava.auth.enabled=false`
+- **THEN** 所有请求跳过鉴权,行为等同于 enable 前的裸奔状态
+
+#### Scenario: 路径豁免
+
+- **WHEN** 请求路径匹配 `mjava.auth.exempt-paths` 列表中任一 Ant 风格模式
+- **THEN** 跳过签名校验
+- **AND** 典型清单:`/actuator/**`(健康检查)、`/api/*/callback/**`(第三方 webhook)
+
+#### Scenario: 注解豁免
+
+- **WHEN** 目标 Controller 方法或其类带 `@NoAuth` 注解
+- **THEN** 签名校验跳过
+- **AND** 注解识别发生在 `HandlerInterceptor.preHandle`(能访问到 `HandlerMethod`)
+
+### Requirement: 密钥配置
+
+`secret` MUST 通过环境变量注入,禁止硬编码。`enabled=true` 但 `secret` 为空时 MUST 拒绝启动或拒绝请求。
+
+#### Scenario: secret 未配置
+
+- **WHEN** `mjava.auth.enabled=true` 但 `mjava.auth.secret` 为空或未设置
+- **THEN** 启动时打 ERROR 日志
+- **AND** 所有受保护请求返回 `503 { code: "AUTH_CONFIG_MISSING" }`
+
+#### Scenario: secret 从环境变量读取
+
+- **WHEN** `application.yml` 中 `secret: ${AUTH_SECRET}`
+- **AND** 部署环境设置 `AUTH_SECRET` 变量
+- **THEN** 启动时成功加载,后续请求按此密钥校验
+

+ 72 - 0
openspec/specs/yida-form-atomic/spec.md

@@ -0,0 +1,72 @@
+# yida-form-atomic Specification
+
+## Purpose
+TBD - created by archiving change extend-yida-api-coverage. Update Purpose after archive.
+## Requirements
+### Requirement: 表单实例 CRUD 原子接口
+
+`YDClient_Form` SHALL 提供与宜搭官方 1:1 对应的表单实例增删改查原子方法。方法签名 MUST 严格遵守 `mjava-baseline §3.4.2`(必填参数显式 + `body_ext` 承接可选参数 + javadoc 枚举全部 body_ext key + `@apiNote` 链到官方文档)。
+
+#### Scenario: 新增表单实例
+
+- **WHEN** 调用 `YDClient_Form.saveForm(conf, formUuid, formDataJson, body_ext)`
+- **THEN** 发起 `POST /v1.0/yida/forms/instances`(或旧版 `/dingtalk/yida/processes/saveFormData`)
+- **AND** `body_ext` 所有可选参数必须透传(不删、不填默认)
+- **AND** 失败时抛 `McException` 带宜搭原始错误码
+
+#### Scenario: 更新表单实例
+
+- **WHEN** 调用 `YDClient_Form.updateForm(conf, formInstanceId, updateFormDataJson, body_ext)`
+- **THEN** 必须支持 `useLatestVersion` / `ignoreEmpty` 选项(通过 body_ext 透传)
+- **AND** 默认**不改**这两个参数的宜搭侧默认行为
+
+#### Scenario: 删除表单实例
+
+- **WHEN** 调用 `YDClient_Form.deleteForm(conf, formInstanceId, body_ext)`
+- **THEN** 单条删除走 `DELETE` 接口;批量条件删除走 `deleteFormByCondition`
+
+### Requirement: 表单查询原子接口
+
+`YDClient_Form` SHALL 提供分页、ID 列表、全量含子表三种查询粒度,查询方法 MUST 分别对应宜搭官方的不同 endpoint(不混用一个方法承担多种语义)。
+
+#### Scenario: 分页查询
+
+- **WHEN** `searchForm(conf, formUuid, searchFieldJson, currentPage, pageSize, body_ext)`
+- **THEN** 对应 `POST /v1.0/yida/forms/instances/search`
+- **AND** `pageSize` 超过 100 时抛 `McException`(宜搭侧强制上限)
+
+#### Scenario: 全量查询含子表
+
+- **WHEN** `listFormsAll(conf, formUuid, currentPage, pageSize, body_ext)`
+- **THEN** 对应 `retrieve_list_all`(含子表数据),与 `searchForm`(不含子表)行为区分清楚
+
+#### Scenario: 查询组件值
+
+- **WHEN** `listComponentValues(conf, formUuid, fieldId, body_ext)`
+- **THEN** 可获取指定字段的可选项列表
+
+### Requirement: 批量操作
+
+`YDClient_Form` SHALL 提供批量新增与批量 upsert 两个方法,MUST 遵守宜搭侧每批 ≤ 100 条的限制。
+
+#### Scenario: 批量新增
+
+- **WHEN** `batchSaveForm(conf, formUuid, formDataListJson, body_ext)`
+- **THEN** 一次调用批量创建多条记录
+- **AND** 超过 100 条时必须由调用方自行分片(或方法内自动分片,策略见 Service 层)
+
+#### Scenario: 批量 upsert
+
+- **WHEN** `batchUpsertForm(conf, formUuid, searchConditionListJson, dataListJson, body_ext)`
+- **THEN** 对应批量 upsert
+- **AND** `searchCondition` 里日期字段 MUST 用字符串数组格式(规避 `selectListException`)
+
+### Requirement: 操作日志查询
+
+`YDClient_Form` SHALL 提供表单实例的操作历史查询方法。
+
+#### Scenario: 查询实例变更历史
+
+- **WHEN** `listFormOperations(conf, formInstanceId, body_ext)`
+- **THEN** 返回该实例的全部变更历史记录(含操作人 / 操作时间 / 操作类型)
+

+ 79 - 0
openspec/specs/yida-process-atomic/spec.md

@@ -0,0 +1,79 @@
+# yida-process-atomic Specification
+
+## Purpose
+TBD - created by archiving change extend-yida-api-coverage. Update Purpose after archive.
+## Requirements
+### Requirement: 流程实例生命周期
+
+`YDClient_Process` SHALL 覆盖流程发起、终止、撤回、跳转四个关键动作。每个方法 MUST 对应宜搭官方的一个流程实例 endpoint。
+
+#### Scenario: 发起流程
+
+- **WHEN** `startProcess(conf, processCode, formUuid, formDataJson, body_ext)`
+- **THEN** 对应 `POST /v1.0/yida/processes/instances/start`
+- **AND** 返回 `processInstanceId`
+
+#### Scenario: 终止流程
+
+- **WHEN** `terminateProcess(conf, processInstanceId, body_ext)`
+- **AND** `body_ext` 中支持 `operator` / `noExecuteExpression` 等
+
+#### Scenario: 撤回流程
+
+- **WHEN** `revokeProcess(conf, processInstanceId, body_ext)`
+- **THEN** 发起人撤回已提交但未审批完成的流程
+
+### Requirement: 审批任务动作
+
+`YDClient_Process` SHALL 覆盖同意 / 拒绝 / 转交 / 抄送 / 评论五类审批任务动作。每个方法 MUST 与宜搭官方 task 类 endpoint 1:1 对应。
+
+#### Scenario: 同意任务
+
+- **WHEN** `agreeTask(conf, processInstanceId, taskId, comment, body_ext)`
+- **THEN** 对应 `POST /v1.0/yida/processes/tasks/agree`
+
+#### Scenario: 拒绝任务
+
+- **WHEN** `disagreeTask(conf, processInstanceId, taskId, comment, body_ext)`
+- **AND** `body_ext` 支持 `nextOperatorUserIds`(拒绝后指定下一个审批人,部分流程开启此功能)
+
+#### Scenario: 转交任务
+
+- **WHEN** `redirectTask(conf, processInstanceId, taskId, toUserId, comment, body_ext)`
+
+#### Scenario: 抄送
+
+- **WHEN** `ccTask(conf, processInstanceId, taskId, toUserIds, comment, body_ext)`
+
+#### Scenario: 添加评论
+
+- **WHEN** `commentTask(conf, processInstanceId, taskId, comment, body_ext)`
+
+### Requirement: 流程与任务查询
+
+`YDClient_Process` SHALL 提供流程详情、流程列表、任务列表三类查询能力。
+
+#### Scenario: 查单个流程详情
+
+- **WHEN** `getProcess(conf, processInstanceId, body_ext)`
+- **THEN** 返回流程全量信息含已走过的所有节点
+
+#### Scenario: 分页查流程
+
+- **WHEN** `searchProcesses(conf, appType, formUuid, processCode, searchCriteria, body_ext)`
+- **THEN** 支持按发起人 / 状态 / 时间范围过滤
+
+#### Scenario: 查任务列表
+
+- **WHEN** `searchTasks(conf, userId, statuses, body_ext)`
+- **THEN** 返回某用户当前的待办 / 已办任务
+
+### Requirement: 节点跳转
+
+`YDClient_Process` SHALL 提供流程节点跳转能力,允许运维干预流程路径。
+
+#### Scenario: 跳转到指定节点
+
+- **WHEN** `redirectProcess(conf, processInstanceId, targetActivityId, body_ext)`
+- **THEN** 流程跳转到指定节点,支持前跳与后跳
+