adspecs 0.1.33 → 0.1.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.adspecs/paths.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codebuddy-plugin/plugin.json +1 -1
- package/.qoder-plugin/plugin.json +1 -1
- package/.workbuddy-plugin/plugin.json +1 -1
- package/CLAUDE.md +2 -2
- package/README.md +1 -1
- package/package.json +1 -1
- package/references/ant6-front-standard/02-/347/273/204/344/273/266/350/247/204/350/214/203.md +7 -0
- package/references/ant6-front-standard/03-/345/210/227/350/241/250/350/247/204/350/214/203.md +222 -147
- package/references/ant6-front-standard/04-/350/241/250/345/215/225/350/247/204/350/214/203.md +8 -0
- package/references/ant6-front-standard/09-/345/270/270/350/247/201/351/227/256/351/242/230/350/247/204/350/214/203.md +1 -1
- package/references/ant6-front-standard/index.md +100 -99
- package/references/yudaocloud-end-standard/01-Java/345/220/216/347/253/257/347/274/226/347/240/201/350/247/204/350/214/203.md +1166 -0
- package/references/yudaocloud-end-standard/02-/346/225/260/346/215/256/345/272/223/350/256/276/350/256/241/344/270/216/344/275/277/347/224/250/350/247/204/350/214/203.md +1023 -0
- package/references/yudaocloud-end-standard/03-/346/225/260/346/215/256/345/255/227/345/205/270/344/270/216/350/217/234/345/215/225/350/247/204/350/214/203.md +336 -0
- package/references/yudaocloud-end-standard/index.md +14 -3
- package/references/yudaocloud-end-standard/system_dict_type.sql +186 -0
- package/skills/adspecs-adversarial-review/SKILL.md +1 -1
- package/skills/adspecs-analyze/SKILL.md +564 -574
- package/skills/adspecs-plan/SKILL.md +1 -1
- package/skills/adspecs-utest/SKILL.md +62 -40
- package/skills/project-init/SKILL.md +4 -4
- package/src/lib/paths-defaults.js +1 -1
- package/src/lib/readme-gen.js +1 -1
- package/references/ant6-front-standard/05-/345/211/215/347/253/257/347/274/226/347/240/201/350/247/204/350/214/203.md +0 -1443
|
@@ -0,0 +1,1166 @@
|
|
|
1
|
+
# ultracloud 项目 Java 后端编码规范
|
|
2
|
+
|
|
3
|
+
> **版本**: v2.2
|
|
4
|
+
> **修订日期**: 2026-08-30
|
|
5
|
+
> **适用范围**: ultracloud 项目所有 Java 后端模块
|
|
6
|
+
|
|
7
|
+
本规范基于项目实际代码(ultracloud-module-system、ultracloud-module-infra、ultracloud-module-bpm、ultracloud-module-ai)和 ultracloud-framework 框架源码编写,可直接落地、团队通用。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 一、项目约定
|
|
12
|
+
|
|
13
|
+
### 1.1 技术栈
|
|
14
|
+
|
|
15
|
+
| 组件 | 版本 |
|
|
16
|
+
| -------------------- | --------- |
|
|
17
|
+
| Java | JDK 25 |
|
|
18
|
+
| Spring Boot | 4.1.0 |
|
|
19
|
+
| Spring Cloud | 2025.1.2 |
|
|
20
|
+
| Spring Cloud Alibaba | 2025.1.0.0|
|
|
21
|
+
| MyBatis Plus | 3.5.16 |
|
|
22
|
+
| MyBatis Plus Join | 1.5.7 |
|
|
23
|
+
| Redis + Redisson | 4.6.1 |
|
|
24
|
+
| Flowable | 8.0.0 |
|
|
25
|
+
| MapStruct | 1.6.3 |
|
|
26
|
+
| Knife4j | 4.6.0.3 |
|
|
27
|
+
| Springdoc | 3.0.3 |
|
|
28
|
+
| Lombok | 1.18.46 |
|
|
29
|
+
| Hutool | 5.8.46 |
|
|
30
|
+
| Druid | 1.2.28 |
|
|
31
|
+
| Easy-Trans | 3.1.5 |
|
|
32
|
+
| Lock4j | 2.2.7 |
|
|
33
|
+
|
|
34
|
+
### 1.2 基础约定
|
|
35
|
+
|
|
36
|
+
- **ID 类型**: 统一使用 `Long`(自增主键),不使用 UUID
|
|
37
|
+
- **时间类型**: 统一使用 `LocalDateTime`,不使用 `Date`/`Instant`
|
|
38
|
+
- **依赖注入**: 统一使用 `@Resource`(Jakarta),不使用 `@Autowired`
|
|
39
|
+
- **对象命名**: DO(数据对象)替代 PO,继承 BaseDO/TenantBaseDO
|
|
40
|
+
- **审计字段**: createTime/updateTime/creator/updater/deleted 由框架自动填充,无需手动维护
|
|
41
|
+
- **对象转换**: 简单转换使用 `BeanUtils.toBean()`,复杂映射使用 MapStruct
|
|
42
|
+
- **分页返回**: 使用 `PageResult<T>`,分页参数继承 `PageParam`
|
|
43
|
+
- **异常抛出**: 使用 `ServiceExceptionUtil.exception(ErrorCodeConstants.XXX)`
|
|
44
|
+
- **数据翻译**: 使用 `@TransMethodResult` + Easy-Trans 自动翻译关联字段
|
|
45
|
+
- **租户隔离**: 使用 `TenantBaseDO` + 框架自动过滤,跨租户查询加 `@TenantIgnore`
|
|
46
|
+
- **错误码段**: `1_XXX_YYY_ZZZ`(XXX 为模块编号,如 Infra=001, System=002, BPM=003, AI=040)
|
|
47
|
+
|
|
48
|
+
### 1.3 模块结构
|
|
49
|
+
|
|
50
|
+
每个业务模块采用 `-api` + `-server` 双模块结构:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
ultracloud-module-xxx/
|
|
54
|
+
├── ultracloud-module-xxx-api/ # 跨模块 RPC 接口 + DTO + 枚举 + 错误码
|
|
55
|
+
│ └── cn.ultracloud.com.module.xxx/
|
|
56
|
+
│ ├── api/ # @FeignClient 接口定义
|
|
57
|
+
│ ├── dto/ # RPC 传输 DTO
|
|
58
|
+
│ └── enums/ # ErrorCodeConstants、业务枚举
|
|
59
|
+
└── ultracloud-module-xxx-server/ # 服务实现
|
|
60
|
+
└── cn.ultracloud.com.module.xxx/
|
|
61
|
+
├── controller/admin/ # 管理端 REST(自动添加 /admin-api 前缀)
|
|
62
|
+
│ └── {domain}/vo/ # VO 定义(按业务域子目录)
|
|
63
|
+
├── controller/app/ # C 端 API(自动添加 /app-api 前缀)
|
|
64
|
+
├── service/{domain}/ # Service 接口 + Impl
|
|
65
|
+
├── dal/dataobject/{domain}/ # DO 实体
|
|
66
|
+
├── dal/mysql/{domain}/ # MyBatis Plus Mapper
|
|
67
|
+
├── convert/ # MapStruct 转换器
|
|
68
|
+
├── api/ # RPC API 实现(@RestController)
|
|
69
|
+
└── framework/config/ # 模块配置(SecurityConfiguration 等)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 二、命名规范
|
|
75
|
+
|
|
76
|
+
### 2.1 包名
|
|
77
|
+
|
|
78
|
+
全部使用小写字母,单词之间用小数点分隔,禁止使用下划线、大写字母。
|
|
79
|
+
|
|
80
|
+
格式:`cn.ultracloud.com.module.{module}.{layer}.{domain}`
|
|
81
|
+
|
|
82
|
+
示例:
|
|
83
|
+
- `cn.ultracloud.com.module.wms.controller.admin.warehouse`
|
|
84
|
+
- `cn.ultracloud.com.module.wms.dal.dataobject`
|
|
85
|
+
- `cn.ultracloud.com.module.wms.service.warehouse`
|
|
86
|
+
- `cn.ultracloud.com.module.wms.dal.mysql`
|
|
87
|
+
- `cn.ultracloud.com.module.wms.enums`
|
|
88
|
+
|
|
89
|
+
### 2.2 类名
|
|
90
|
+
|
|
91
|
+
采用大驼峰命名法,望文知意,后缀明确:
|
|
92
|
+
|
|
93
|
+
| 类型 | 后缀 | 示例 |
|
|
94
|
+
| ---------------- | -------------------- | -------------------------------------------- |
|
|
95
|
+
| Controller | `Controller` | `WmsWarehouseController` |
|
|
96
|
+
| Service 接口 | `Service` | `WmsWarehouseService`(不加 `I` 前缀) |
|
|
97
|
+
| Service 实现 | `ServiceImpl` | `WmsWarehouseServiceImpl` |
|
|
98
|
+
| Mapper | `Mapper` | `WmsWarehouseMapper` |
|
|
99
|
+
| DO 实体 | `DO` | `WmsWarehouseDO`(继承 BaseDO/TenantBaseDO) |
|
|
100
|
+
| 创建/更新请求 | `SaveReqVO` | `WarehouseSaveReqVO` |
|
|
101
|
+
| 分页查询请求 | `PageReqVO` | `WarehousePageReqVO` |
|
|
102
|
+
| 列表查询请求 | `ListReqVO` | `DeptListReqVO` |
|
|
103
|
+
| 响应对象 | `RespVO` | `WarehouseRespVO` |
|
|
104
|
+
| 精简响应 | `SimpleRespVO` | `WarehouseSimpleRespVO` |
|
|
105
|
+
| RPC DTO | `ReqDTO` / `RespDTO` | `AdminUserRespDTO`(位于 -api 模块) |
|
|
106
|
+
| 枚举 | `Enum` | `WmsOrgStatusEnum` |
|
|
107
|
+
| MapStruct 转换器 | `Convert` | `ConfigConvert` |
|
|
108
|
+
|
|
109
|
+
### 2.3 方法名
|
|
110
|
+
|
|
111
|
+
采用小驼峰命名法,以动词开头:
|
|
112
|
+
|
|
113
|
+
| 操作 | 命名 | 示例 |
|
|
114
|
+
| -------- | ------------------------------------------------ | ------------------------------------------------ |
|
|
115
|
+
| 创建 | `create{Entity}` | `createWarehouse` |
|
|
116
|
+
| 更新 | `update{Entity}` | `updateWarehouse` |
|
|
117
|
+
| 删除 | `delete{Entity}` | `deleteWarehouse` |
|
|
118
|
+
| 批量删除 | `delete{Entity}List` / `delete{Entity}ListByIds` | `deleteWarehouseListByIds` |
|
|
119
|
+
| 获得单个 | `get{Entity}` | `getWarehouse` |
|
|
120
|
+
| 分页查询 | `get{Entity}Page` | `getWarehousePage` |
|
|
121
|
+
| 列表查询 | `get{Entity}List` | `getSimpleList` |
|
|
122
|
+
| 状态修改 | `{action}{Entity}` | `enableWarehouse` / `disableWarehouse` |
|
|
123
|
+
| 校验方法 | `validate{Condition}` | `validateWarehouseExists` / `validateCodeUnique` |
|
|
124
|
+
|
|
125
|
+
### 2.4 变量名
|
|
126
|
+
|
|
127
|
+
采用小驼峰命名法,语义清晰,禁止拼音、单字母、无意义缩写。
|
|
128
|
+
|
|
129
|
+
- 正确:`warehouseCode`、`createTime`、`orgId`
|
|
130
|
+
- 错误:`a`、`abc`、`ckbm`、`czsj`
|
|
131
|
+
|
|
132
|
+
### 2.5 常量名
|
|
133
|
+
|
|
134
|
+
全部大写,单词之间用下划线分隔,集中定义在 `ErrorCodeConstants` 或常量类中,禁止魔法值。
|
|
135
|
+
|
|
136
|
+
示例:`WAREHOUSE_NOT_EXISTS`、`MAX_PAGE_SIZE`
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 三、API 接口规范
|
|
141
|
+
|
|
142
|
+
### 3.1 请求路径
|
|
143
|
+
|
|
144
|
+
项目采用**路径动词风格**,不依赖 HTTP method 区分操作:
|
|
145
|
+
|
|
146
|
+
| 操作 | 路径 | HTTP Method |
|
|
147
|
+
| -------- | ----------------------------- | ----------- |
|
|
148
|
+
| 创建 | `/wms/warehouse/create` | POST |
|
|
149
|
+
| 更新 | `/wms/warehouse/update` | PUT |
|
|
150
|
+
| 删除 | `/wms/warehouse/delete` | DELETE |
|
|
151
|
+
| 批量删除 | `/wms/warehouse/delete-list` | DELETE |
|
|
152
|
+
| 获得单个 | `/wms/warehouse/get` | GET |
|
|
153
|
+
| 分页查询 | `/wms/warehouse/page` | GET |
|
|
154
|
+
| 列表查询 | `/wms/warehouse/list` | GET |
|
|
155
|
+
| 导出 | `/wms/warehouse/export-excel` | GET |
|
|
156
|
+
| 其他操作 | `/wms/warehouse/{action}` | PUT/GET |
|
|
157
|
+
|
|
158
|
+
路径规则:
|
|
159
|
+
- 全部使用小写字母,单词之间用中横线分隔
|
|
160
|
+
- `@RequestMapping("/wms/warehouse")` 不包含 `/admin-api` 或 `/app-api` 前缀
|
|
161
|
+
- `controller/admin/` 下的类由框架自动添加 `/admin-api` 前缀
|
|
162
|
+
- `controller/app/` 下的类自动添加 `/app-api` 前缀
|
|
163
|
+
- Gateway 负责路由转发
|
|
164
|
+
|
|
165
|
+
### 3.2 接口返回格式
|
|
166
|
+
|
|
167
|
+
所有接口返回统一使用 `CommonResult<T>`:
|
|
168
|
+
|
|
169
|
+
```java
|
|
170
|
+
// 成功
|
|
171
|
+
return success(data); // CommonResult<Long>
|
|
172
|
+
return success(true); // CommonResult<Boolean>
|
|
173
|
+
return success(BeanUtils.toBean(warehouse, WarehouseRespVO.class)); // CommonResult<WarehouseRespVO>
|
|
174
|
+
return success(BeanUtils.toBean(pageResult, WarehouseRespVO.class)); // CommonResult<PageResult<WarehouseRespVO>>
|
|
175
|
+
|
|
176
|
+
// 失败(通过框架全局异常处理自动返回)
|
|
177
|
+
throw exception(WAREHOUSE_NOT_EXISTS); // CommonResult.error(code, msg)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`CommonResult` 结构:
|
|
181
|
+
- `code`(Integer)— 状态码,使用 `ErrorCode` 对象
|
|
182
|
+
- `msg`(String)— 提示信息
|
|
183
|
+
- `data`(T)— 业务数据
|
|
184
|
+
|
|
185
|
+
### 3.3 错误码体系
|
|
186
|
+
|
|
187
|
+
错误码定义在 `-api` 模块的 `ErrorCodeConstants` **接口**中:
|
|
188
|
+
|
|
189
|
+
```java
|
|
190
|
+
package cn.ultracloud.com.module.wms.enums;
|
|
191
|
+
|
|
192
|
+
import cn.ultracloud.com.framework.common.exception.ErrorCode;
|
|
193
|
+
|
|
194
|
+
public interface ErrorCodeConstants {
|
|
195
|
+
|
|
196
|
+
// ========== 仓库 1-060-003-000 ==========
|
|
197
|
+
ErrorCode WAREHOUSE_NOT_EXISTS = new ErrorCode(1_060_003_000, "仓库不存在");
|
|
198
|
+
ErrorCode WAREHOUSE_CODE_DUPLICATE = new ErrorCode(1_060_003_001, "仓库编码已存在");
|
|
199
|
+
ErrorCode WAREHOUSE_HAS_ZONES = new ErrorCode(1_060_003_002, "仓库下存在库区,不允许删除");
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
格式:`1_XXX_YYY_ZZZ`(Java 下划线数字字面量)
|
|
204
|
+
- `1` = 业务模块前缀
|
|
205
|
+
- `XXX` = 模块编号(Infra=001, System=002, BPM=003, AI=040,新增模块按序分配)
|
|
206
|
+
- `YYY` = 子模块编号
|
|
207
|
+
- `ZZZ` = 子模块内序号
|
|
208
|
+
|
|
209
|
+
错误消息支持 `{}` 占位符:
|
|
210
|
+
```java
|
|
211
|
+
ErrorCode DEPT_ORG_CODE_DUPLICATE = new ErrorCode(1_002_004_008, "已经存在编码为【{}】的组织");
|
|
212
|
+
// 使用:throw exception(DEPT_ORG_CODE_DUPLICATE, orgCode);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### 3.4 权限控制
|
|
216
|
+
|
|
217
|
+
使用 `@PreAuthorize("@ss.hasPermission('module:resource:action')")`:
|
|
218
|
+
|
|
219
|
+
```java
|
|
220
|
+
@PreAuthorize("@ss.hasPermission('wms:warehouse:create')") // 创建
|
|
221
|
+
@PreAuthorize("@ss.hasPermission('wms:warehouse:update')") // 修改
|
|
222
|
+
@PreAuthorize("@ss.hasPermission('wms:warehouse:delete')") // 删除
|
|
223
|
+
@PreAuthorize("@ss.hasPermission('wms:warehouse:query')") // 查询
|
|
224
|
+
@PreAuthorize("@ss.hasPermission('wms:warehouse:export')") // 导出
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### 3.5 API 文档注解
|
|
228
|
+
|
|
229
|
+
使用 SpringDoc(OpenAPI 3)注解:
|
|
230
|
+
|
|
231
|
+
```java
|
|
232
|
+
// Controller 类级别
|
|
233
|
+
@Tag(name = "管理后台 - WMS 仓库")
|
|
234
|
+
|
|
235
|
+
// 方法级别
|
|
236
|
+
@Operation(summary = "创建仓库")
|
|
237
|
+
|
|
238
|
+
// 参数级别
|
|
239
|
+
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
|
240
|
+
|
|
241
|
+
// VO 类级别
|
|
242
|
+
@Schema(description = "管理后台 - WMS 仓库新增/修改 Request VO")
|
|
243
|
+
|
|
244
|
+
// VO 字段级别
|
|
245
|
+
@Schema(description = "仓库编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "WH001")
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## 四、Controller 层规范
|
|
251
|
+
|
|
252
|
+
### 4.1 标准写法
|
|
253
|
+
|
|
254
|
+
```java
|
|
255
|
+
package cn.ultracloud.com.module.wms.controller.admin.warehouse;
|
|
256
|
+
|
|
257
|
+
import cn.ultracloud.com.framework.common.pojo.CommonResult;
|
|
258
|
+
import cn.ultracloud.com.framework.common.pojo.PageResult;
|
|
259
|
+
import cn.ultracloud.com.framework.common.util.object.BeanUtils;
|
|
260
|
+
import cn.ultracloud.com.module.wms.controller.admin.vo.warehouse.WarehousePageReqVO;
|
|
261
|
+
import cn.ultracloud.com.module.wms.controller.admin.vo.warehouse.WarehouseRespVO;
|
|
262
|
+
import cn.ultracloud.com.module.wms.controller.admin.vo.warehouse.WarehouseSaveReqVO;
|
|
263
|
+
import cn.ultracloud.com.module.wms.dal.dataobject.WmsWarehouseDO;
|
|
264
|
+
import cn.ultracloud.com.module.wms.service.warehouse.WmsWarehouseService;
|
|
265
|
+
import io.swagger.v3.oas.annotations.Operation;
|
|
266
|
+
import io.swagger.v3.oas.annotations.Parameter;
|
|
267
|
+
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
268
|
+
import jakarta.annotation.Resource;
|
|
269
|
+
import jakarta.validation.Valid;
|
|
270
|
+
import org.springframework.security.access.prepost.PreAuthorize;
|
|
271
|
+
import org.springframework.validation.annotation.Validated;
|
|
272
|
+
import org.springframework.web.bind.annotation.*;
|
|
273
|
+
|
|
274
|
+
import static cn.ultracloud.com.framework.common.pojo.CommonResult.success;
|
|
275
|
+
|
|
276
|
+
@Tag(name = "管理后台 - WMS 仓库")
|
|
277
|
+
@RestController
|
|
278
|
+
@RequestMapping("/wms/warehouse")
|
|
279
|
+
@Validated
|
|
280
|
+
public class WmsWarehouseController {
|
|
281
|
+
|
|
282
|
+
@Resource
|
|
283
|
+
private WmsWarehouseService warehouseService;
|
|
284
|
+
|
|
285
|
+
@PostMapping("/create")
|
|
286
|
+
@Operation(summary = "创建仓库")
|
|
287
|
+
@PreAuthorize("@ss.hasPermission('wms:warehouse:create')")
|
|
288
|
+
public CommonResult<Long> createWarehouse(@Valid @RequestBody WarehouseSaveReqVO createReqVO) {
|
|
289
|
+
return success(warehouseService.createWarehouse(createReqVO));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
@PutMapping("/update")
|
|
293
|
+
@Operation(summary = "更新仓库")
|
|
294
|
+
@PreAuthorize("@ss.hasPermission('wms:warehouse:update')")
|
|
295
|
+
public CommonResult<Boolean> updateWarehouse(@Valid @RequestBody WarehouseSaveReqVO updateReqVO) {
|
|
296
|
+
warehouseService.updateWarehouse(updateReqVO);
|
|
297
|
+
return success(true);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
@DeleteMapping("/delete")
|
|
301
|
+
@Operation(summary = "删除仓库")
|
|
302
|
+
@Parameter(name = "id", description = "编号", required = true)
|
|
303
|
+
@PreAuthorize("@ss.hasPermission('wms:warehouse:delete')")
|
|
304
|
+
public CommonResult<Boolean> deleteWarehouse(@RequestParam("id") Long id) {
|
|
305
|
+
warehouseService.deleteWarehouse(id);
|
|
306
|
+
return success(true);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
@GetMapping("/get")
|
|
310
|
+
@Operation(summary = "获得仓库")
|
|
311
|
+
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
|
312
|
+
@PreAuthorize("@ss.hasPermission('wms:warehouse:query')")
|
|
313
|
+
public CommonResult<WarehouseRespVO> getWarehouse(@RequestParam("id") Long id) {
|
|
314
|
+
WmsWarehouseDO warehouse = warehouseService.getWarehouse(id);
|
|
315
|
+
return success(BeanUtils.toBean(warehouse, WarehouseRespVO.class));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
@GetMapping("/page")
|
|
319
|
+
@Operation(summary = "获得仓库分页")
|
|
320
|
+
@PreAuthorize("@ss.hasPermission('wms:warehouse:query')")
|
|
321
|
+
public CommonResult<PageResult<WarehouseRespVO>> getWarehousePage(@Valid WarehousePageReqVO pageReqVO) {
|
|
322
|
+
PageResult<WmsWarehouseDO> pageResult = warehouseService.getWarehousePage(pageReqVO);
|
|
323
|
+
return success(BeanUtils.toBean(pageResult, WarehouseRespVO.class));
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### 4.2 禁止事项
|
|
329
|
+
|
|
330
|
+
1. 禁止在 Controller 层编写任何业务逻辑(参数处理、数据转换、数据库操作)
|
|
331
|
+
2. 禁止直接返回 `Map`、`Object`、`List` 等未封装对象,必须使用 `CommonResult`
|
|
332
|
+
3. 禁止不做参数校验(POST/PUT 必须加 `@Valid @RequestBody`,GET 加 `@Valid`)
|
|
333
|
+
4. 禁止使用 `@Autowired` 注入,统一使用 `@Resource`
|
|
334
|
+
5. 禁止接口路径包含 `/admin-api` 或 `/app-api` 前缀(由框架自动添加)
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## 五、Service 层规范
|
|
339
|
+
|
|
340
|
+
### 5.1 接口与实现分离
|
|
341
|
+
|
|
342
|
+
```java
|
|
343
|
+
// Service 接口(不加注解)
|
|
344
|
+
public interface WmsWarehouseService {
|
|
345
|
+
|
|
346
|
+
Long createWarehouse(WarehouseSaveReqVO createReqVO);
|
|
347
|
+
|
|
348
|
+
void updateWarehouse(WarehouseSaveReqVO updateReqVO);
|
|
349
|
+
|
|
350
|
+
void deleteWarehouse(Long id);
|
|
351
|
+
|
|
352
|
+
WmsWarehouseDO getWarehouse(Long id);
|
|
353
|
+
|
|
354
|
+
PageResult<WmsWarehouseDO> getWarehousePage(WarehousePageReqVO pageReqVO);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Service 实现类
|
|
358
|
+
@Service
|
|
359
|
+
@Validated
|
|
360
|
+
@Slf4j
|
|
361
|
+
public class WmsWarehouseServiceImpl implements WmsWarehouseService {
|
|
362
|
+
|
|
363
|
+
@Resource
|
|
364
|
+
private WmsWarehouseMapper warehouseMapper;
|
|
365
|
+
|
|
366
|
+
@Override
|
|
367
|
+
public Long createWarehouse(WarehouseSaveReqVO createReqVO) {
|
|
368
|
+
validateCodeUnique(null, createReqVO.getCode());
|
|
369
|
+
WmsWarehouseDO warehouse = BeanUtils.toBean(createReqVO, WmsWarehouseDO.class);
|
|
370
|
+
warehouseMapper.insert(warehouse);
|
|
371
|
+
return warehouse.getId();
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
@Override
|
|
375
|
+
public void updateWarehouse(WarehouseSaveReqVO updateReqVO) {
|
|
376
|
+
validateExists(updateReqVO.getId());
|
|
377
|
+
validateCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
|
|
378
|
+
WmsWarehouseDO updateObj = BeanUtils.toBean(updateReqVO, WmsWarehouseDO.class);
|
|
379
|
+
warehouseMapper.updateById(updateObj);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
@Override
|
|
383
|
+
public void deleteWarehouse(Long id) {
|
|
384
|
+
WmsWarehouseDO warehouse = validateExists(id);
|
|
385
|
+
if (!WmsOrgStatusEnum.PENDING.getStatus().equals(warehouse.getStatus())) {
|
|
386
|
+
throw exception(WAREHOUSE_STATUS_INVALID);
|
|
387
|
+
}
|
|
388
|
+
warehouseMapper.deleteById(id);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
@Override
|
|
392
|
+
public WmsWarehouseDO getWarehouse(Long id) {
|
|
393
|
+
return warehouseMapper.selectById(id);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
@Override
|
|
397
|
+
public PageResult<WmsWarehouseDO> getWarehousePage(WarehousePageReqVO pageReqVO) {
|
|
398
|
+
return warehouseMapper.selectPage(pageReqVO, new LambdaQueryWrapperX<WmsWarehouseDO>()
|
|
399
|
+
.likeIfPresent(WmsWarehouseDO::getCode, pageReqVO.getCode())
|
|
400
|
+
.likeIfPresent(WmsWarehouseDO::getName, pageReqVO.getName())
|
|
401
|
+
.eqIfPresent(WmsWarehouseDO::getOrgId, pageReqVO.getOrgId())
|
|
402
|
+
.orderByDesc(WmsWarehouseDO::getId));
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ====== 私有校验方法 ======
|
|
406
|
+
|
|
407
|
+
private WmsWarehouseDO validateExists(Long id) {
|
|
408
|
+
WmsWarehouseDO warehouse = warehouseMapper.selectById(id);
|
|
409
|
+
if (warehouse == null) {
|
|
410
|
+
throw exception(WAREHOUSE_NOT_EXISTS);
|
|
411
|
+
}
|
|
412
|
+
return warehouse;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
private void validateCodeUnique(Long id, String code) {
|
|
416
|
+
WmsWarehouseDO warehouse = warehouseMapper.selectOne(WmsWarehouseDO::getCode, code);
|
|
417
|
+
if (warehouse == null) return;
|
|
418
|
+
if (id == null || !id.equals(warehouse.getId())) {
|
|
419
|
+
throw exception(WAREHOUSE_CODE_DUPLICATE);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
**关键约定**:
|
|
426
|
+
- Service 方法直接接收 `SaveReqVO`/`PageReqVO`,返回 `DO` 对象(不是 RespVO)
|
|
427
|
+
- Controller 层负责 DO → RespVO 转换
|
|
428
|
+
- 接口无需注解,实现类加 `@Service` + `@Validated` + `@Slf4j`
|
|
429
|
+
- 私有校验方法使用 `throw exception(ErrorCodeConstants.XXX)` 抛出异常
|
|
430
|
+
|
|
431
|
+
### 5.2 事务管理
|
|
432
|
+
|
|
433
|
+
- 涉及多表操作的方法加 `@Transactional(rollbackFor = Exception.class)`
|
|
434
|
+
- 单表 CRUD 不需要事务注解
|
|
435
|
+
- 主子表场景下代码生成器自动添加事务注解
|
|
436
|
+
|
|
437
|
+
### 5.3 禁止事项
|
|
438
|
+
|
|
439
|
+
1. 禁止 Service 层直接接收 HTTP 请求、返回响应
|
|
440
|
+
2. 禁止 Service 层跳过 Mapper 层直接编写 SQL 操作数据库
|
|
441
|
+
3. 禁止未加事务注解的多表修改操作
|
|
442
|
+
4. 禁止手动设置审计字段(createTime/updateTime 等由框架自动填充)
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
## 六、Mapper/数据库规范
|
|
447
|
+
|
|
448
|
+
### 6.1 Mapper 接口
|
|
449
|
+
|
|
450
|
+
```java
|
|
451
|
+
package cn.ultracloud.com.module.wms.dal.mysql;
|
|
452
|
+
|
|
453
|
+
import cn.ultracloud.com.framework.mybatis.core.mapper.BaseMapperX;
|
|
454
|
+
import cn.ultracloud.com.module.wms.dal.dataobject.WmsWarehouseDO;
|
|
455
|
+
import org.apache.ibatis.annotations.Mapper;
|
|
456
|
+
|
|
457
|
+
import java.util.List;
|
|
458
|
+
|
|
459
|
+
@Mapper
|
|
460
|
+
public interface WmsWarehouseMapper extends BaseMapperX<WmsWarehouseDO> {
|
|
461
|
+
|
|
462
|
+
default List<WmsWarehouseDO> selectListByOrgId(Long orgId) {
|
|
463
|
+
return selectList(WmsWarehouseDO::getOrgId, orgId);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
default Long selectCountByOrgId(Long orgId) {
|
|
467
|
+
return selectCount(WmsWarehouseDO::getOrgId, orgId);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
**关键约定**:
|
|
473
|
+
- 继承 `BaseMapperX<DO>`(扩展自 `MPJBaseMapper<T>`,支持 MyBatis Plus Join)
|
|
474
|
+
- 接口加 `@Mapper` 注解
|
|
475
|
+
- 位于 `dal/mysql/{domain}/` 目录
|
|
476
|
+
- 自定义方法使用 `default` 方法 + `LambdaQueryWrapperX` 链式 API
|
|
477
|
+
|
|
478
|
+
### 6.2 LambdaQueryWrapperX 链式 API
|
|
479
|
+
|
|
480
|
+
优先使用 `LambdaQueryWrapperX`,不手写 SQL:
|
|
481
|
+
|
|
482
|
+
```java
|
|
483
|
+
default PageResult<ConfigDO> selectPage(ConfigPageReqVO reqVO) {
|
|
484
|
+
return selectPage(reqVO, new LambdaQueryWrapperX<ConfigDO>()
|
|
485
|
+
.likeIfPresent(ConfigDO::getConfigName, reqVO.getConfigName())
|
|
486
|
+
.eqIfPresent(ConfigDO::getConfigKey, reqVO.getConfigKey())
|
|
487
|
+
.eqIfPresent(ConfigDO::getType, reqVO.getType())
|
|
488
|
+
.betweenIfPresent(ConfigDO::getCreateTime, reqVO.getCreateTime())
|
|
489
|
+
.orderByDesc(ConfigDO::getId));
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
常用条件方法:
|
|
494
|
+
|
|
495
|
+
| 方法 | 说明 |
|
|
496
|
+
| ------------------------------------------------------- | ---------------------------- |
|
|
497
|
+
| `likeIfPresent(column, value)` | 模糊匹配(值非空时才加条件) |
|
|
498
|
+
| `eqIfPresent(column, value)` | 精确匹配 |
|
|
499
|
+
| `betweenIfPresent(column, val1, val2)` | 区间匹配 |
|
|
500
|
+
| `inIfPresent(column, collection)` | IN 匹配 |
|
|
501
|
+
| `neIfPresent(column, value)` | 不等匹配 |
|
|
502
|
+
| `gtIfPresent / geIfPresent / ltIfPresent / leIfPresent` | 大小写比较 |
|
|
503
|
+
| `orderByDesc(column)` | 降序排序 |
|
|
504
|
+
| `orderByAsc(column)` | 升序排序 |
|
|
505
|
+
|
|
506
|
+
### 6.3 BaseMapperX 内置方法
|
|
507
|
+
|
|
508
|
+
| 方法 | 说明 |
|
|
509
|
+
| ------------------------------------------- | ---------------------------------------- |
|
|
510
|
+
| `selectOne(field, value)` | 单字段精确查询,返回一条 |
|
|
511
|
+
| `selectOne(field1, value1, field2, value2)` | 多字段精确查询 |
|
|
512
|
+
| `selectCount(field, value)` | 计数查询 |
|
|
513
|
+
| `selectList(field, value)` | 单字段列表查询 |
|
|
514
|
+
| `selectList(Wrapper)` | Wrapper 条件查询 |
|
|
515
|
+
| `selectPage(PageParam, Wrapper)` | 分页查询,支持 `PAGE_SIZE_NONE` 返回全部 |
|
|
516
|
+
| `insert(entity)` | 插入单条 |
|
|
517
|
+
| `insertBatch(Collection)` | 批量插入 |
|
|
518
|
+
| `updateById(entity)` | 根据 ID 更新 |
|
|
519
|
+
| `updateBatch(Collection)` | 批量更新 |
|
|
520
|
+
| `deleteById(id)` | 根据 ID 删除 |
|
|
521
|
+
| `deleteBatch(Collection)` | 批量删除 |
|
|
522
|
+
|
|
523
|
+
### 6.4 DO 实体
|
|
524
|
+
|
|
525
|
+
```java
|
|
526
|
+
package cn.ultracloud.com.module.wms.dal.dataobject;
|
|
527
|
+
|
|
528
|
+
import cn.ultracloud.com.framework.tenant.core.db.TenantBaseDO;
|
|
529
|
+
import com.baomidou.mybatisplus.annotation.KeySequence;
|
|
530
|
+
import com.baomidou.mybatisplus.annotation.TableId;
|
|
531
|
+
import com.baomidou.mybatisplus.annotation.TableName;
|
|
532
|
+
import lombok.*;
|
|
533
|
+
|
|
534
|
+
@TableName("wms_warehouse")
|
|
535
|
+
@KeySequence("wms_warehouse_seq")
|
|
536
|
+
@Data
|
|
537
|
+
@EqualsAndHashCode(callSuper = true)
|
|
538
|
+
@ToString(callSuper = true)
|
|
539
|
+
@Builder
|
|
540
|
+
@NoArgsConstructor
|
|
541
|
+
@AllArgsConstructor
|
|
542
|
+
public class WmsWarehouseDO extends TenantBaseDO {
|
|
543
|
+
|
|
544
|
+
@TableId
|
|
545
|
+
private Long id;
|
|
546
|
+
private String code;
|
|
547
|
+
private String name;
|
|
548
|
+
private Long orgId;
|
|
549
|
+
// 其他字段... 不需要 @TableField,MyBatis Plus 自动驼峰转下划线
|
|
550
|
+
}
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
**关键约定**:
|
|
554
|
+
- 命名:`{Entity}DO`,位于 `dal/dataobject/{domain}/` 目录
|
|
555
|
+
- 基类:`BaseDO`(标准)或 `TenantBaseDO`(多租户)
|
|
556
|
+
- 字段默认使用 camelCase,MyBatis Plus 自动映射为 snake_case,**不需要 `@TableField`**(唯一例外:JSONB 字段需加 `@TableField(typeHandler = JsonbTypeHandler.class)`,见 §6.9)
|
|
557
|
+
- 审计字段(createTime/updateTime/creator/updater/deleted)由 `BaseDO` 基类提供
|
|
558
|
+
- 多租户字段(tenantId)由 `TenantBaseDO` 基类提供,需用引用`
|
|
559
|
+
import cn.ultracloud.com.framework.tenant.core.db.TenantBaseDO;`
|
|
560
|
+
|
|
561
|
+
### 6.5 BaseDO 提供的字段
|
|
562
|
+
|
|
563
|
+
| 字段 | 类型 | 说明 |
|
|
564
|
+
| ------------ | --------------- | -------------------------------------------- |
|
|
565
|
+
| `createTime` | `LocalDateTime` | `@TableField(fill = INSERT)` 自动填充 |
|
|
566
|
+
| `updateTime` | `LocalDateTime` | `@TableField(fill = INSERT_UPDATE)` 自动填充 |
|
|
567
|
+
| `creator` | `String` | `@TableField(fill = INSERT)` 自动填充 |
|
|
568
|
+
| `updater` | `String` | `@TableField(fill = INSERT_UPDATE)` 自动填充 |
|
|
569
|
+
| `deleted` | `Boolean` | `@TableLogic` 逻辑删除 |
|
|
570
|
+
|
|
571
|
+
### 6.6 TenantBaseDO 提供的字段
|
|
572
|
+
|
|
573
|
+
`TenantBaseDO` 继承自 `BaseDO`,额外提供以下字段:
|
|
574
|
+
|
|
575
|
+
| 字段 | 类型 | 说明 |
|
|
576
|
+
| ---------- | ------ | ---------------------------------------------------------- |
|
|
577
|
+
| `tenantId` | `Long` | 租户编号,框架自动填充并过滤,跨租户查询加 `@TenantIgnore` |
|
|
578
|
+
|
|
579
|
+
需要多租户隔离的 DO 继承 `TenantBaseDO`,引入路径:
|
|
580
|
+
|
|
581
|
+
```java
|
|
582
|
+
import cn.ultracloud.com.framework.tenant.core.db.TenantBaseDO;
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
### 6.7 数据库表规范
|
|
586
|
+
|
|
587
|
+
- 表名:小写字母 + 下划线,语义清晰,如 `wms_warehouse`、`system_dept`
|
|
588
|
+
- 字段名:小写字母 + 下划线,与 DO 实体 camelCase 对应
|
|
589
|
+
- 主键:`id`(`BIGINT` 自增)
|
|
590
|
+
- 不需要手动定义 `create_time`、`update_time`、`is_deleted` 字段(由 BaseDO 管理)
|
|
591
|
+
|
|
592
|
+
### 6.8 SQL 规范
|
|
593
|
+
|
|
594
|
+
- 优先使用 `LambdaQueryWrapperX` 链式 API,不手写 SQL
|
|
595
|
+
- 复杂 SQL(JOIN、子查询、聚合)才使用 XML Mapper
|
|
596
|
+
- XML 文件位于 `src/main/resources/mapper/{domain}/` 目录
|
|
597
|
+
- 逻辑删除由 `@TableLogic` 自动处理,无需手动加 `WHERE deleted = 0`
|
|
598
|
+
|
|
599
|
+
### 6.9 JSONB 字段处理规范(PostgreSQL)
|
|
600
|
+
|
|
601
|
+
PostgreSQL 的 `jsonb` 列**不允许** `varchar → jsonb` 隐式转换。MyBatis-Plus 自带的 `JacksonTypeHandler` 通过 `PreparedStatement#setString` 写参,直接写 JSONB 列会抛:
|
|
602
|
+
|
|
603
|
+
```
|
|
604
|
+
column is of type jsonb but expression is of type character varying
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
因此项目使用**自研 `JsonbTypeHandler`**(而非 `JacksonTypeHandler`),通过 `PGobject` 显式声明 jdbc 类型为 `jsonb` 精确写入。
|
|
608
|
+
|
|
609
|
+
**规范**:
|
|
610
|
+
|
|
611
|
+
1. **DO 字段**:JSONB 列在 Java 侧映射为 `String`(JSON 文本),并加 `@TableField(typeHandler = JsonbTypeHandler.class)`:
|
|
612
|
+
|
|
613
|
+
```java
|
|
614
|
+
@TableName("code_rule")
|
|
615
|
+
public class CodeRuleDO extends TenantBaseDO {
|
|
616
|
+
|
|
617
|
+
/** 段位配置(JSON 文本,对应 jsonb 列 segment_config) */
|
|
618
|
+
@TableField(typeHandler = JsonbTypeHandler.class)
|
|
619
|
+
private String segmentConfig;
|
|
620
|
+
}
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
2. **类型处理器**:`JsonbTypeHandler extends BaseTypeHandler<String>`,位于模块内 `framework/mybatis/core/type/JsonbTypeHandler.java`,加 `@MappedJdbcTypes(JdbcType.OTHER)`:
|
|
624
|
+
|
|
625
|
+
```java
|
|
626
|
+
@MappedJdbcTypes(JdbcType.OTHER)
|
|
627
|
+
public class JsonbTypeHandler extends BaseTypeHandler<String> {
|
|
628
|
+
|
|
629
|
+
private static final String JSONB_TYPE = "jsonb";
|
|
630
|
+
private static final ThreadLocal<Boolean> IS_POSTGRESQL = new ThreadLocal<>();
|
|
631
|
+
|
|
632
|
+
@Override
|
|
633
|
+
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
|
|
634
|
+
throws SQLException {
|
|
635
|
+
if (isPostgresql(ps)) {
|
|
636
|
+
// PostgreSQL JSONB 列:必须通过 PGobject 显式声明 jsonb 类型,否则 setString 会失败
|
|
637
|
+
PGobject pgObject = new PGobject();
|
|
638
|
+
pgObject.setType(JSONB_TYPE);
|
|
639
|
+
pgObject.setValue(parameter);
|
|
640
|
+
ps.setObject(i, pgObject);
|
|
641
|
+
} else {
|
|
642
|
+
// H2 / MySQL 等:列为 TEXT / JSON,直接写字符串(单元测试环境)
|
|
643
|
+
ps.setString(i, parameter);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// getNullableResult x3 使用 rs.getString / cs.getString 读取即可
|
|
648
|
+
}
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
- 是否 PostgreSQL 用 `ThreadLocal<Boolean>` 缓存连接元数据(同一进程数据源固定),避免每次写入都查询 `DatabaseMetaData`
|
|
652
|
+
- 已有实现见 `ultracloud-module-ai`、`ultracloud-module-code` 等模块的 `framework/mybatis/core/type/JsonbTypeHandler.java`,新模块直接复制
|
|
653
|
+
|
|
654
|
+
3. **JSON 字段前后端契约**:VO/DO/RPC DTO 中的 JSON 字段统一为 `String`(JSON 文本),**不使用** `List<T>` 强类型 VO;前端 `Form.List` 编辑为数组,提交前 `JSON.stringify` 序列化,读取时 `JSON.parse` 还原:
|
|
655
|
+
|
|
656
|
+
```java
|
|
657
|
+
// SaveReqVO:JSON 字段声明为 String,用 @NotBlank 校验
|
|
658
|
+
@NotBlank(message = "段位配置不能为空")
|
|
659
|
+
private String segmentConfig;
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
> **禁止**:JSONB 字段使用 `JacksonTypeHandler` + `@TableName(autoResultMap = true)`(在 PostgreSQL 下会因 varchar→jsonb 隐式转换失败报错)。
|
|
663
|
+
|
|
664
|
+
---
|
|
665
|
+
|
|
666
|
+
## 七、VO 规范
|
|
667
|
+
|
|
668
|
+
### 7.1 VO 分类
|
|
669
|
+
|
|
670
|
+
| 类型 | 命名 | 用途 | 继承 | 校验 |
|
|
671
|
+
| ------------ | ---------------------- | ------------------------------------------- | ----------- | -------------------------- |
|
|
672
|
+
| SaveReqVO | `{Entity}SaveReqVO` | 创建/更新(id=null 为创建,非 null 为更新) | 无 | `@NotBlank`、`@NotNull` 等 |
|
|
673
|
+
| PageReqVO | `{Entity}PageReqVO` | 分页查询 | `PageParam` | 无 |
|
|
674
|
+
| ListReqVO | `{Entity}ListReqVO` | 列表查询(不分页) | 无 | 无 |
|
|
675
|
+
| RespVO | `{Entity}RespVO` | 响应展示 | 无 | 无 |
|
|
676
|
+
| SimpleRespVO | `{Entity}SimpleRespVO` | 精简响应(下拉列表) | 无 | 无 |
|
|
677
|
+
|
|
678
|
+
### 7.2 SaveReqVO 示例
|
|
679
|
+
|
|
680
|
+
```java
|
|
681
|
+
package cn.ultracloud.com.module.wms.controller.admin.vo.warehouse;
|
|
682
|
+
|
|
683
|
+
import io.swagger.v3.oas.annotations.media.Schema;
|
|
684
|
+
import jakarta.validation.constraints.NotBlank;
|
|
685
|
+
import jakarta.validation.constraints.NotNull;
|
|
686
|
+
import lombok.Data;
|
|
687
|
+
|
|
688
|
+
@Schema(description = "管理后台 - WMS 仓库新增/修改 Request VO")
|
|
689
|
+
@Data
|
|
690
|
+
public class WarehouseSaveReqVO {
|
|
691
|
+
|
|
692
|
+
@Schema(description = "仓库编号", example = "1024")
|
|
693
|
+
private Long id;
|
|
694
|
+
|
|
695
|
+
@Schema(description = "仓库编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "WH001")
|
|
696
|
+
@NotBlank(message = "仓库编码不能为空")
|
|
697
|
+
private String code;
|
|
698
|
+
|
|
699
|
+
@Schema(description = "仓库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "华东主仓")
|
|
700
|
+
@NotBlank(message = "仓库名称不能为空")
|
|
701
|
+
private String name;
|
|
702
|
+
|
|
703
|
+
@Schema(description = "所属库存组织编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
|
704
|
+
@NotNull(message = "所属库存组织不能为空")
|
|
705
|
+
private Long orgId;
|
|
706
|
+
}
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
### 7.3 PageReqVO 示例
|
|
710
|
+
|
|
711
|
+
```java
|
|
712
|
+
package cn.ultracloud.com.module.wms.controller.admin.vo.warehouse;
|
|
713
|
+
|
|
714
|
+
import cn.ultracloud.com.framework.common.pojo.PageParam;
|
|
715
|
+
import io.swagger.v3.oas.annotations.media.Schema;
|
|
716
|
+
import lombok.Data;
|
|
717
|
+
import org.springframework.format.annotation.DateTimeFormat;
|
|
718
|
+
import java.time.LocalDateTime;
|
|
719
|
+
|
|
720
|
+
import static cn.ultracloud.com.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
|
721
|
+
|
|
722
|
+
@Schema(description = "管理后台 - WMS 仓库分页 Request VO")
|
|
723
|
+
@Data
|
|
724
|
+
public class WarehousePageReqVO extends PageParam {
|
|
725
|
+
|
|
726
|
+
@Schema(description = "仓库编码", example = "WH001")
|
|
727
|
+
private String code;
|
|
728
|
+
|
|
729
|
+
@Schema(description = "仓库名称", example = "华东主仓")
|
|
730
|
+
private String name;
|
|
731
|
+
|
|
732
|
+
@Schema(description = "所属库存组织编号", example = "1")
|
|
733
|
+
private Long orgId;
|
|
734
|
+
|
|
735
|
+
@Schema(description = "创建时间")
|
|
736
|
+
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
|
737
|
+
private LocalDateTime[] createTime;
|
|
738
|
+
}
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
### 7.4 RespVO 示例
|
|
742
|
+
|
|
743
|
+
```java
|
|
744
|
+
package cn.ultracloud.com.module.wms.controller.admin.vo.warehouse;
|
|
745
|
+
|
|
746
|
+
import io.swagger.v3.oas.annotations.media.Schema;
|
|
747
|
+
import lombok.Data;
|
|
748
|
+
import java.time.LocalDateTime;
|
|
749
|
+
|
|
750
|
+
@Schema(description = "管理后台 - WMS 仓库 Response VO")
|
|
751
|
+
@Data
|
|
752
|
+
public class WarehouseRespVO {
|
|
753
|
+
|
|
754
|
+
@Schema(description = "仓库编号", example = "1024")
|
|
755
|
+
private Long id;
|
|
756
|
+
|
|
757
|
+
@Schema(description = "仓库编码", example = "WH001")
|
|
758
|
+
private String code;
|
|
759
|
+
|
|
760
|
+
@Schema(description = "仓库名称", example = "华东主仓")
|
|
761
|
+
private String name;
|
|
762
|
+
|
|
763
|
+
@Schema(description = "所属库存组织编号", example = "1")
|
|
764
|
+
private Long orgId;
|
|
765
|
+
|
|
766
|
+
@Schema(description = "状态", example = "1")
|
|
767
|
+
private Integer status;
|
|
768
|
+
|
|
769
|
+
@Schema(description = "创建时间", example = "2026-01-01 00:00:00")
|
|
770
|
+
private LocalDateTime createTime;
|
|
771
|
+
}
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
### 7.5 VO 位置
|
|
775
|
+
|
|
776
|
+
位于 `controller/admin/{domain}/vo/{domain}/` 下,按业务域组织子目录。
|
|
777
|
+
|
|
778
|
+
### 7.6 DTO 与 VO 的区分
|
|
779
|
+
|
|
780
|
+
- **VO**:前端接口使用,位于 `-server` 模块的 `controller/admin/{domain}/vo/`
|
|
781
|
+
- **DTO**:跨模块 RPC 调用使用,位于 `-api` 模块的 `dto/{domain}/`
|
|
782
|
+
- 前端接口不使用 DTO,跨模块调用不使用 VO
|
|
783
|
+
|
|
784
|
+
---
|
|
785
|
+
|
|
786
|
+
## 八、对象转换规范
|
|
787
|
+
|
|
788
|
+
### 8.1 BeanUtils.toBean()
|
|
789
|
+
|
|
790
|
+
项目统一使用 `BeanUtils.toBean()`(基于 Hutool `BeanUtil`):
|
|
791
|
+
|
|
792
|
+
```java
|
|
793
|
+
import static cn.ultracloud.com.framework.common.util.object.BeanUtils.toBean;
|
|
794
|
+
|
|
795
|
+
// 单对象转换
|
|
796
|
+
WmsWarehouseDO warehouse = warehouseMapper.selectById(id);
|
|
797
|
+
return success(toBean(warehouse, WarehouseRespVO.class));
|
|
798
|
+
|
|
799
|
+
// 分页转换(自动保留 total)
|
|
800
|
+
PageResult<WmsWarehouseDO> pageResult = warehouseMapper.selectPage(pageReqVO);
|
|
801
|
+
return success(toBean(pageResult, WarehouseRespVO.class));
|
|
802
|
+
|
|
803
|
+
// List 转换
|
|
804
|
+
List<WmsWarehouseDO> list = warehouseMapper.selectList(reqVO);
|
|
805
|
+
return success(toBean(list, WarehouseRespVO.class));
|
|
806
|
+
|
|
807
|
+
// 带回调的转换
|
|
808
|
+
return success(toBean(warehouse, WarehouseRespVO.class, vo -> {
|
|
809
|
+
vo.setExtraInfo("computed value");
|
|
810
|
+
}));
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
### 8.2 MapStruct 转换器
|
|
814
|
+
|
|
815
|
+
复杂映射使用 MapStruct,位于 `convert/` 目录:
|
|
816
|
+
|
|
817
|
+
```java
|
|
818
|
+
@Mapper
|
|
819
|
+
public interface ConfigConvert {
|
|
820
|
+
ConfigConvert INSTANCE = Mappers.getMapper(ConfigConvert.class);
|
|
821
|
+
|
|
822
|
+
ConfigDO convert(ConfigSaveReqVO bean);
|
|
823
|
+
ConfigRespVO convert(ConfigDO bean);
|
|
824
|
+
}
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
---
|
|
828
|
+
|
|
829
|
+
## 九、枚举规范
|
|
830
|
+
|
|
831
|
+
### 9.1 简单枚举
|
|
832
|
+
|
|
833
|
+
不需要参数校验的枚举,普通定义即可:
|
|
834
|
+
|
|
835
|
+
```java
|
|
836
|
+
@Getter
|
|
837
|
+
@AllArgsConstructor
|
|
838
|
+
public enum RoleTypeEnum {
|
|
839
|
+
SYSTEM(1),
|
|
840
|
+
CUSTOM(2);
|
|
841
|
+
private final Integer type;
|
|
842
|
+
}
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
### 9.2 ArrayValuable 枚举
|
|
846
|
+
|
|
847
|
+
需要用于 `@InEnum` 参数校验的枚举:
|
|
848
|
+
|
|
849
|
+
```java
|
|
850
|
+
package cn.ultracloud.com.module.wms.enums;
|
|
851
|
+
|
|
852
|
+
import cn.ultracloud.com.framework.common.core.ArrayValuable;
|
|
853
|
+
import lombok.AllArgsConstructor;
|
|
854
|
+
import lombok.Getter;
|
|
855
|
+
|
|
856
|
+
import java.util.Arrays;
|
|
857
|
+
|
|
858
|
+
@Getter
|
|
859
|
+
@AllArgsConstructor
|
|
860
|
+
public enum WmsOrgStatusEnum implements ArrayValuable<Integer> {
|
|
861
|
+
|
|
862
|
+
PENDING(0, "待启用"),
|
|
863
|
+
ENABLED(1, "已启用"),
|
|
864
|
+
DISABLED(2, "已停用");
|
|
865
|
+
|
|
866
|
+
public static final Integer[] ARRAYS = Arrays.stream(values())
|
|
867
|
+
.map(WmsOrgStatusEnum::getStatus).toArray(Integer[]::new);
|
|
868
|
+
|
|
869
|
+
private final Integer status;
|
|
870
|
+
private final String name;
|
|
871
|
+
|
|
872
|
+
@Override
|
|
873
|
+
public Integer[] array() {
|
|
874
|
+
return ARRAYS;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
### 9.3 @InEnum 校验
|
|
880
|
+
|
|
881
|
+
在 VO 字段上使用 `@InEnum` 进行枚举值校验:
|
|
882
|
+
|
|
883
|
+
```java
|
|
884
|
+
@InEnum(WmsOrgStatusEnum.class)
|
|
885
|
+
@Schema(description = "状态", example = "1")
|
|
886
|
+
private Integer status;
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
枚举位于 `-api` 模块的 `enums/` 目录下。
|
|
890
|
+
|
|
891
|
+
---
|
|
892
|
+
|
|
893
|
+
## 十、参数校验规范
|
|
894
|
+
|
|
895
|
+
### 10.1 Controller 校验
|
|
896
|
+
|
|
897
|
+
- Controller 类加 `@Validated` 注解
|
|
898
|
+
- POST/PUT 方法参数加 `@Valid @RequestBody`
|
|
899
|
+
- GET 方法参数加 `@Valid`(不加 @RequestBody)
|
|
900
|
+
|
|
901
|
+
### 10.2 常用校验注解
|
|
902
|
+
|
|
903
|
+
来自 `jakarta.validation.constraints`:
|
|
904
|
+
|
|
905
|
+
| 注解 | 说明 |
|
|
906
|
+
| -------------------------- | ------------------------------------------------ |
|
|
907
|
+
| `@NotBlank` | 字符串非空(禁止空白字符) |
|
|
908
|
+
| `@NotNull` | 对象非空(用于 Integer、Long、LocalDateTime 等) |
|
|
909
|
+
| `@NotEmpty` | 字符串/集合非空 |
|
|
910
|
+
| `@Size(min = N, max = M)` | 长度限制 |
|
|
911
|
+
| `@Email` | 邮箱格式 |
|
|
912
|
+
| `@Pattern(regexp = "...")` | 正则校验 |
|
|
913
|
+
| `@InEnum(XxxEnum.class)` | 枚举值校验 |
|
|
914
|
+
|
|
915
|
+
### 10.3 时间参数
|
|
916
|
+
|
|
917
|
+
项目统一使用 `LocalDateTime`,JSON 序列化由框架全局配置处理,**无需在字段上加 `@JsonFormat`**。
|
|
918
|
+
|
|
919
|
+
PageReqVO 中的时间区间查询:
|
|
920
|
+
```java
|
|
921
|
+
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
|
922
|
+
private LocalDateTime[] createTime;
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
---
|
|
926
|
+
|
|
927
|
+
## 十一、异常规范
|
|
928
|
+
|
|
929
|
+
### 11.1 全局异常处理
|
|
930
|
+
|
|
931
|
+
全局异常处理由 `ultracloud-spring-boot-starter-web` 的 `GlobalExceptionHandler` 统一提供(`@RestControllerAdvice`),业务模块**无需手写**。
|
|
932
|
+
|
|
933
|
+
框架已处理的异常:
|
|
934
|
+
- 参数校验异常(`MethodArgumentNotValidException`、`BindException` 等)→ 400
|
|
935
|
+
- 业务异常(`ServiceException`)→ 返回 `CommonResult.error(code, msg)`
|
|
936
|
+
- 权限异常(`AccessDeniedException`)→ 403
|
|
937
|
+
- 资源不存在(`NoHandlerFoundException`)→ 404
|
|
938
|
+
- 兜底异常(`Exception`)→ 500,自动记录错误日志
|
|
939
|
+
|
|
940
|
+
### 11.2 业务异常抛出
|
|
941
|
+
|
|
942
|
+
使用 `ServiceExceptionUtil.exception()` 通过静态导入抛出:
|
|
943
|
+
|
|
944
|
+
```java
|
|
945
|
+
import static cn.ultracloud.com.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
946
|
+
import static cn.ultracloud.com.module.wms.enums.ErrorCodeConstants.*;
|
|
947
|
+
|
|
948
|
+
// 简单抛出
|
|
949
|
+
throw exception(WAREHOUSE_NOT_EXISTS);
|
|
950
|
+
|
|
951
|
+
// 带占位符参数
|
|
952
|
+
throw exception(WAREHOUSE_CODE_DUPLICATE, code);
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
### 11.3 异常分类
|
|
956
|
+
|
|
957
|
+
| 类型 | 处理方式 |
|
|
958
|
+
| ------------ | ----------------------------------------------- |
|
|
959
|
+
| 业务异常 | `throw exception(ErrorCodeConstants.XXX)` |
|
|
960
|
+
| 参数校验异常 | `@Valid` + `@Validated`,框架自动捕获并返回 400 |
|
|
961
|
+
| 系统异常 | 框架兜底返回 500,自动记录错误日志 |
|
|
962
|
+
|
|
963
|
+
### 11.4 禁止事项
|
|
964
|
+
|
|
965
|
+
1. 禁止手写 `GlobalExceptionHandler`(框架已提供)
|
|
966
|
+
2. 禁止手写 `ServiceException extends RuntimeException`(框架已提供 `ServiceExceptionUtil`)
|
|
967
|
+
3. 禁止使用 `try-catch` 捕获所有异常(仅捕获特定异常,兜底异常交给全局处理)
|
|
968
|
+
4. 禁止抛出 `Exception`、`RuntimeException` 等通用异常
|
|
969
|
+
|
|
970
|
+
---
|
|
971
|
+
|
|
972
|
+
## 十二、多租户开发规范
|
|
973
|
+
|
|
974
|
+
### 12.1 实体层
|
|
975
|
+
|
|
976
|
+
- 需要租户隔离的 DO 继承 `TenantBaseDO`(自动获得 `tenantId` 字段)
|
|
977
|
+
- 不需要租户隔离的 DO 继承 `BaseDO`,并在类上加 `@TenantIgnore` 注解:
|
|
978
|
+
|
|
979
|
+
```java
|
|
980
|
+
@TenantIgnore
|
|
981
|
+
@TableName("infra_config")
|
|
982
|
+
@Data
|
|
983
|
+
@EqualsAndHashCode(callSuper = true)
|
|
984
|
+
public class ConfigDO extends BaseDO {
|
|
985
|
+
// ...
|
|
986
|
+
}
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
### 12.2 查询层
|
|
990
|
+
|
|
991
|
+
- 框架自动在 SQL 中追加 `tenant_id = ?` 条件,无需手动添加
|
|
992
|
+
- 跨租户查询:在 Service 方法上加 `@TenantIgnore` 注解
|
|
993
|
+
|
|
994
|
+
---
|
|
995
|
+
|
|
996
|
+
## 十三、跨模块 RPC 调用规范
|
|
997
|
+
|
|
998
|
+
### 13.1 Feign Client 定义(-api 模块)
|
|
999
|
+
|
|
1000
|
+
```java
|
|
1001
|
+
package cn.ultracloud.com.module.system.api.user;
|
|
1002
|
+
|
|
1003
|
+
import cn.ultracloud.com.framework.common.pojo.CommonResult;
|
|
1004
|
+
import org.springframework.cloud.openfeign.FeignClient;
|
|
1005
|
+
import org.springframework.web.bind.annotation.GetMapping;
|
|
1006
|
+
import org.springframework.web.bind.annotation.RequestParam;
|
|
1007
|
+
|
|
1008
|
+
@FeignClient(name = ApiConstants.NAME, path = ApiConstants.PREFIX + "/user")
|
|
1009
|
+
public interface AdminUserApi {
|
|
1010
|
+
|
|
1011
|
+
@GetMapping("/get")
|
|
1012
|
+
CommonResult<AdminUserRespDTO> getUser(@RequestParam("id") Long id);
|
|
1013
|
+
}
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
### 13.2 RPC API 实现(-server 模块)
|
|
1017
|
+
|
|
1018
|
+
```java
|
|
1019
|
+
package cn.ultracloud.com.module.system.api.user;
|
|
1020
|
+
|
|
1021
|
+
import cn.ultracloud.com.framework.common.pojo.CommonResult;
|
|
1022
|
+
import cn.ultracloud.com.module.system.api.user.dto.AdminUserRespDTO;
|
|
1023
|
+
import cn.ultracloud.com.module.system.service.user.AdminUserService;
|
|
1024
|
+
import jakarta.annotation.Resource;
|
|
1025
|
+
import org.springframework.web.bind.annotation.RestController;
|
|
1026
|
+
|
|
1027
|
+
import static cn.ultracloud.com.framework.common.util.object.BeanUtils.toBean;
|
|
1028
|
+
import static cn.ultracloud.com.framework.common.pojo.CommonResult.success;
|
|
1029
|
+
|
|
1030
|
+
@RestController
|
|
1031
|
+
public class AdminUserApiImpl implements AdminUserApi {
|
|
1032
|
+
|
|
1033
|
+
@Resource
|
|
1034
|
+
private AdminUserService adminUserService;
|
|
1035
|
+
|
|
1036
|
+
@Override
|
|
1037
|
+
public CommonResult<AdminUserRespDTO> getUser(Long id) {
|
|
1038
|
+
return success(toBean(adminUserService.getUser(id), AdminUserRespDTO.class));
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
```
|
|
1042
|
+
|
|
1043
|
+
### 13.3 DTO 定义
|
|
1044
|
+
|
|
1045
|
+
- 位于 `-api` 模块的 `dto/{domain}/` 目录
|
|
1046
|
+
- 命名以 `RespDTO` / `ReqDTO` 结尾
|
|
1047
|
+
- 不包含 VO 特有的 `@Schema`、校验注解等
|
|
1048
|
+
|
|
1049
|
+
---
|
|
1050
|
+
|
|
1051
|
+
## 十四、测试规范
|
|
1052
|
+
|
|
1053
|
+
### 14.1 测试基类
|
|
1054
|
+
|
|
1055
|
+
- DB 单元测试:继承 `BaseDbUnitTest`(使用 H2 内存数据库)
|
|
1056
|
+
- 需要 Redis 的测试:继承 `BaseDbAndRedisUnitTest`(使用 jedis-mock)
|
|
1057
|
+
|
|
1058
|
+
### 14.2 测试类模板
|
|
1059
|
+
|
|
1060
|
+
```java
|
|
1061
|
+
package cn.ultracloud.com.module.wms.service.warehouse;
|
|
1062
|
+
|
|
1063
|
+
import cn.ultracloud.com.framework.test.core.ut.BaseDbUnitTest;
|
|
1064
|
+
import jakarta.annotation.Resource;
|
|
1065
|
+
import org.junit.jupiter.api.Test;
|
|
1066
|
+
import org.springframework.boot.test.context.SpringBootTest;
|
|
1067
|
+
import org.springframework.test.context.ActiveProfiles;
|
|
1068
|
+
import org.springframework.test.context.jdbc.Sql;
|
|
1069
|
+
|
|
1070
|
+
import static org.junit.jupiter.api.Assertions.*;
|
|
1071
|
+
|
|
1072
|
+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbUnitTest.Application.class)
|
|
1073
|
+
@ActiveProfiles("unit-test")
|
|
1074
|
+
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
|
|
1075
|
+
class WmsWarehouseServiceImplTest extends BaseDbUnitTest {
|
|
1076
|
+
|
|
1077
|
+
@Resource
|
|
1078
|
+
private WmsWarehouseServiceImpl warehouseService;
|
|
1079
|
+
|
|
1080
|
+
@Test
|
|
1081
|
+
void testCreateWarehouse_success() {
|
|
1082
|
+
// given
|
|
1083
|
+
WarehouseSaveReqVO reqVO = new WarehouseSaveReqVO();
|
|
1084
|
+
reqVO.setCode("WH001");
|
|
1085
|
+
reqVO.setName("华东主仓");
|
|
1086
|
+
// when
|
|
1087
|
+
Long id = warehouseService.createWarehouse(reqVO);
|
|
1088
|
+
// then
|
|
1089
|
+
assertNotNull(id);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
```
|
|
1093
|
+
|
|
1094
|
+
### 14.3 测试 SQL
|
|
1095
|
+
|
|
1096
|
+
- 创建表脚本:`src/test/resources/sql/create_tables.sql`
|
|
1097
|
+
- 清理脚本:`src/test/resources/sql/clean.sql`(每个测试方法执行后运行)
|
|
1098
|
+
|
|
1099
|
+
### 14.4 测试方法命名
|
|
1100
|
+
|
|
1101
|
+
`test{Method}_{Scenario}`,如 `testCreateWarehouse_success`、`testCreateWarehouse_codeDuplicate`
|
|
1102
|
+
|
|
1103
|
+
---
|
|
1104
|
+
|
|
1105
|
+
## 十五、注释规范
|
|
1106
|
+
|
|
1107
|
+
### 15.1 类注释
|
|
1108
|
+
|
|
1109
|
+
- DO 类:代码生成器自动生成 `/** ... */` Javadoc 包含 `@author`
|
|
1110
|
+
- 手写类:简洁注释说明作用即可,不强制 `@author` / `@date`
|
|
1111
|
+
|
|
1112
|
+
### 15.2 方法注释
|
|
1113
|
+
|
|
1114
|
+
- 公共方法:简洁的 `// 说明` 注释或单行 Javadoc
|
|
1115
|
+
- 复杂业务逻辑方法:详细 Javadoc(方法作用、参数说明、返回值)
|
|
1116
|
+
|
|
1117
|
+
### 15.3 字段注释
|
|
1118
|
+
|
|
1119
|
+
- VO 字段:使用 `@Schema(description = "...")` 替代 Javadoc
|
|
1120
|
+
- DO 字段:使用 `/** ... */` Javadoc 说明含义
|
|
1121
|
+
|
|
1122
|
+
### 15.4 禁止事项
|
|
1123
|
+
|
|
1124
|
+
1. 禁止无意义注释(如 `// 这里是循环`)
|
|
1125
|
+
2. 禁止注释与代码不一致
|
|
1126
|
+
3. 禁止使用拼音注释
|
|
1127
|
+
|
|
1128
|
+
---
|
|
1129
|
+
|
|
1130
|
+
## 十六、代码风格规范
|
|
1131
|
+
|
|
1132
|
+
1. **缩进**: 统一使用 4 个空格,禁止 Tab
|
|
1133
|
+
2. **大括号**: 左大括号紧跟语句末尾,右大括号单独成行
|
|
1134
|
+
3. **空行**: 方法之间、类成员之间空一行,逻辑块之间空一行
|
|
1135
|
+
4. **魔法值**: 禁止直接写数字、字符串,需定义为常量或枚举
|
|
1136
|
+
5. **集合初始化**: 指定大小(如 `new ArrayList<>(10)`)
|
|
1137
|
+
6. **空判断**: 使用 `ObjectUtil.isNull()` / `CollUtil.isEmpty()`(Hutool 工具类)
|
|
1138
|
+
7. **代码简洁**: 禁止冗余代码,复杂逻辑拆分方法
|
|
1139
|
+
8. **命名统一**: 同一含义的字段、方法,命名必须统一
|
|
1140
|
+
|
|
1141
|
+
---
|
|
1142
|
+
|
|
1143
|
+
## 十七、代码生成器
|
|
1144
|
+
|
|
1145
|
+
`ultracloud-module-infra` 提供代码生成器(codegen),可生成:
|
|
1146
|
+
- Controller、Service、Mapper、DO、VO(SaveReqVO/PageReqVO/ListReqVO/RespVO)
|
|
1147
|
+
- 单元测试类、SQL 脚本
|
|
1148
|
+
- 前端代码(Vue2 Element UI、Vue3 Element Plus、Vue3 Vben5 等)
|
|
1149
|
+
|
|
1150
|
+
使用方式:管理后台 `/admin-api/infra/codegen/` 或 API 调用。
|
|
1151
|
+
|
|
1152
|
+
生成的代码已遵循本规范中的所有约定。生成的错误码常量需手动补充到 `ErrorCodeConstants` 中。
|
|
1153
|
+
|
|
1154
|
+
---
|
|
1155
|
+
|
|
1156
|
+
## 十八、开发标准流程
|
|
1157
|
+
|
|
1158
|
+
1. **需求分析**: 明确功能、入参、出参、业务逻辑
|
|
1159
|
+
2. **定义 VO**: 编写 SaveReqVO(含校验)、PageReqVO、RespVO
|
|
1160
|
+
3. **定义 DO**: 在 `dal/dataobject/` 下创建实体,继承 BaseDO/TenantBaseDO
|
|
1161
|
+
4. **定义 Mapper**: 在 `dal/mysql/` 下创建接口,继承 BaseMapperX
|
|
1162
|
+
5. **定义 Service**: 接口 + Impl,实现业务逻辑
|
|
1163
|
+
6. **定义 Controller**: 定义接口路径、请求方法、权限注解,调用 Service
|
|
1164
|
+
7. **定义错误码**: 在 `ErrorCodeConstants` 中补充业务错误码
|
|
1165
|
+
8. **单元测试**: 编写测试用例,覆盖正常流程和异常场景
|
|
1166
|
+
9. **联调测试**: 测试接口是否正常、参数校验是否生效、异常是否正确返回
|