name: crud-by-controller-ylsw-bw
当用户要求在 business/water-meter 或表务相关模块中新增/补全接口时,优先对齐 com.tofly.wm.ledger 下既有代码(首选参考 ledger/meter,台账类参考 ledger/purchase、ledger/arrive、ledger/verify)。
language/java/pom-factory-skill.md;Maven 治理:rules/java-pom-governance.mdc。rules/java-crud-common.mdc。rules/java-tf-ylsw-backend.mdc(与 ledger 实际写法冲突时,以 ledger 存量风格为准,并在输出中注明差异)。com.tofly.wm)模块根包 com.tofly.wm,按业务分层;新建代码必须落在与职责匹配的包下,禁止全部堆在 ledger 或单一层级。
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.{domain} |
| 主表 CRUD、流程、审批、写入业务表 | manage.{业务域} |
| 移动端/专项入口 | ledger.{domain}.app 或 manage.*.app |
ResultResponse<T>(带泛型,禁止裸类型)。@RestController + @RequestMapping("/xxx") + @Api(tags = "...")。@RequiredArgsConstructor + @FieldDefaults(level = PRIVATE, makeFinal = true)(Controller 常见);Service 可用 @AllArgsConstructor 或 @Service 无接口实现类。| 能力 | 方法 | 路径 | 说明 |
|---|---|---|---|
| 分页 | GET | /page |
与 /list 共用 {Domain}Query(含 pageNum/pageSize) |
| 列表 | GET | /list |
与 /page 同一 Query、同一 Service 条件;不分页,返回 List |
| 详情 | GET | /{id} |
|
| 新增 | POST | / |
@RequestBody 实体 |
| 修改 | POST | /update |
本模块用 POST 而非 PUT |
| 删除 | DELETE | /{id} |
|
| 批量删除 | DELETE | /deleteByIds |
ids 请求参数,逗号分隔主键 |
| 导出 | GET | /export |
返回 void,EasyPoiUtil.download(台账类) |
| 场景 | 用法 | 禁止/避免 |
|---|---|---|
| 字符串为空 | 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 |
cn.hutool.core.*(StrUtil、CollUtil、BeanUtil 等),与项目存量一致。Objects。PageQuery,Swagger @ApiModel / @ApiModelProperty。Long/String/枚举/LocalDate 等基础类型;OrganizationEntry、UserNameEntry、DictEntry 仅出现在 Entity/Vo,不出现在 Query(字典筛选可用字典 code 的 String)。Wrappers.<Entity>lambdaQuery(),条件首参遵循上表;枚举用 Enumerable.getKey()。list(query) 与分页共用 buildWrapper;page(query) 内部 PageHelper.startPage(query, () -> list(query))。deleteByIds(String ids) 解析逗号分隔主键后 removeByIds,需 @Transactional。MPJWrappers.lambdaJoin() + selectJoinList(见 purchase/verify 台账),不强行拆 XML。//出厂编码、//需求部门。MPJBaseService<Entity> / MPJBaseServiceImpl<Mapper, Entity>。MPJBaseMapper<Entity>;简单查询不写 XML。*Mapper.xml 编写,禁止 Mapper 注解 SQL。BaseEntity;主键 @TableId(value = "ID", type = IdType.ASSIGN_ID)(雪花)。@Data、@Accessors(chain = true)、@FieldDefaults(level = PRIVATE);字段 @TableField 大写列名。OrganizationEntry 存部门/公司(含 id、name 映射),禁止裸 Long deptId + String deptName 双字段落库。Query / *LedgerQuery 中禁止使用 OrganizationEntry:按部门/公司筛选时用 Long(如 demandCompany、applyDeptId),按名称模糊筛选用 String(如 deptName,按需);与 PurchasePlanQuery.demandCompany 一致。OrganizationEntry.of(部门或公司ID);会话上下文可用 ApplicationSession.getDepartmentId() / getCompanyId()。Long 与实体 OrganizationEntry 字段对接,例如.eq(Objects.nonNull(q.getDemandCompany()), Entity::getDemandCompany, q.getDemandCompany())(见 PurchasePlanServiceImpl)。UserNameEntry,禁止仅 Long userId 落库。UserNameEntry;按用户筛选用 Long userId 或 String 用户名(如 inboundUser)。UserNameEntry.of(ApplicationSession.getUserId()) 或业务传入的用户 ID。字典项字段类型 DictEntry;字段上必须标注字典目录:
@DictDirectory(MeterConstant.DICT_METER_JJCD) // 字典目录码,定义在 MeterConstant
@TableField("PRIORITY")
@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<T>,getKey() / getLabel(),放 {domain}/enums/。| 层级 | 要求 |
|---|---|
| 类 | JavaDoc:中文业务说明;@author、@date(与 ledger 存量一致) |
| public 方法(Service) | JavaDoc:@param、@return;说明业务含义,非复述方法名 |
| 关键代码 | 仅对非显而易见逻辑:状态流转、双写出入库、补偿、Wrappers 中每组条件的业务含义(参考 MeterServiceImpl.list 行尾 //总公司/分公司) |
| 禁止 | 无信息注释(// 查询、// 保存);大量注释掩盖坏命名 |
示例(Service 条件注释,摘自 meter):
.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())
@Api(tags);方法 @ApiOperation;路径参数 @ApiParam。java-tf-ylsw-backend 补 @ToFlyAppLog(ledger/meter 存量未全覆盖时,新增写接口应补齐)。ledger/meter(MeterController ~ MeterMapper)。ledger/purchase 或 ledger/verify。TF_WM_*)。ResultResponse、权限、路由)。## CRUD 结果(ylsw-bw)
- 域包:`com.tofly.wm.ledger.{domain}`
- 参考模板:`ledger/meter` | `ledger/purchase` | ...
- 新增/修改文件:`...`
## 接口清单
- `GET /{resource}/page`
- `GET /{resource}/list`
- `DELETE /{resource}/deleteByIds`
- ...
## 规范检查
- [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
business/water-meter/src/main/java/com/tofly/wm/ledger/meter/