# Single-Table CRUD Reference ## Inputs to Confirm Before generating files, identify: - Module/plugin name, business object name, table name, route path, component path, menu parent, and permission prefix. - Field list with DB type, Java type, frontend component type, validation, searchable/list/edit/import/export flags, and sort/index needs. - Dictionary fields and whether each dictionary reuses an existing `dict_type` or needs new seed data. - Sensitive fields that require API encryption/decryption or response masking. - Import/export enablement and Excel `config_key`. ## Backend File Layout Follow the existing plugin package layout: ```text forge/forge-framework/forge-plugin-parent/forge-plugin-/ ├── src/main/java/com/mdframe/forge/plugin// │ ├── controller/Controller.java │ ├── domain/entity/.java │ ├── dto/DTO.java │ ├── dto/Query.java │ ├── mapper/Mapper.java │ ├── service/Service.java │ ├── service/impl/ServiceImpl.java │ └── vo/VO.java └── src/main/resources/mapper/Mapper.xml ``` If the target module has a different established package pattern, follow the module pattern instead of introducing a parallel convention. ## Entity Pattern Use `TenantEntity` for business tables with `tenant_id`. ```java @Data @EqualsAndHashCode(callSuper = true) @TableName("biz_example") public class BizExample extends TenantEntity { @Serial private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.ASSIGN_ID) private Long id; private String exampleName; private String status; } ``` Do not duplicate `createBy`, `createTime`, `createDept`, `updateBy`, `updateTime`, or `tenantId` fields when extending `TenantEntity`. ## Controller Contract Generate Forge codegen-safe endpoints. Do not use `PUT` or `DELETE` for generated CRUD modules because project gateway and security policies expect POST for detail, update, and delete operations. ```java @Slf4j @RestController @RequestMapping("/biz/example") @RequiredArgsConstructor public class BizExampleController { private final BizExampleService exampleService; @GetMapping("/page") @OperationLog(module = "示例管理", type = OperationType.QUERY, desc = "分页查询示例") public RespInfo> page(PageQuery pageQuery, BizExampleQuery query) { return RespInfo.success(exampleService.page(pageQuery, query)); } @PostMapping("/getById") @OperationLog(module = "示例管理", type = OperationType.QUERY, desc = "查询示例详情") public RespInfo detail(@RequestParam Long id) { return RespInfo.success(exampleService.getDetail(id)); } @PostMapping("/add") @OperationLog(module = "示例管理", type = OperationType.ADD, desc = "新增示例") public RespInfo create(@RequestBody BizExampleDTO dto) { return RespInfo.success(exampleService.create(dto)); } @PostMapping("/edit") @OperationLog(module = "示例管理", type = OperationType.UPDATE, desc = "修改示例") public RespInfo update(@RequestBody BizExampleDTO dto) { exampleService.update(dto); return RespInfo.success(); } @PostMapping("/remove/{id}") @OperationLog(module = "示例管理", type = OperationType.DELETE, desc = "删除示例") public RespInfo delete(@PathVariable Long id) { exampleService.delete(id); return RespInfo.success(); } @PostMapping("/removeBatch") @OperationLog(module = "示例管理", type = OperationType.DELETE, desc = "批量删除示例") public RespInfo removeBatch(@RequestBody Long[] ids) { exampleService.deleteBatch(ids); return RespInfo.success(); } } ``` Add class-level or method-level `@ApiDecrypt` and `@ApiEncrypt` when fields or the page use encrypted requests. For mixed endpoints, encrypt reads with `@ApiEncrypt` and decrypt mutating request bodies with `@ApiDecrypt`. ## Service and Mapper Rules - Service methods handle validation, uniqueness checks, DTO-to-entity mapping, and transaction boundaries. - Mapper XML handles page/list/detail queries and any joins required for VO rendering. - Batch delete must validate `ids != null && ids.length > 0`, then use `removeByIds(Arrays.asList(ids))` or a Mapper XML delete only if custom constraints require it. - Do not inject two Services into each other. Put orchestration in the Controller or a Manager class. Mapper XML page query skeleton: ```xml ``` For fields named `region_code`, include the `ALL` virtual organization rule from `AGENTS.md` in Mapper XML. ## Frontend Page Pattern Use `AiCrudPage`; do not hand-roll table, pagination, add, edit, or delete unless the page pattern requires a custom wrapper. ```vue ``` If a numeric DB field uses dictionary values stored as strings, convert options through a local computed helper so submitted values match backend types. ## Import and Export For fixed generated CRUD, prefer the common Excel endpoints when no business-specific permission wrapping is required: - `POST /api/excel/export/{configKey}` - `GET /api/excel/template/{configKey}` - `POST /api/excel/import/{configKey}` If the business module needs custom permission, validation, or persistence logic, generate wrapper endpoints under the business Controller and keep the same request/response expectations used by AiCrudPage. Always generate matching SQL for `sys_excel_export_config` and `sys_excel_column_config` when enabling import/export.