# ZCloud Java 项目公共开发规范与模板 > 本文件可复制到新项目直接使用。服务名、包名、网关前缀、领域边界和业务规则应写在项目自己的文档中,不要写入本公共模板。 ## 1. DDD 分层 新项目统一采用 DDD 多模块结构,实际包名前缀、服务名和网关前缀由项目自行定义。 | 模块 | 职责 | 禁止事项 | | --- | --- | --- | | `web-client` | API、Cmd、Qry、Co | 依赖 App、Domain、Infrastructure | | `web-adapter` | Web/App/Socket Controller、Dubbo Facade 等入站适配 | 直接访问 Mapper、编写业务规则 | | `web-app` | Client 接口实现、执行器、用例编排、事务 | 堆积可复用领域规则、长事务调用远程服务 | | `web-domain` | 领域实体、值对象、领域服务、Gateway 接口 | 依赖 MyBatis、Dubbo、HTTP、外部 DTO | | `web-infrastructure` | Gateway 实现、Dubbo/HTTP 调用、DO、Mapper、Repository | 向 Domain 泄漏 DO 或外部 DTO | | `start` | 启动、配置、XXL-JOB 装配 | 编写业务逻辑 | 标准调用链: ```text Adapter -> Client接口(App实现) -> Executor -> Domain/Gateway -> Infrastructure ``` 公司文档允许执行器或 Adapter 直接调用基础设施能力;采用 DDD 分层的项目优先通过 Domain Gateway。纯协议转换且不进入领域用例的场景,Adapter 才可直接引用外部 Facade。 ## 2. 命名与对象基类 | 类型 | 命名/基类 | 放置位置 | | --- | --- | --- | | 领域实体 | `XxxE` | `web-domain/model` | | 领域网关 | `XxxGateway` | `web-domain/gateway` | | 网关实现 | `XxxGatewayImpl` | `web-infrastructure/gatewayimpl` | | 持久化对象 | `XxxDO extends BaseDO` | `persistence/dataobject` | | Mapper | `XxxMapper` | `persistence/mapper` | | 新增/修改命令 | `XxxAddCmd`、`XxxUpdateCmd extends Command` | `web-client` | | 分页查询 | `XxxPageQry extends BasePageQuery` | `web-client` | | 返回对象 | `XxxCo extends ClientObject` | `web-client` | | 执行器 | `XxxAddExe`、`XxxUpdateExe`、`XxxRemoveExe`、`XxxQueryExe` | `web-app` | - Cmd、Qry、Co 和 Adapter 使用的服务接口统一定义在 `web-client`。 - 不得直接返回 DO、领域实体或第三方 DTO。 - 不得在 `persistence.dataobject` 和 `persistence.domainobject` 创建同名 DO;新增 DO 统一放 `dataobject`。 ## 3. HTTP 接口模板 - 路径使用 `/{gateway-prefix}/{resource}`,其中网关前缀由具体项目确定。 - GET、POST、PUT、DELETE 按资源语义使用。 - Controller 只做参数接收、校验、权限控制和应用服务调用。 - 新增、修改分别使用 AddCmd、UpdateCmd,配合 `@Validated` 和 Bean Validation。 - Swagger 使用 `@Api`、`@ApiOperation`、`@ApiModelProperty`;Swagger 的 `required` 不能代替参数校验注解。 - 当前用户通过 `AuthContext.getCurrentUser()` 获取,不信任前端传入的租户、单位、创建人等身份字段。 ```java @ApiOperation("新增") @PostMapping("/save") @PreAuthorize("@pms.hasPermission('实际菜单或按钮权限编码')") public SingleResponse add(@Validated @RequestBody XxxAddCmd cmd) { return xxxService.add(cmd); } ``` 接口权限编码必须取自实际菜单/按钮配置,禁止复制示例编码。 ### 标准返回类 | 类型 | 场景 | | --- | --- | | `SingleResponse` | 单条数据 | | `PageResponse` | 分页数据 | | `MultiResponse` | 集合数据 | | `Response` | 无返回数据 | 禁止新增另一套通用响应外壳。批量操作存在部分成功时,在标准响应承载的 Co 中返回逐条结果。 ## 4. 数据权限模板 接口权限控制“能否调用”,数据权限控制“能看到哪些数据”,两者不能互相替代。 ```java @Mapper @DataScopes({ @DataScope( method = "selectOne", menuPerms = "实际菜单权限编码", tenantAlias = "tenant_id" ) }) public interface XxxMapper extends BaseMapper { } ``` - `method` 与实际 Mapper 方法名一致。 - `menuPerms` 使用实际权限编码。 - `tenantAlias` 与 SQL 中租户列或表别名一致;多表查询必须写明确表别名。 - 定时任务和 Dubbo 等非登录态入口必须明确租户、单位、环境范围,禁止默认跨租户扫描。 ## 5. App、Domain 与异常模板 - App 服务实现负责把 Client 请求分派到执行器。 - 执行器负责参数转换、用例编排、事务和 Gateway 调用。 - 可复用业务规则放入领域实体或领域服务。 - 写操作使用 `@Transactional(rollbackFor = Exception.class)`。 - 领域模型不得调用基础设施层、Mapper、Repository 实现或 Dubbo Facade。 - 保存失败、业务校验失败及远程调用失败统一转换为项目 `BizException`。 - 转换异常时保留原异常日志/异常链;错误信息不能泄露密钥、人脸、证件或完整敏感报文。 ## 6. Dubbo 服务模板 Dubbo 公共契约统一维护在组织公共接口项目 `zcloud_gbs_common`。新项目使用公共仓库的实际发布版本,不在规范中固定开发机绝对路径。 Maven 坐标: ```xml com.zcloud.gbscommon zcloud_gbscommon ${zcloud.gbscommon.version} ``` 使用已有 Dubbo 能力时,先在 Common 项目中查找对应 Facade、Request、Response,禁止在业务项目内重复定义同名公共契约。 ### 发布服务 1. 在 Common 项目的 `src/main/java/com/zcloud/gbscommon/{业务域}` 定义公共契约: - `facade/XxxFacade` - `request/XxxQry`、`XxxCmd` - `response/XxxCo` 2. 服务项目在 Adapter 实现 Facade,使用 `@DubboService` 发布。 3. Facade 只做校验、协议转换和应用服务调用。 4. 修改 Common 契约属于跨项目变更:字段和方法确认后再修改、发布约定版本,并在消费项目验证依赖更新;未经明确需求不得直接修改 Common 项目。 ```java @DubboService public class XxxFacadeImpl implements XxxFacade { // 调用应用服务 } ``` ### 调用服务 - 启动类增加 `@EnableFacadeRpcClient`。 - 消费方使用公共契约,并通过 `@DubboReference(check = false)` 注入。 - DDD 项目优先在 Infrastructure GatewayImpl 中调用 Facade 并转换外部 DTO。 - `check = false` 只是不在启动时检查提供方,不代表可以忽略运行期失败。 - 写接口自动重试可能造成重复业务,必须结合幂等性设置超时和重试策略。 ```java @DubboReference(check = false) private XxxFacade xxxFacade; ``` ## 7. 用户、岗位、部门、企业翻译模板 按 ID 补充系统内公共信息时使用 `TranslateField`: - 用户:`getZcloudUserInfoById(s)` - 岗位:`getZcloudPostInfoById(s)` - 部门:`getZcloudDpetInfoById(s)` - 企业:`getZcloudCorpInfoById(s)` - 对象/集合:`translateZcloudCommonInformation(data, XxxCo.class)` 列表必须使用 `ByIds` 或集合翻译,禁止循环逐条兑换造成 N+1 远程调用。 ```java @ZCloudTranslates({ @ZCloudTranslate( primaryKeyType = TranslateEunm.USER, primaryKey = "userId", mappedFields = {"userName:username"} ), @ZCloudTranslate( primaryKeyType = TranslateEunm.POST, primaryKey = "postId", mappedFields = {"postName:name"} ), @ZCloudTranslate( primaryKeyType = TranslateEunm.DEPT, primaryKey = "deptId", mappedFields = {"deptName:name"} ), @ZCloudTranslate( primaryKeyType = TranslateEunm.CORP, primaryKey = "corpId", mappedFields = {"corpName:name"} ) }) public class XxxCo extends ClientObject { } ``` - `primaryKeyType`:翻译类型。 - `primaryKey`:当前类中的 ID 属性。 - `mappedFields`:`当前类目标属性:common Co来源属性`。 - 翻译结果用于展示补充;核心业务判断应显式查询并校验。 ## 8. 分页和动态查询模板 - 分页 Qry 继承 `BasePageQuery`,返回 `PageResponse`。 - `toHashMap` 查询键使用“操作符前缀 + 驼峰字段”,基础设施层转换为下划线列名。 | 前缀 | SQL 条件 | 示例 | | --- | --- | --- | | `Like` | `LIKE` | `LikePersonName` | | `eq` | `=` | `eqTaskStatus` | | `gt` | `>` | `gtCreateTime` | | `lt` | `<` | `ltCreateTime` | | `le` | `<=` | `leCreateTime` | | `ge` | `>=` | `geCreateTime` | | `ne` | `<>` | `neTaskStatus` | 前缀大小写必须与公共 `toHashMap` 解析规则一致;新增条件前先核对公共组件,禁止自行拼接 SQL 运算符。 ## 9. 数据库模板 所有新建业务表必须包含以下公共字段: ```sql `delete_enum` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '删除标识true false', `remarks` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', `create_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人姓名', `update_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '更新人姓名', `tenant_id` bigint DEFAULT NULL COMMENT '租户id', `org_id` bigint DEFAULT NULL COMMENT '单位id', `version` int DEFAULT NULL COMMENT '版本', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_time` datetime DEFAULT NULL COMMENT '修改时间', `create_id` bigint DEFAULT NULL COMMENT '创建人id', `update_id` bigint DEFAULT NULL COMMENT '修改人id', `env` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '环境标识' ``` - 表使用 InnoDB、`utf8mb4`、`utf8mb4_0900_ai_ci`。 - `version` 是本地乐观锁版本,业务版本另设 `data_version`。 - `delete_enum` 是本地逻辑删除,不等于业务 `DELETE/DISABLE/REVOKE/EXPIRE`。 - 多租户业务索引考虑 `tenant_id`,共享环境同时考虑 `env`。 - 任务号、幂等键、外部回执号等必须有唯一约束。 - 状态扫描组合索引应覆盖状态、下次执行时间、优先级等实际条件。 - 不配置跨服务语义的数据库物理外键。 - DDL、DO、Mapper、XML 保持一致;变更表结构同步维护 DDL 和正式迁移脚本。 - 大报文和敏感信息应加密或仅存脱敏快照,禁止写普通日志。 ## 10. XXL-JOB 模板 - 生产定时任务统一使用 XXL-JOB,不新增仅依赖本地 `@Scheduled` 的任务。 - Handler 使用唯一且稳定的 `@XxlJob` 名称。 - Job 只读取调度参数、触发应用用例、返回结果;业务逻辑放在 App/Domain。 - 通过任务号、唯一键或锁保证业务幂等。 - 失败必须抛出或返回失败,让调度中心感知;禁止吞异常后返回成功。 ```java @Component @RequiredArgsConstructor public class XxxJob { private final XxxApplicationService xxxApplicationService; @XxlJob("projectXxxJob") public ReturnT execute(String param) { xxxApplicationService.execute(param); return ReturnT.SUCCESS; } } ``` ## 11. 第三方 HTTP 接口鉴权与租户上下文 - EdgeGuard 第三方接口统一使用平台签发的 token 鉴权。调用方通过请求头 `token` 传递 token;框架同时兼容 `Magic-Token`,但项目文档和联调统一使用 `token`,禁止通过 URL 查询参数传递 token。 - `jjb-saas-framework-auth` 的 `AuthenticationFilter` 负责读取并解析 token,构造 `SSOUser`,写入线程级 `AuthContext`,请求结束后清理上下文。业务代码通过 `AuthContext.getTenantId()`、`getOrgId()`、`getAppKey()` 等方法读取可信调用身份。 - 第三方开放接口在进入查询或写入用例时必须校验 `AuthContext.getTenantId()` 非空;缺少租户上下文立即拒绝,禁止退化为跨租户查询或写入租户为空的数据。 - 框架虽然兼容读取 `tenant_id`、`tenantId`、`webTenantId` 请求参数或请求头,但这些值不能替代 token 鉴权,也不能直接作为第三方接口的可信租户身份。租户必须以认证完成后的 `AuthContext` 为准。 - 查询指定企业时仍须受当前租户约束;不传企业 ID 时只查询当前 `tenantId` 下的数据。接收报警时,区域、摄像头、企业及最终写入记录必须属于当前租户。 - Controller 是否标记为 `/open/**` 不代表允许匿名调用。对外接口需同时在网关配置应用授权、签名、防重放、限流和允许访问的企业范围;业务服务继续执行租户兜底校验。 - MyBatis/BaseDO 的租户插件和自动填充只能作为持久化保护,不能替代用例入口的租户校验。异步任务、Dubbo、XXL-JOB 等无 HTTP token 场景必须显式建立可信租户上下文或把租户作为经过校验的任务参数。 ## 12. 开发完成检查表 - [ ] Client、Adapter、App、Domain、Infrastructure 职责和依赖方向正确。 - [ ] Cmd/Co/PageQry 分别继承 `Command`、`ClientObject`、`BasePageQuery`。 - [ ] Controller 无 Mapper 调用和业务规则,返回标准 Response 类型。 - [ ] 接口配置真实 `@PreAuthorize` 权限编码,查询配置正确 `@DataScopes`。 - [ ] Dubbo 契约来自 `zcloud_gbs_common`,提供方使用 `@DubboService`,消费方启用 RPC Client。 - [ ] 外部调用完成 DTO 防腐转换、超时、异常和幂等处理。 - [ ] 公共信息使用批量翻译,`mappedFields` 方向正确。 - [ ] 写操作事务和 `BizException` 处理正确,不存在远程长事务。 - [ ] DDL、DO、Mapper、XML、公共字段和索引一致。 - [ ] XXL-JOB 名称唯一,任务幂等,失败可被调度中心感知。 - [ ] 日志和响应未泄露人脸、证件、文件、密钥或完整敏感报文。 - [ ] 完成与改动风险匹配的 Maven 编译、测试和 `git diff` 检查。