--- name: crud-by-controller-ylsw-bw description: 基于仪陇表务(ylsw-bw / water-meter)ledger 模块风格生成 CRUD、台账查询与导出接口。参考 com.tofly.wm.ledger.meter 等既有实现,统一 ResultResponse、MPJ、PageHelper 与权限注解。用户提到表务、水表台账、ledger、出入库、生成接口时使用。 --- # CRUD 生成 Skill(仪陇表务 ylsw-bw) ## 适用场景 当用户要求在 `business/water-meter` 或表务相关模块中新增/补全接口时,优先对齐 **`com.tofly.wm.ledger`** 下既有代码(首选参考 `ledger/meter`,台账类参考 `ledger/purchase`、`ledger/arrive`、`ledger/verify`)。 ## 公共 Skill 引用 - POM 工作流:`language/java/pom-factory-skill.md`;**Maven 治理**:[`rules/java-pom-governance.mdc`](../../../../../rules/java/java-pom-governance.mdc)。 - **Java CRUD 通用规范**:[`rules/java-crud-common.mdc`](../../../../../rules/java/java-crud-common.mdc)。 - **tf-ylsw 条线 MUST/SHOULD**:[`rules/java-tf-ylsw-backend.mdc`](../../../../../rules/java/java-tf-ylsw-backend.mdc)(与 ledger 实际写法冲突时,以 ledger 存量风格为准,并在输出中注明差异)。 - 当本文件与 **java-crud-common** 冲突时,本文件(ylsw-bw / ledger)优先。 ## 代码落点与包结构(对齐 `com.tofly.wm`) 模块根包 **`com.tofly.wm`**,按业务分层;**新建代码必须落在与职责匹配的包下**,禁止全部堆在 `ledger` 或单一层级。 ### 包树与职责(参考 ledger / manage 存量) ``` com.tofly.wm ├── cons/ # 模块常量(字典目录码、业务编码前缀等) │ └── MeterConstant.java ├── ledger/ # 台账、出入库、只读汇总(少写或不写主数据) │ ├── meter/ # 水表出入库 CRUD │ │ ├── enums/ # 域内枚举(BoundTypeEnum、CompanyTypeEnum…) │ │ └── app/ # 子场景(App 入库、领用等),*Controller/*Service/*Vo │ ├── purchase/ # 采购台账(Vo + Query + Service,无独立 Entity 表) │ ├── arrive/ # 到货台账 │ └── verify/ # 落地检台账 ├── manage/ # 管理端业务写操作、主数据维护 │ ├── puachase/ # 采购(存量包名 puachase,新建子包沿用勿改拼写) │ │ ├── apply/ # 采购申请 │ │ │ └── list/ # 申请明细子表 │ │ ├── plan/ # 采购计划 │ │ │ ├── arrive/ # 到货单 │ │ │ └── arriveList/ # 到货明细 │ │ └── claim/ # 领用 │ ├── brand/ # 厂家 │ ├── verify/ # 检定管理(含 giveback 等子包) │ └── warehouse/ ├── workorder/ # 工单 └── api/ # 对外/第三方接口(verify 等) ``` ### 单包内文件组织(以 `ledger/meter` 为准) | 类型 | 命名 | 位置 | |------|------|------| | 接口 | `{Domain}Controller` | 域包根目录 | | 服务接口/实现 | `{Domain}Service` / `{Domain}ServiceImpl` | 域包根目录;台账可无接口,仅 `{Domain}LedgerService` | | 实体 | `{Domain}` | 域包根目录 | | 查询 | `{Domain}Query` / `{Domain}LedgerQuery` | 域包根目录 | | 展示 | `{Domain}Vo` / `{Domain}LedgerVo` | 域包根目录 | | Mapper | `{Domain}Mapper` | 域包根目录 | | 枚举 | `*Enum` | `{domain}/enums/` | | App/专项 | `App*`、`Claim*` | `{domain}/app/` 等子包 | - **同包平铺**为主,不按 `controller/service/entity` 再分子包(与 ledger 一致)。 - **子表/明细**:独立子包 `list`、`arriveList` 等,类名与主表关联清晰。 - **常量**:字典目录、流水号类型等放 **`com.tofly.wm.cons.MeterConstant`**,禁止魔法字符串。 - 表名前缀 **`TF_WM_*`**,业务表多为 **`_W`**;`mapper.xml` → `src/main/resources/mapper/`。 ### 选型:ledger 还是 manage? | 场景 | 推荐包 | |------|--------| | 分页/列表/导出台账、跨表只读汇总 | `ledger.{domain}` | | 主表 CRUD、流程、审批、写入业务表 | `manage.{业务域}` | | 移动端/专项入口 | `ledger.{domain}.app` 或 `manage.*.app` | ## ylsw-bw 专项约束 ### 接口与返回 - 返回体统一 **`ResultResponse`**(带泛型,禁止裸类型)。 - Controller:`@RestController` + `@RequestMapping("/xxx")` + `@Api(tags = "...")`。 - 依赖注入:`@RequiredArgsConstructor` + `@FieldDefaults(level = PRIVATE, makeFinal = true)`(Controller 常见);Service 可用 `@AllArgsConstructor` 或 `@Service` 无接口实现类。 ### 路由习惯(对齐 meter) | 能力 | 方法 | 路径 | 说明 | |------|------|------|------| | 分页 | GET | `/page` | 与 `/list` 共用 `{Domain}Query`(含 `pageNum`/`pageSize`) | | 列表 | GET | `/list` | 与 `/page` **同一 Query、同一 Service 条件**;不分页,返回 `List` | | 详情 | GET | `/{id}` | | | 新增 | POST | `/` | `@RequestBody` 实体 | | 修改 | POST | `/update` | 本模块用 POST 而非 PUT | | 删除 | DELETE | `/deleteByIds` | `ids` 请求参数,逗号分隔主键;**禁止**单条 `DELETE /{id}` | | 导出 | GET | `/export` | 返回 `void`,`EasyPoiUtil.download`(台账类) | - **禁止**提供 `DELETE /{id}`、`removeById` 对外接口;删除统一走 **`DELETE /deleteByIds`**(单条删除传 `ids=1` 即可)。 - 生成 Controller 时**不得**保留 `@DeleteMapping("/{id}")` 模板代码。 ### 接口文档生成(强制) 编制或生成各 PRD-04 [功能需求说明](../../../../business/water-meter/doc/demand/功能需求说明/) **§接口设计(研发)**、[公共接口说明](../../../../business/water-meter/doc/demand/功能需求说明/仪陇表务-公共接口说明-V1.0.md) 时: 1. **标准 CRUD 类资源**须按上表 **全量列出** 模板接口(`page`、`list`、`{id}`、`POST /`、`update`、`deleteByIds`),**不得**因 PRD 未写或业务暂未实现而省略。 2. **台账只读类**须列出模板:`GET /page`、`GET /list`(若适用)、`GET /{id}`(若适用)、`GET /export`;不写 `POST`/`deleteByIds` 时在文档中标注「台账只读,无写接口」。 3. 业务专属接口(工单发起、派单、App 回传等)在模板接口 **之后** 追加列出。 4. 文档中 **禁止** 出现 `DELETE /{resource}/{id}`;删除统一写 `DELETE /{resource}/deleteByIds`。 ### 工具类与空值判断 | 场景 | 用法 | 禁止/避免 | |------|------|-----------| | 字符串为空 | `StrUtil.isNotBlank(s)` / `StrUtil.isBlank(s)` | 手写 `s != null && !s.isEmpty()` | | 集合为空 | `CollUtil.isEmpty(list)` / `CollUtil.isNotEmpty(list)` | 仅 `list == null` | | 对象拷贝 | `BeanUtil.copyProperties(...)` | 大段手动 set(无业务差异时) | | **对象**是否为 null | **`Objects.nonNull(x)`** / **`Objects.isNull(x)`** | 对象用 `== null` 写在条件首参外且无一致性时 | | 枚举入库条件 | `Optional.ofNullable(enum).map(Enumerable::getKey).orElse(null)` | 直接 `.eq(field, enum)` 未取 key | | 范围时间 | `.between(Objects.nonNull(q.getStartDate()), ...)` | 未判空即 between | - Hutool 包名:**`cn.hutool.core.*`**(`StrUtil`、`CollUtil`、`BeanUtil` 等),与项目存量一致。 - **字符串**用 Hutool;**引用类型对象**(实体字段、枚举、Long、OrganizationEntry 等)用 **`Objects`**。 ### 分页与查询 - 查询类继承 **`PageQuery`**,Swagger `@ApiModel` / `@ApiModelProperty`。 - **Query 字段类型**:筛选用 `Long`/`String`/枚举/`LocalDate` 等基础类型;**`OrganizationEntry`、`UserNameEntry`、`DictEntry` 仅出现在 Entity/Vo**,不出现在 Query(字典筛选可用字典 code 的 `String`)。 - 单表条件:**`Wrappers.lambdaQuery()`**,条件首参遵循上表;枚举用 **`Enumerable.getKey()`**。 - 列表:**`list(query)`** 与分页共用 `buildWrapper`;`page(query)` 内部 **`PageHelper.startPage(query, () -> list(query))`**。 - 批量删除:Service **`deleteByIds(String ids)`** 解析逗号分隔主键后 `removeByIds`,需 **`@Transactional`**。 - 多表台账只读:**`MPJWrappers.lambdaJoin()`** + `selectJoinList`(见 purchase/verify 台账),不强行拆 XML。 - **关键条件行**加简短行尾注释(见注释规范),如 `//出厂编码`、`//需求部门`。 ### Service / Mapper 基类 - 实体 CRUD:`MPJBaseService` / `MPJBaseServiceImpl`。 - Mapper:`MPJBaseMapper`;简单查询不写 XML。 - 复杂统计、窗口函数、批量状态更新:在 `*Mapper.xml` 编写,**禁止** Mapper 注解 SQL。 ### 实体与 BaseEntity(强制) > **与 `database-design.mdc` 的关系**:公司通用规范写 `create_user`/`is_delete` 等;**本仓库(ylsw-bw / Oracle 11)以 `com.tofly.entity.pojo.BaseEntity` 映射为准**,逻辑删除列名为 **`DELETED`**(`NUMBER(1)`),创建/更新人为 **`NUMBER(19)`**。建表、实体、Skill 自检均以本节为准。 #### 建表:每张业务表必须包含的公共列 **新建** `TF_WM_*` 主表、明细表时,在 **`CREATE TABLE` 语句内**与主键 `ID`、业务字段**一并声明**下列列(Oracle 11),**不要**单独用 `ALTER TABLE ... ADD` 事后补列。 ```sql CREATE TABLE TF_WM_XXX_W ( ID NUMBER(19) NOT NULL, -- BaseEntity 公共列(主表/明细表均须包含) CREATE_USER NUMBER(19), CREATE_TIME TIMESTAMP, UPDATE_USER NUMBER(19), UPDATE_TIME TIMESTAMP, DELETED NUMBER(1) DEFAULT 0, CREATE_COMPANY_ID NUMBER(19), CREATE_COMPANY_NAME VARCHAR2(200), CREATE_DEPT_ID NUMBER(19), CREATE_DEPT_NAME VARCHAR2(200), CREATE_POST_ID NUMBER(19), CREATE_POST_NAME VARCHAR2(200), CREATE_USER_NAME VARCHAR2(100), CUR_COMPANY_ID NUMBER(19), CUR_DEPT_ID NUMBER(19), -- 业务字段 … CONSTRAINT PK_TF_WM_XXX_W PRIMARY KEY (ID) ); COMMENT ON TABLE TF_WM_XXX_W IS '表说明'; COMMENT ON COLUMN TF_WM_XXX_W.ID IS '主键'; COMMENT ON COLUMN TF_WM_XXX_W.CREATE_USER IS '创建人'; COMMENT ON COLUMN TF_WM_XXX_W.CREATE_TIME IS '创建时间'; COMMENT ON COLUMN TF_WM_XXX_W.UPDATE_USER IS '更新人'; COMMENT ON COLUMN TF_WM_XXX_W.UPDATE_TIME IS '更新时间'; COMMENT ON COLUMN TF_WM_XXX_W.DELETED IS '删除状态:0未删除 1已删除'; COMMENT ON COLUMN TF_WM_XXX_W.CREATE_COMPANY_ID IS '创建公司ID'; COMMENT ON COLUMN TF_WM_XXX_W.CREATE_COMPANY_NAME IS '创建公司名称'; COMMENT ON COLUMN TF_WM_XXX_W.CREATE_DEPT_ID IS '创建部门ID'; COMMENT ON COLUMN TF_WM_XXX_W.CREATE_DEPT_NAME IS '创建部门名称'; COMMENT ON COLUMN TF_WM_XXX_W.CREATE_POST_ID IS '创建岗位ID'; COMMENT ON COLUMN TF_WM_XXX_W.CREATE_POST_NAME IS '创建岗位名称'; COMMENT ON COLUMN TF_WM_XXX_W.CREATE_USER_NAME IS '创建用户名称'; COMMENT ON COLUMN TF_WM_XXX_W.CUR_COMPANY_ID IS '当前公司ID'; COMMENT ON COLUMN TF_WM_XXX_W.CUR_DEPT_ID IS '当前部门ID'; -- 业务列 COMMENT 逐列补充 ``` | 列名 | 类型 | 说明 | |------|------|------| | CREATE_USER | NUMBER(19) | 创建人 ID | | CREATE_TIME | TIMESTAMP | 创建时间 | | UPDATE_USER | NUMBER(19) | 更新人 ID | | UPDATE_TIME | TIMESTAMP | 更新时间 | | DELETED | NUMBER(1) | 逻辑删除:0 未删,1 已删(Java 侧见下节 `deleted`) | | CREATE_COMPANY_ID / CREATE_COMPANY_NAME | | 创建时公司 | | CREATE_DEPT_ID / CREATE_DEPT_NAME | | 创建时部门 | | CREATE_POST_ID / CREATE_POST_NAME | | 创建时岗位 | | CREATE_USER_NAME | VARCHAR2(100) | 创建人姓名 | | CUR_COMPANY_ID / CUR_DEPT_ID | NUMBER(19) | 当前公司/部门(数据权限) | 填充由平台 **`MetaObjectHandler`** / 会话上下文自动写入,业务代码**禁止**在子类重复声明上述字段。 #### 逻辑外键列注释(强制) 凡 **关联表 ID** 列(通常为 `*_id`,以及 BaseEntity 的 `create_user`/`update_user`),`COMMENT ON COLUMN` 须写明 **业务含义 + 关联目标**: ```sql COMMENT ON COLUMN wssmet_meterlife_w.operator_id IS '操作人ID,关联tf_user表的id字段'; COMMENT ON COLUMN wssmet_meter_w.warehouse_id IS '仓库ID,关联wssmet_warehouse_w表的id字段'; COMMENT ON COLUMN wssmet_purapplylist_w.apply_id IS '采购申请ID,关联wssmet_purapply_w表的id字段'; ``` | 关联类型 | 目标表示例 | 说明 | |----------|------------|------| | 平台用户 | `tf_user.id` | `UserNameEntry`、操作人/执行人/签收人等 | | 平台组织 | `tf_org.id` | `OrganizationEntry`、部门/公司/管辖组织等 | | 平台岗位 | `tf_post.id` | BaseEntity `create_post_id` | | 本模块业务表 | `wssmet_{x2}_w.id` | 水表、仓库、申请主表、任务主表、明细父表等 | | workflow/外部 | 注释单独说明 | 如 `process_instance_id`,不写伪外键 | - **禁止**仅写「水表」「申请」「操作人」等简称而不写关联表。 - 多态关联(`ref_table_name` + `ref_table_id`)须说明与 `ref_table_name` 配合解析。 - 生成 DDL 时逐列自检;设计文档 `business/water-meter/doc/design/数据库设计-V1.0.md` 须与本节一致。 #### 字典 / 枚举列注释(强制) 凡 **字典**(`DictEntry` + `@DictDirectory`)或 **业务枚举**(Java `*Enum implements Enumerable`)落库列(多为 `VARCHAR2` 的 `*_code`、`*_status`),`COMMENT ON COLUMN` 须标明类型: ```sql -- 字典:目录码与 MeterConstant 一致 COMMENT ON COLUMN wssmet_meter_w.caliber_code IS '口径编号,字典类型,目录码METER_DIAMETER'; COMMENT ON COLUMN wssmet_meter_w.meter_type_code IS '水表类型编号,字典类型,目录码METER_TYPE'; -- 枚举:写 Java 枚举类名 COMMENT ON COLUMN wssmet_purapply_w.approve_status IS '审批状态,枚举类型,ApproveStatusEnum'; COMMENT ON COLUMN wssmet_purplan_w.arrive_status_code IS '到货状态编号,枚举类型,ArriveStatusEnum'; ``` | 类型 | COMMENT 必含 | Java 侧 | |------|--------------|---------| | **字典** | `字典类型,目录码{目录码}` | `DictEntry` + `@DictDirectory(MeterConstant.*)` | | **枚举** | `枚举类型,{EnumClassName}` | `{domain}/enums/*Enum implements Enumerable` | - **禁止**字典/枚举列仅写「状态编号」「类型编号」而不标明字典或枚举。 - 业务单号类 `VARCHAR2`(`apply_code`、`meter_code`、`task_code` 等)为**普通字段**,不要求字典/枚举标注。 #### 非普通字段清单 §2.1(设计文档,强制) 数据库设计文档须含 **§2.1 非普通字段清单(按表)**:**仅列出字典列与枚举列**,用表格标注 `字典` / `枚举`、**说明**(字段业务含义)、**引用**(目录码或 Java 枚举类名);**字典项与枚举取值明细**须单独维护于设计目录 [字典与枚举说明-V1.0.md](../../../../business/water-meter/doc/design/字典与枚举说明-V1.0.md)。 **§2.1 必须排除(不得出现在按表清单中)** | 排除项 | 列示例 | 说明 | |--------|--------|------| | **BaseEntity 公共列** | `create_user`、`create_dept_id`、`cur_company_id`、`deleted` 等 | 见设计文档 §2;COMMENT 仍按 §逻辑外键列注释 编写 | | **主键 / 外键关联列** | `id`、`meter_id`、`apply_id`、`apply_dept_id` 等 `*_id` | 关联关系在 DDL `COMMENT` 中说明,**不入 §2.1 表** | | **普通业务列** | `apply_code`、`meter_code`、数量/日期/备注 | 非字典/枚举 | **§2.1 应列入(仅此两类)** | 类型 | 列示例 | 说明列 | 引用列 | |------|--------|--------|--------| | **字典** | `caliber_code`、`meter_type_code` | 口径、水表类型等业务含义 | 目录码,如 `METER_DIAMETER` | | **枚举** | `task_status_code`、`approve_status` | 任务状态、审批状态等业务含义 | Java 枚举类名,如 `TaskStatusEnum` | ```markdown ### wssmet_purapply_w — 采购申请 | 字段 | 类型 | 说明 | 引用 | | process_status_code | 枚举 | 流程状态 | ProcessStatusEnum | | approve_status | 枚举 | 审批状态 | ApproveStatusEnum | ``` (`caliber_code` 等字典列:说明填业务含义,引用填目录码;**禁止**在说明列写 `DictEntry`、`@DictDirectory`、`implements Enumerable` 等实现细节。枚举/字典**取值**见 [字典与枚举说明-V1.0.md](../../../../business/water-meter/doc/design/字典与枚举说明-V1.0.md)。) - 某表无字典/枚举业务列时,**不设该表小节**。 - 清单与 DDL 中字典/枚举列的 `COMMENT ON COLUMN` 一致;维护时手工同步 §2.1 与 DDL。 #### 持久层实体(强制) - **所有**映射数据库表的实体类 **`extends BaseEntity`**(含主表、明细表、子表,如 `PurchaseApplyList`、`GiveBackMeter`)。 - 主键仍在子类声明:`@TableId(value = "ID", type = IdType.ASSIGN_ID) Long id`。 - **禁止**在子类再次声明 `createUser`、`createTime`、`updateUser`、`updateTime` 及 `createCompanyId`~`curDeptId` 等 BaseEntity 已映射字段(避免覆盖、类型不一致,如 `BigDecimal updateUser`)。 - Lombok:`@Data`;子类可用 `@EqualsAndHashCode(callSuper = true)`(按需);`@FieldDefaults(level = PRIVATE)`;业务列 **`@TableField` 大写列名**。 #### 逻辑删除(`deleted` / `Boolean`) | 层 | 约定 | |----|------| | 数据库 | 列名 **`DELETED`**,`NUMBER(1) DEFAULT 0`(0 未删,1 已删) | | Java 实体 | 属性名 **`deleted`**,类型 **`Boolean`**(**禁止** `Integer`/`Number`/`is_delete`/`del_flag`) | | 映射 | `@TableField("DELETED")` | | MyBatis-Plus | 须逻辑删除时加 **`@TableLogic`**(参考 `PurchasePlan` 存量写法) | - 继承 **`BaseEntity`** 且父类已提供 **`Boolean deleted`** 时,**子类禁止重复声明**。 - 父类无 `deleted` 时,在实体中显式声明(默认值 `false`): ```java @TableLogic @TableField("DELETED") @ApiModelProperty("删除状态") Boolean deleted = false; ``` - 查询默认过滤已删除数据:依赖 `@TableLogic` 或 Wrapper 自动带 `DELETED = 0`;手写 SQL/XML 须显式 `DELETED = 0`(或布尔映射等价条件)。 #### 值对象与数据库列映射(强制) | Java 类型 | 数据库类型 | 实体注解 | 说明 | |-----------|------------|----------|------| | **`OrganizationEntry`** | **`NUMBER(19)`** | `@TableField("APPLY_DEPT_ID")` 等 | 仅存组织/部门/公司 **ID**;name 由平台解析,**禁止**再建 `*_name` 快照列 | | **`UserNameEntry`** | **`NUMBER(19)`** | `@TableField("APPLY_USER_ID")` 等 | 仅存用户 **ID**;**禁止**再建 `*_name` 快照列 | | **`DictEntry`** | **`VARCHAR2`** | `@TableField("PRIORITY_CODE")` + `@DictDirectory` | 存字典 **code**(如 `METER_JJCD` 项编码),列名常用 `*_code` | | **`FileEntry` / `FileEntryList`** | **无列** | **`@TableField(exist = false)`** | **DDL 不建字段**;附件由平台文件服务按业务主键关联 | - 组织/用户列命名:业务语义 + `_id`(如 `apply_dept_id`、`verify_user_id`);Java 属性仍用 `OrganizationEntry applyDept`、`UserNameEntry verifyUser`。 - 字典列命名:业务语义 + `_code`(如 `priority_code`、`meter_type_code`);与 `@DictDirectory(MeterConstant.*)` 成对出现。 - BaseEntity 审计列中的 `create_company_name`、`create_user_name` 等 **平台自动填充**,与业务字段上的 Entry 规则无关。 #### 附件(`FileEntry` / `FileEntryList`) | 场景 | Java 类型 | 说明 | |------|-----------|------| | 多个附件 | **`com.tofly.entity.pojo.FileEntryList`** | 接口入参/出参、Entity/Vo 展示 | | 单个附件 | **`com.tofly.entity.pojo.FileEntry`** | 仅一个文件时使用 | ```java @TableField(exist = false) @ApiModelProperty("附件") FileEntryList attach; ``` - **禁止**在 `CREATE TABLE` 中声明 `attach`/`photos`/`evidence_files` 等 CLOB 列;**禁止**用 `String` 存逗号分隔文件 ID、`List` 裸 ID、JSON 字符串等替代。 - DTO/Vo 与 Entity 保持一致:接收、返回附件均用 **`FileEntryList` 或 `FileEntry`**,并标注 **`@TableField(exist = false)`**(映射表字段时)。 #### 自检(建表 / 新建实体) - [ ] 关联列 COMMENT 含「关联{表名}表的{字段}字段」 - [ ] 字典/枚举列 COMMENT 含「字典类型,目录码…」或「枚举类型,…Enum」 - [ ] 设计文档 §2.1 已维护该表字典/枚举清单(**不含**公共列与关联列) - [ ] 实体 `extends BaseEntity`,且无重复审计字段(`deleted` 若父类已有则不重复) - [ ] 逻辑删除为 **`Boolean deleted`** + 列 **`DELETED`**,需要时含 `@TableLogic` - [ ] 组织/用户 Entry 对应 **`NUMBER(19)`** 单列;字典 Entry 对应 **`VARCHAR2`**;附件 **`@TableField(exist = false)`** 且无库列 - [ ] 明细表与主表均具备相同公共列(非「纯关系表」例外须在表注释说明) ### 实体与值对象(部门 / 用户 / 字典) - 主键 `@TableId(value = "ID", type = IdType.ASSIGN_ID)`(雪花)。 - Lombok:`@Accessors(chain = true)`(按需);字段 **`@TableField` 大写列名**。 #### 部门 / 公司(OrganizationEntry) - **仅持久层实体、Vo** 使用 **`OrganizationEntry`** 表示部门/公司;库列类型 **`NUMBER(19)`**(如 `apply_dept_id`),**禁止**裸 `Long deptId` + `String deptName` 双字段落库,**禁止**为 Entry 再建 `apply_dept_name` 等快照列。 - **`Query` / `*LedgerQuery` 中禁止使用 `OrganizationEntry`**:按部门/公司筛选时用 **`Long`**(如 `demandCompany`、`applyDeptId`),按名称模糊筛选用 **`String`**(如 `deptName`,按需);与 `PurchasePlanQuery.demandCompany` 一致。 - **新增/修改赋值(Entity)**:`OrganizationEntry.of(部门或公司ID)`;会话上下文可用 `ApplicationSession.getDepartmentId()` / `getCompanyId()`。 - **Service 条件拼装**:Query 的 `Long` 与实体 `OrganizationEntry` 字段对接,例如 `.eq(Objects.nonNull(q.getDemandCompany()), Entity::getDemandCompany, q.getDemandCompany())`(见 `PurchasePlanServiceImpl`)。 #### 用户(UserNameEntry) - **Entity、Vo** 上申请人、入库人等:**`UserNameEntry`**;库列类型 **`NUMBER(19)`**(如 `apply_user_id`),**禁止** `Long userId` + `String userName` 双字段落库。 - **Query 中不使用 `UserNameEntry`**;按用户筛选用 **`Long userId`** 或 **`String` 用户名**(如 `inboundUser`)。 - **赋值**:`UserNameEntry.of(ApplicationSession.getUserId())` 或业务传入的用户 ID。 #### 字典(DictEntry + @DictDirectory) - 字典项字段类型 **`DictEntry`**;库列类型 **`VARCHAR2`**,存字典项 **code**;字段上必须标注字典目录: ```java @DictDirectory(MeterConstant.DICT_METER_JJCD) // 字典目录码,定义在 MeterConstant @TableField("PRIORITY_CODE") @ApiModelProperty("紧急程度") DictEntry priority; ``` - **目录常量**集中在 **`com.tofly.wm.cons.MeterConstant`**,例如: - `DICT_METER_JJCD` — 紧急程度 - `DICT_METER_TYPE` — 水表类型 - `DICT_METER_DIAMETER` — 口径 - `DICT_WORKER_ORDER` — 工单类型 - 新建字典目录时:**先在 `MeterConstant` 增加常量** → 再在实体/Vo 上使用 `@DictDirectory`。 - 按字典编码写死业务分支时:`DictEntry.of(MeterConstant.PURCHASE_APPLY)` 等(见 `PurchaseApplyServiceImpl`)。 - Query 中若仅按字典 code 筛选,可用 `String` + `eq`;**回显/入库**仍用实体上的 **`DictEntry`**。 #### 业务枚举(非字典) - 状态、出入库类型等:**实现 `Enumerable`**,`getKey()` / `getLabel()`,放 **`{domain}/enums/`**。 ### 注释规范 | 层级 | 要求 | |------|------| | **类** | JavaDoc:中文业务说明;`@author`、`@date`(与 ledger 存量一致) | | **public 方法(Service)** | JavaDoc:`@param`、`@return`;说明业务含义,非复述方法名 | | **关键代码** | 仅对**非显而易见**逻辑:状态流转、双写出入库、补偿、Wrappers 中每组条件的业务含义(参考 `MeterServiceImpl.list` 行尾 `//总公司/分公司`) | | **禁止** | 无信息注释(`// 查询`、`// 保存`);大量注释掩盖坏命名 | 示例(Service 条件注释,摘自 meter): ```java .eq(Objects.nonNull(q.getCompanyType()), Meter::getCompanyType, Optional.ofNullable(q.getCompanyType()).map(CompanyTypeEnum::getKey).orElse(null)) //出厂编码 .like(StrUtil.isNotBlank(q.getFactoryCode()), Meter::getFactoryCode, q.getFactoryCode()) ``` ### Swagger 与日志 - 类 `@Api(tags)`;方法 `@ApiOperation`;路径参数 `@ApiParam`。 - 写操作按 **`java-tf-ylsw-backend`** 补 `@ToFlyAppLog`(ledger/meter 存量未全覆盖时,新增写接口应补齐)。 ## 生成步骤 1. **识别域与参考类** - 标准 CRUD → `ledger/meter`(`MeterController` ~ `MeterMapper`)。 - 只读台账 + 导出 → `ledger/purchase` 或 `ledger/verify`。 2. **生成 Query / Vo**(分页查询、导出列)。 3. **生成 Entity + Enum**(表结构对齐 `TF_WM_*`)。 4. **生成 Mapper + Service(Impl)**(单表 Wrappers,多表 MPJJoin)。 5. **生成 Controller**(`ResultResponse`、权限、路由;**无** `DELETE /{id}`)。 6. **同步接口文档**(标准 CRUD 模板接口全量列出,见 §接口文档生成)。 7. **按需生成 Mapper.xml**(仅复杂 SQL)。 8. **自检**(见下)。 ## 输出格式 ```markdown ## CRUD 结果(ylsw-bw) - 域包:`com.tofly.wm.ledger.{domain}` - 参考模板:`ledger/meter` | `ledger/purchase` | ... - 新增/修改文件:`...` ## 接口清单 - `GET /{resource}/page` - `GET /{resource}/list` - `GET /{resource}/{id}` - `POST /{resource}/` - `POST /{resource}/update` - `DELETE /{resource}/deleteByIds`(无 `DELETE /{resource}/{id}`) - ... ## 规范检查 - [x] ResultResponse 带泛型 - [x] 包路径符合 ledger/manage 职责划分 - [x] 字符串用 StrUtil,对象空值用 Objects.nonNull/isNull - [x] Entity/Vo 使用 OrganizationEntry、UserNameEntry、DictEntry + @DictDirectory;Query 不用 Entry 对象 - [x] 字典目录常量来自 MeterConstant - [x] 类/方法/关键逻辑注释符合规范 - [x] 分页使用 PageHelper + PageQuery - [x] 简单查询用 Wrappers / MPJ,复杂 SQL 在 XML - [x] Mapper 无注解 SQL - [x] 表含 BaseEntity 全部公共列;实体 `extends BaseEntity` 且无重复审计字段 - [x] 关联列 COMMENT 含「关联{表名}表的{字段}字段」 - [x] 字典/枚举列 COMMENT 含「字典类型,目录码…」或「枚举类型,…Enum」 - [x] 数据库设计 §2.1 仅含字典/枚举列,与 DDL COMMENT 一致 - [x] 无 `DELETE /{id}`;删除仅 `deleteByIds` - [x] 接口文档已按模板全量列出 CRUD 路径 ``` ## 参考 - 模板代码见 [examples-ylsw-bw.md](examples-ylsw-bw.md) - 仓库实现:`business/water-meter/src/main/java/com/tofly/wm/ledger/meter/`