|
|
@@ -0,0 +1,196 @@
|
|
|
+## ADDED Requirements
|
|
|
+
|
|
|
+### Requirement: R1 原子接口分层(server / service 两层)
|
|
|
+
|
|
|
+mjava 基座对每个产品方接入按两层组织:
|
|
|
+
|
|
|
+- **`com.malk.server.{product}/`**:产品方原始数据契约。允许放 POJO(如 `*R` 响应、`*Param` 入参、`*Dto` 字段映射)、`{Prod}Conf` 配置类、枚举、常量、签名/加密工具类。禁业务逻辑。
|
|
|
+- **`com.malk.service.{product}/{Prod}Client*.java`**:1:1 对应产品官方接口文档,**一接口一方法**,不做组合,不做错误码翻译,仅做参数填充 + HTTP 调用 + 反序列化。
|
|
|
+- **`com.malk.service.{product}/{Prod}Service.java`**:可选。仅当存在跨客户共用的二次封装(自动续 token、分页拼装、错误码翻译、跨接口编排)时建立。准入条件见 R7。
|
|
|
+
|
|
|
+实现类一律落在 `com.malk.service.{product}.impl/` 子包。
|
|
|
+
|
|
|
+#### Scenario: 新增一个产品方接入
|
|
|
+
|
|
|
+- **WHEN** 需要在 mjava 基座新增对某产品方(如 "腾讯会议")的接入
|
|
|
+- **THEN** 必须建 `server/txmeeting/` 与 `service/txmeeting/` 两个目录
|
|
|
+- **AND** `server/txmeeting/` 下至少有 `TXMConf` 或 `TXMR` 至少一个数据契约类
|
|
|
+- **AND** `service/txmeeting/TXMClient.java` 接口 + `impl/TXMClientImpl.java` 实现各一个文件
|
|
|
+- **AND** 如无跨客户复用场景,**禁建** `TXMService.java` 空壳
|
|
|
+
|
|
|
+#### Scenario: Client 方法定义
|
|
|
+
|
|
|
+- **WHEN** 实现 `{Prod}Client*.java` 接口的方法
|
|
|
+- **THEN** 方法签名应与产品方官方接口文档 1:1 对应(每个 OpenAPI 一个 Java 方法)
|
|
|
+- **AND** 方法体只做:构造 headers / params / body → 调 `UtilHttp` → 反序列化 → 返回;**不调其他 Client 方法、不写 if-else 业务分支**
|
|
|
+
|
|
|
+#### Scenario: 集成平台 INTP 当前现状与演进
|
|
|
+
|
|
|
+- **WHEN** 当前 `service/integration/` 仅 `INTPClient_User`(单一域)
|
|
|
+- **THEN** **不**预建空壳 `INTPClient` 主入口或 `INTPService`(服从 R7 防空壳准入)
|
|
|
+- **AND** 未来扩多域时(例:补 `INTPClient_Org` / `INTPClient_Permission` 等)按 R2 触发:
|
|
|
+ - 若总方法数 > 15 或跨 2+ 域,必须建 `INTPClient` 主入口聚合
|
|
|
+ - 若 ≥2 客户子项目复用相同的二次编排(如统一鉴权续约),按 R7 准入条件补 `INTPService`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Requirement: R2 板块拆 client
|
|
|
+
|
|
|
+当单一 `{Prod}Client.java` 接口方法数 > 15,或跨 2+ 产品板块(例:钉钉的「通讯录」+「考勤」),必须按板块拆分为 `{Prod}Client_{Module}.java`。
|
|
|
+
|
|
|
+参照范式:
|
|
|
+- 钉钉 12 子 client:`DDClient_Attendance` / `DDClient_Contacts` / `DDClient_Group` 等
|
|
|
+- 宜搭 2 子 client:`YDClient_Form` / `YDClient_Process`
|
|
|
+- 北森 2 子 client:`BSClient_Attendance` / `BSClient_Employee`
|
|
|
+
|
|
|
+主 `{Prod}Client.java` 保留作为总入口或聚合(含 token 获取、通用工具方法),不消失。
|
|
|
+
|
|
|
+#### Scenario: Client 方法数超阈值
|
|
|
+
|
|
|
+- **WHEN** 给某 Client 增加新方法导致总数 > 15
|
|
|
+- **THEN** 必须在同次 PR 中拆出至少 1 个 `{Prod}Client_{Module}.java` 子文件,按业务板块归类
|
|
|
+- **OR** 如无法立即拆分,必须在 PR 描述里明确「**技术债**:超阈值 N 方法,跟踪 issue/change #xxx」
|
|
|
+
|
|
|
+#### Scenario: 跨板块新方法
|
|
|
+
|
|
|
+- **WHEN** 新方法所属产品板块(如「考勤」)与现有 Client 主体(如「通讯录」)不同
|
|
|
+- **THEN** 必须建新的 `{Prod}Client_{Module}.java` 而不是塞进现有 Client
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Requirement: R3 调用优先级(子项目复用 mjava)
|
|
|
+
|
|
|
+所有 mjava-ai 仓内子项目(mcli / pro / com / 未来新建客户模块)和**外部独立客户仓**(akds / 光明独立仓 / 其他客户仓)调产品方接口时遵循优先级:
|
|
|
+
|
|
|
+1. **优先调** `mjava.service.{product}.{Prod}Client*` / `{Prod}Service`
|
|
|
+2. mjava 未提供但**确属产品方原子接口** → 按 R1/R2 在 mjava 新增 Client → 再被子项目调
|
|
|
+3. mjava 未提供且**仅为该客户独有业务编排**(非产品方原子接口)→ 留在客户子项目本地
|
|
|
+
|
|
|
+子项目**禁止**自建 HTTP 直调(绕过 `UtilHttp`)、禁止自建产品方鉴权/Token 逻辑(绕过 `UtilToken`)、禁止引入产品方三方 SDK。
|
|
|
+
|
|
|
+#### Scenario: 子项目发现 mjava 缺接口
|
|
|
+
|
|
|
+- **WHEN** 客户子项目需要某产品方接口,mjava 当前未提供
|
|
|
+- **THEN** 先判断该接口是否为「产品方原子能力」
|
|
|
+- **AND** 是 → 起 mjava 侧 change 新增 Client → mjava 发版 → 子项目升级 mjava 版本调用
|
|
|
+- **AND** 否(客户独有编排)→ 在子项目本地写,不进 mjava
|
|
|
+
|
|
|
+#### Scenario: 子项目存量 HTTP 直调
|
|
|
+
|
|
|
+- **WHEN** 巡检发现客户子项目内有 `UtilHttp.doXxx` 直接调产品方域名
|
|
|
+- **THEN** 标记为待迁移技术债
|
|
|
+- **AND** 下次触碰该代码段时按 R3 上提到 mjava Client
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Requirement: R4 变更确认(核心)
|
|
|
+
|
|
|
+对 `com.malk.service.{product}.{Prod}Client*.java` 或 `{Prod}Service.java` 的**接口签名**做以下任一变更前,必须执行变更确认流程:
|
|
|
+
|
|
|
+- 删除方法
|
|
|
+- 改名方法
|
|
|
+- 改方法参数列表(增/减/换类型/换顺序)
|
|
|
+- 改方法返回类型
|
|
|
+- 删除/改名接口本身
|
|
|
+- 删除/改名 `server/` 层 POJO 类、字段、方法
|
|
|
+
|
|
|
+变更确认流程:
|
|
|
+
|
|
|
+1. **grep 引用扫描**:
|
|
|
+ - **基础建设阶段(当前默认)**:扫 mjava-ai 本仓全量
|
|
|
+ - **有客户子项目依赖时**:用户在 ACK 流程中显式列出需扩扫的下游仓库路径(如未来 akds / 光明独立仓 / 新客户仓)
|
|
|
+2. **生成引用清单**:file:line + 调用上下文
|
|
|
+3. **报告给用户**:影响面 + 拟改动方案 + 兼容路径(如有)
|
|
|
+4. **等待用户 ACK**:用户回复"OK / 同意 / 改吧"等明确肯定后方可执行
|
|
|
+5. **变更后回写**:commit message 注明 `BREAKING: {Class}.{method} 签名变更`,受影响调用方列表写入 commit body
|
|
|
+
|
|
|
+**仅扩展**变更(新方法 / 新重载 / 新子 client 文件 / 新 server 层 POJO)**不需要**确认,但 commit message 注明 `feat(service): 新增 {Class}.{method}`。
|
|
|
+
|
|
|
+#### Scenario: 改 Client 方法签名
|
|
|
+
|
|
|
+- **WHEN** 计划修改 `DDClient_Contacts.getUserDetail(String userid)` 改为 `getUserDetail(String userid, Boolean includeExt)`
|
|
|
+- **THEN** 必须先执行 grep `\.getUserDetail\(` 跨仓扫描
|
|
|
+- **AND** 生成引用清单提交给用户
|
|
|
+- **AND** 在用户 ACK 前**不修改源代码**
|
|
|
+- **AND** ACK 后才动手,commit message 标 `BREAKING`
|
|
|
+
|
|
|
+#### Scenario: 仅扩展(加新方法)
|
|
|
+
|
|
|
+- **WHEN** 给 `DDClient_Contacts` 新增 `listDepartmentTree()` 方法
|
|
|
+- **THEN** 直接实现,无需扫引用
|
|
|
+- **AND** commit message 写 `feat(service/dingtalk): DDClient_Contacts 新增 listDepartmentTree`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Requirement: R5 命名一致(Impl 后缀)
|
|
|
+
|
|
|
+所有实现类命名风格统一为 `XxxImpl` **后缀**:
|
|
|
+
|
|
|
+- 正:`DDClientImpl` / `DDClient_AttendanceImpl` / `DDServiceImpl` / `YDClient_FormImpl` / `BSServiceImpl`
|
|
|
+- 负:`DDImplClient` / `DDImplClient_Attendance` / `DDImplService`(中缀风格,禁止新增)
|
|
|
+
|
|
|
+R5 对**新增代码**强制生效。钉钉现有 14 个中缀文件(`DDImplClient` + `DDImplClient_X×12` + `DDImplService`)作为存量技术债,拆到独立 change `rename-dingtalk-impl-suffix` 推进,本 change 不动。
|
|
|
+
|
|
|
+#### Scenario: 新增实现类
|
|
|
+
|
|
|
+- **WHEN** 给某 Client 接口新增实现
|
|
|
+- **THEN** 文件名必须用 `XxxImpl` 后缀,禁用 `XxxImplXxx` 中缀
|
|
|
+
|
|
|
+#### Scenario: 钉钉新增子 client 实现
|
|
|
+
|
|
|
+- **WHEN** 给 `service/dingtalk/impl/` 新增子 client 实现
|
|
|
+- **THEN** 即使周围 12 个文件是 `DDImplClient_X` 中缀风格,新文件必须用 `DDClient_XImpl` 后缀
|
|
|
+- **AND** 不为「对齐周围风格」回退 R5
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Requirement: R6 server/ 层定位(数据契约纯净性)
|
|
|
+
|
|
|
+`com.malk.server.{product}/` 包内的类必须满足:
|
|
|
+
|
|
|
+- **允许**:POJO(含 `*R` `*Param` `*Dto` `*Conf`)、枚举(`enum`)、常量类(`public static final`)、产品方签名/加密工具类(如 `DingCallbackCrypto`、`DigestUtil`)
|
|
|
+- **禁止**:
|
|
|
+ - `@Service` / `@Component` / `@Repository` / `@RestController` 等 Bean 注解
|
|
|
+ - `@Autowired` / `@Resource` / 构造器注入其他 Bean
|
|
|
+ - 直接调 `UtilHttp` / `UtilToken` 发起网络请求
|
|
|
+ - 含业务编排逻辑(if-else 业务分支、跨产品组合)
|
|
|
+
|
|
|
+`{Prod}Conf` 是 `@ConfigurationProperties` 的允许例外(标了 `@Component` 注解但仅为配置绑定,不参与业务)。
|
|
|
+
|
|
|
+#### Scenario: 误把业务类放进 server/
|
|
|
+
|
|
|
+- **WHEN** 代码评审发现 `server/dingtalk/` 下出现 `@Service` 注解的类
|
|
|
+- **THEN** 必须移到 `service/dingtalk/impl/` 或客户子项目对应位置
|
|
|
+
|
|
|
+#### Scenario: server/ 类的依赖
|
|
|
+
|
|
|
+- **WHEN** 写 `server/{product}/` 下的 POJO 类
|
|
|
+- **THEN** import 范围限定在:JDK / Lombok / fastjson / `com.malk.server.common.*` / 同包内类
|
|
|
+- **AND** 不得 import `com.malk.service.*` / `com.malk.utils.*`(除常量工具如 `UtilMap` 静态方法)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Requirement: R7 Service 层准入(防空壳)
|
|
|
+
|
|
|
+`{Prod}Service.java` 仅当满足以下任一条件时才建:
|
|
|
+
|
|
|
+- 当前有 ≥2 个客户子项目(包括 mjava-ai 内 mcli/pro/com 与外部独立客户仓)使用相同二次编排逻辑
|
|
|
+- 该编排涉及非业务的通用横切(自动续 token、统一分页拼装、产品方错误码 → `McException` 翻译)
|
|
|
+- 该编排为新接入产品的「试水期占位」,提案中明确承诺 3 个月内补复用方,否则归零
|
|
|
+
|
|
|
+不满足上述条件时,二次编排留在客户子项目内 `com.malk.{customer}.service.{product}.{Prod}LocalService.java`,命名带 `Local` 前缀以示区分。
|
|
|
+
|
|
|
+未满足条件强行预建 Service 文件(空 interface / 仅一个 default 方法 / 仅一个未被任何客户调用的方法)视为违反 R7,code review 必须打回。
|
|
|
+
|
|
|
+#### Scenario: 新接入产品评估 Service 层
|
|
|
+
|
|
|
+- **WHEN** 在 mjava 接入新产品(如「腾讯会议」)
|
|
|
+- **THEN** 默认**只建 Client**,不建 Service
|
|
|
+- **AND** 第一个客户的二次编排放在客户子项目本地
|
|
|
+- **AND** 第二个客户来要复用时再上提
|
|
|
+
|
|
|
+#### Scenario: 误建空壳 Service
|
|
|
+
|
|
|
+- **WHEN** PR 中出现 `XxxService.java` 接口仅有 1~2 个未被任何子项目调用的方法
|
|
|
+- **THEN** code review 必须打回
|
|
|
+- **AND** 编排迁回客户子项目
|