以下示例提炼自 business/water-meter/src/main/java/com/tofly/wm/ledger/meter/,生成新模块时替换 {Domain}、{domain}、TF_WM_{DOMAIN}_W 等占位符即可。
package com.tofly.wm.ledger.{domain};
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tofly.common.core.entity.ResultResponse;
import com.tofly.entity.enumertion.Permission;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RequestMapping("/{domain}")
@RestController
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
@RequiredArgsConstructor
@Api(tags = "{业务中文名}")
public class {Domain}Controller {
{Domain}Service {domain}Service;
@GetMapping("/page")
@ApiOperation("{业务中文名}分页查询")
public ResultResponse<Page<{Domain}>> page({Domain}Query query) {
return ResultResponse.success(this.{domain}Service.page(query));
}
@GetMapping("/list")
@ApiOperation("{业务中文名}列表查询")
public ResultResponse<List<{Domain}>> list({Domain}Query query) {
return ResultResponse.success(this.{domain}Service.list(query));
}
@GetMapping("/{id}")
@ApiOperation("{业务中文名}详情查询")
public ResultResponse<{Domain}> getById(@PathVariable @ApiParam("主键") Long id) {
return ResultResponse.success(this.{domain}Service.getById(id));
}
@PostMapping
@ApiOperation("{业务中文名}新增")
public ResultResponse<Boolean> add(@RequestBody {Domain} entity) {
return ResultResponse.success(this.{domain}Service.save(entity));
}
@PostMapping("/update")
@ApiOperation("{业务中文名}修改")
public ResultResponse<Boolean> update(@RequestBody {Domain} entity) {
return ResultResponse.success(this.{domain}Service.updateById(entity));
}
@DeleteMapping("/deleteByIds")
@ApiOperation("{业务中文名}删除")
public ResultResponse<Boolean> deleteByIds(@RequestParam @ApiParam("主键,逗号分隔,如 1,2,3") String ids) {
return ResultResponse.success(this.{domain}Service.deleteByIds(ids));
}
}
GET /list 与 GET /page 共用同一 Query,条件字段保持一致;/page 额外使用 PageQuery 中的 pageNum、pageSize。
Query 不使用 OrganizationEntry / UserNameEntry / DictEntry,部门/用户筛选用 Long 或 String;值对象仅用于 Entity/Vo。
package com.tofly.wm.ledger.{domain};
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tofly.entity.pojo.PageQuery;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("{业务中文名}查询")
public class {Domain}Query extends PageQuery {
@ApiModelProperty("申请部门ID")
Long applyDeptId;
@ApiModelProperty("示例编码")
String code;
@Override
public void wrapper(LambdaQueryWrapper wrapper) {
// 单表场景可留空,条件在 Service 的 buildWrapper 中构建
}
}
强制:所有 @TableName 实体 extends BaseEntity;公共列由父类映射。建表时在 CREATE TABLE 中声明公共列(见 java-ylsw-bw.md § BaseEntity),勿用 ALTER TABLE 补列。
禁止在子类重复声明:createUser、createTime、updateUser、updateTime、createCompanyId~curDeptId(Boolean deleted 若 BaseEntity 已提供则不再声明)。
package com.tofly.wm.manage.{biz};
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.tofly.entity.annotation.DictDirectory;
import com.tofly.entity.pojo.BaseEntity;
import com.tofly.entity.pojo.DictEntry;
import com.tofly.entity.pojo.FileEntryList;
import com.tofly.entity.pojo.OrganizationEntry;
import com.tofly.entity.pojo.UserNameEntry;
import com.tofly.wm.cons.MeterConstant;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.FieldDefaults;
/**
* {业务中文名}实体
*
* @author ...
* @date ...
*/
@Data
@EqualsAndHashCode(callSuper = true)
@FieldDefaults(level = AccessLevel.PRIVATE)
@TableName("TF_WM_{DOMAIN}_W")
@ApiModel("{业务中文名}")
public class {Domain} extends BaseEntity {
@TableId(value = "ID", type = IdType.ASSIGN_ID)
@ApiModelProperty("主键")
Long id;
@TableField("APPLY_DEPT_ID")
@ApiModelProperty("申请部门")
OrganizationEntry applyDept;
@TableField("APPLY_USER_ID")
@ApiModelProperty("申请人")
UserNameEntry applyUser;
@DictDirectory(MeterConstant.DICT_METER_JJCD)
@TableField("PRIORITY_CODE")
@ApiModelProperty("紧急程度")
DictEntry priority;
@DictDirectory(MeterConstant.DICT_METER_TYPE)
@TableField("METER_TYPE_CODE")
@ApiModelProperty("水表类型")
DictEntry meterType;
@TableField(exist = false)
@ApiModelProperty("附件")
FileEntryList attach;
}
逻辑删除(BaseEntity 未含 deleted 时在子类声明;库列仍为 DELETED):
@TableLogic
@TableField("DELETED")
@ApiModelProperty("删除状态")
Boolean deleted = false;
单附件(仅一个文件时用 FileEntry,同样不落库):
@TableField(exist = false)
@ApiModelProperty("附件")
FileEntry attach;
明细表示例(同样继承 BaseEntity):
@TableName("TF_WM_{DOMAIN}_LIST_W")
public class {Domain}List extends BaseEntity {
@TableId(value = "ID", type = IdType.ASSIGN_ID)
Long id;
@TableField("PARENT_ID")
Long parentId;
// 仅业务字段,无 CREATE_USER/DELETED 等
}
保存时赋值(Service):
entity.setApplyDept(OrganizationEntry.of(ApplicationSession.getDepartmentId()));
entity.setApplyUser(UserNameEntry.of(ApplicationSession.getUserId()));
建表 COMMENT(关联列须写目标表):
COMMENT ON COLUMN TF_WM_{DOMAIN}_W.APPLY_DEPT_ID IS '申请部门ID,关联tf_org表的id字段';
COMMENT ON COLUMN TF_WM_{DOMAIN}_W.APPLY_USER_ID IS '申请用户ID,关联tf_user表的id字段';
COMMENT ON COLUMN TF_WM_{DOMAIN}_W.PRIORITY_CODE IS '紧急程度编号,字典类型,目录码METER_JJCD';
COMMENT ON COLUMN TF_WM_{DOMAIN}_W.TASK_STATUS_CODE IS '任务状态编号,枚举类型,TaskStatusEnum';
package com.tofly.wm.ledger.{domain}.enums;
import com.tofly.entity.enumertion.Enumerable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum {Domain}StatusEnum implements Enumerable<Integer> {
ENABLED(1, "启用"),
DISABLED(0, "停用"),
;
Integer code;
String label;
@Override
public Integer getKey() {
return this.code;
}
}
package com.tofly.wm.ledger.{domain};
import com.github.yulichang.base.MPJBaseMapper;
public interface {Domain}Mapper extends MPJBaseMapper<{Domain}> {
}
package com.tofly.wm.ledger.{domain};
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.base.MPJBaseService;
public interface {Domain}Service extends MPJBaseService<{Domain}> {
/** 列表查询,条件与 page 一致 */
List<{Domain}> list({Domain}Query query);
Page<{Domain}> page({Domain}Query query);
/** ids 逗号分隔,如 "1,2,3" */
boolean deleteByIds(String ids);
}
package com.tofly.wm.ledger.{domain};
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.tofly.mybatisplus.page.PageHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Service
public class {Domain}ServiceImpl extends MPJBaseServiceImpl<{Domain}Mapper, {Domain}>
implements {Domain}Service {
/**
* 列表与分页共用条件
*/
private LambdaQueryWrapper<{Domain}> buildWrapper({Domain}Query q) {
return Wrappers.<{Domain}>lambdaQuery()
//申请部门(Query 用 Long,Entity 字段为 OrganizationEntry)
.eq(Objects.nonNull(q.getApplyDeptId()), {Domain}::getApplyDept, q.getApplyDeptId())
//编码
.like(StrUtil.isNotBlank(q.getCode()), {Domain}::getCode, q.getCode())
.eq({Domain}::getDeleted, false);
}
@Override
public List<{Domain}> list({Domain}Query query) {
return this.list(buildWrapper(query));
}
@Override
public Page<{Domain}> page({Domain}Query query) {
return PageHelper.startPage(query, p -> this.list(query));
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteByIds(String ids) {
if (StrUtil.isBlank(ids)) {
return false;
}
List<Long> idList = Arrays.stream(ids.split(","))
.map(String::trim)
.filter(StrUtil::isNotBlank)
.map(Long::parseLong)
.collect(Collectors.toList());
return this.removeByIds(idList);
}
}
参考 ledger/arrive、ledger/verify:
@Api(tags = "{台账中文名}")
@RestController
@RequestMapping("/{domain}Ledger")
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class {Domain}LedgerController {
{Domain}LedgerService {domain}LedgerService;
@GetMapping("/page")
@ApiOperation("分页查询")
public ResultResponse<Page<{Domain}LedgerVo>> page({Domain}LedgerQuery query) {
return ResultResponse.success({domain}LedgerService.page(query));
}
@GetMapping("/export")
@ApiOperation("导出")
public void export({Domain}LedgerQuery query) {
{domain}LedgerService.export(query);
}
}
@Service
@AllArgsConstructor
public class {Domain}LedgerService {
SomeEntityService someEntityService;
public List<{Domain}LedgerVo> getList({Domain}LedgerQuery query) {
return someEntityService.selectJoinList({Domain}LedgerVo.class,
MPJWrappers.<SomeEntity>lambdaJoin()
.selectAs(SomeEntity::getId, {Domain}LedgerVo::getId)
.selectAs(Related::getName, {Domain}LedgerVo::getRelatedName)
.innerJoin(Related.class, Related::getId, SomeEntity::getRelatedId)
.eq(SomeEntity::getDeleted, false)
.like(StrUtil.isNotBlank(query.getCode()), SomeEntity::getCode, query.getCode())
);
}
public Page<{Domain}LedgerVo> page({Domain}LedgerQuery query) {
return PageHelper.startPage(query, this::getList);
}
public void export({Domain}LedgerQuery query) {
List<{Domain}LedgerVo> voList = this.getList(query);
Map<String, Object> dataMap = HashMaps.of("dataList", voList);
EasyPoiUtil.download("{台账中文名}.xlsx", "/template/{domain}Ledger.xlsx", dataMap);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tofly.wm.ledger.{domain}.{Domain}Mapper">
<select id="selectCustomPage" resultType="com.tofly.wm.ledger.{domain}.{Domain}Vo">
SELECT t.ID, t.CODE, t.CREATE_TIME
FROM TF_WM_{DOMAIN}_W t
WHERE t.DELETED = 0
<if test="qry.code != null and qry.code != ''">
AND t.CODE LIKE '%' || #{qry.code} || '%'
</if>
ORDER BY t.CREATE_TIME DESC
</select>
</mapper>
package com.tofly.wm.cons;
public interface MeterConstant {
String DICT_METER_JJCD = "METER_JJCD"; // 紧急程度
String DICT_METER_TYPE = "METER_TYPE"; // 水表类型
String DICT_METER_DIAMETER = "METER_DIAMETER"; // 口径
String DICT_WORKER_ORDER = "METER_ORDER_TYPE"; // 工单类型
String PURCHASE_APPLY = "PURCHASE_APPLY"; // 采购申请流水类型
}
// 错误:返回裸 ResultResponse
public ResultResponse page(MeterQuery query) { ... }
// 错误:在 Mapper 上用注解写 SQL
@Select("select * from TF_WM_METER_W")
List<Meter> listAll();
// 错误:Controller 内拼装 Wrappers / 调 Mapper
@GetMapping("/page")
public ResultResponse<Page<Meter>> page(MeterQuery query) {
return ResultResponse.success(meterMapper.selectPage(...));
}
// 错误:提供单条删除(本仓库仅 DELETE /deleteByIds)
@DeleteMapping("/{id}")
public ResultResponse<Boolean> delete(@PathVariable Long id) { ... }
// 错误:与 meter 模块不一致却强行使用 PUT 修改(本仓库 ledger CRUD 用 POST /update)
@PutMapping
public ResultResponse<Boolean> update(@RequestBody Meter meter) { ... }
// 错误:list 与 page 使用不同 Query 或不同条件逻辑(应共用 buildWrapper / list(query))
@GetMapping("/list")
public ResultResponse<List<Meter>> list(MeterListQuery query) { ... }
// 错误:Entity 用 Long + String 拆部门,未用 OrganizationEntry
Long applyDeptId;
String applyDeptName;
// 错误:OrganizationEntry/UserNameEntry 再建 *_name 快照列
// apply_dept_id + apply_dept_name 双列落库
// 错误:DictEntry 映射 NUMBER 或非 VARCHAR 列
@TableField("PRIORITY")
Integer priorityCode;
// 错误:FileEntry 映射库列 CLOB/ATTACH
@TableField("ATTACH")
FileEntryList attach;
// 错误:FileEntry 未标注 exist = false
FileEntryList photos;
// 错误:Query 使用 OrganizationEntry(Query 应使用 Long applyDeptId 等)
OrganizationEntry applyDept;
// 错误:字典字段无 @DictDirectory 或魔法字符串
@TableField("PRIORITY_CODE")
String priorityCode;
| 类型 | 路径 |
|---|---|
| 标准 CRUD | ledger/meter/MeterController.java 等 |
| 采购台账 | ledger/purchase/PurchaseLedgerController.java |
| 到货台账 | ledger/arrive/MeterArriveLedgerController.java |
| 落地检台账 | ledger/verify/MeterVerifyLedgerController.java |
| 附件 FileEntryList | Entity 用 @TableField(exist = false),参考 manage/puachase/claim/PurchaseClaim.java |