ai-engineering-init 1.4.3 → 1.5.0
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/.cursor/skills/bug-detective/SKILL.md +19 -19
- package/.cursor/skills/project-navigator/SKILL.md +164 -258
- package/package.json +7 -1
- package/scripts/build-skills.js +180 -0
- package/src/platform-map.json +56 -0
- package/src/skills/add-skill/SKILL.md +488 -0
- package/src/skills/add-todo/SKILL.md +269 -0
- package/src/skills/api-development/SKILL.md +266 -0
- package/src/skills/architecture-design/SKILL.md +262 -0
- package/src/skills/backend-annotations/SKILL.md +302 -0
- package/src/skills/banana-image/CHANGELOG.md +37 -0
- package/src/skills/banana-image/README.md +146 -0
- package/src/skills/banana-image/SKILL.md +171 -0
- package/src/skills/banana-image/assets/logo.png +0 -0
- package/src/skills/banana-image/references/advanced-usage.md +189 -0
- package/src/skills/banana-image/scripts/apply_template.py +125 -0
- package/src/skills/banana-image/scripts/banana_image_exec.ts +412 -0
- package/src/skills/banana-image/scripts/batch_prep.py +82 -0
- package/src/skills/banana-image/scripts/package-lock.json +1437 -0
- package/src/skills/banana-image/scripts/package.json +18 -0
- package/src/skills/banana-image/scripts/requirements.txt +10 -0
- package/src/skills/banana-image/templates/poster.json +22 -0
- package/src/skills/banana-image/templates/product.json +17 -0
- package/src/skills/banana-image/templates/social.json +22 -0
- package/src/skills/banana-image/templates/thumbnail.json +17 -0
- package/src/skills/brainstorm/SKILL.md +216 -0
- package/src/skills/bug-detective/SKILL.md +256 -0
- package/src/skills/bug-detective/references/error-patterns.md +242 -0
- package/src/skills/check/SKILL.md +367 -0
- package/src/skills/code-patterns/SKILL.md +280 -0
- package/src/skills/code-patterns/references/leniu-code-patterns.md +87 -0
- package/src/skills/codex-code-review/SKILL.md +135 -0
- package/src/skills/collaborating-with-codex/SKILL.md +174 -0
- package/src/skills/collaborating-with-codex/scripts/codex_bridge.py +275 -0
- package/src/skills/collaborating-with-gemini/SKILL.md +194 -0
- package/src/skills/collaborating-with-gemini/scripts/gemini_bridge.py +275 -0
- package/src/skills/crud/SKILL.md +265 -0
- package/src/skills/crud-development/SKILL.md +409 -0
- package/src/skills/data-permission/SKILL.md +292 -0
- package/src/skills/data-permission/references/custom-data-scope.md +90 -0
- package/src/skills/database-ops/SKILL.md +407 -0
- package/src/skills/dev/SKILL.md +187 -0
- package/src/skills/error-handler/SKILL.md +371 -0
- package/src/skills/file-oss-management/SKILL.md +255 -0
- package/src/skills/file-oss-management/references/entities.md +105 -0
- package/src/skills/file-oss-management/references/service-impl.md +104 -0
- package/src/skills/git-workflow/SKILL.md +397 -0
- package/src/skills/init-docs/SKILL.md +194 -0
- package/src/skills/json-serialization/SKILL.md +357 -0
- package/src/skills/leniu-api-development/SKILL.md +319 -0
- package/src/skills/leniu-api-development/references/real-examples.md +273 -0
- package/src/skills/leniu-architecture-design/SKILL.md +383 -0
- package/src/skills/leniu-backend-annotations/SKILL.md +277 -0
- package/src/skills/leniu-brainstorm/SKILL.md +242 -0
- package/src/skills/leniu-brainstorm/references/business-scenarios.md +162 -0
- package/src/skills/leniu-code-patterns/SKILL.md +411 -0
- package/src/skills/leniu-crud-development/SKILL.md +404 -0
- package/src/skills/leniu-crud-development/references/templates.md +597 -0
- package/src/skills/leniu-customization-location/SKILL.md +410 -0
- package/src/skills/leniu-data-permission/SKILL.md +341 -0
- package/src/skills/leniu-database-ops/SKILL.md +426 -0
- package/src/skills/leniu-error-handler/SKILL.md +462 -0
- package/src/skills/leniu-java-amount-handling/SKILL.md +461 -0
- package/src/skills/leniu-java-code-style/SKILL.md +510 -0
- package/src/skills/leniu-java-concurrent/SKILL.md +400 -0
- package/src/skills/leniu-java-entity/SKILL.md +237 -0
- package/src/skills/leniu-java-entity/references/templates.md +237 -0
- package/src/skills/leniu-java-export/SKILL.md +570 -0
- package/src/skills/leniu-java-logging/SKILL.md +229 -0
- package/src/skills/leniu-java-logging/references/data-mask.md +46 -0
- package/src/skills/leniu-java-logging/references/logging-scenarios.md +113 -0
- package/src/skills/leniu-java-mq/SKILL.md +338 -0
- package/src/skills/leniu-java-mybatis/SKILL.md +267 -0
- package/src/skills/leniu-java-mybatis/references/report-mapper.md +88 -0
- package/src/skills/leniu-java-report-query-param/SKILL.md +291 -0
- package/src/skills/leniu-java-task/SKILL.md +367 -0
- package/src/skills/leniu-java-total-line/SKILL.md +196 -0
- package/src/skills/leniu-marketing-price-rule-customizer/SKILL.md +301 -0
- package/src/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +285 -0
- package/src/skills/leniu-mealtime/SKILL.md +215 -0
- package/src/skills/leniu-redis-cache/SKILL.md +331 -0
- package/src/skills/leniu-report-customization/SKILL.md +335 -0
- package/src/skills/leniu-report-customization/references/table-fields.md +93 -0
- package/src/skills/leniu-report-standard-customization/SKILL.md +328 -0
- package/src/skills/leniu-report-standard-customization/references/analysis-module.md +64 -0
- package/src/skills/leniu-report-standard-customization/references/table-fields.md +113 -0
- package/src/skills/leniu-security-guard/SKILL.md +306 -0
- package/src/skills/leniu-utils-toolkit/SKILL.md +380 -0
- package/src/skills/mysql-debug/SKILL.md +364 -0
- package/src/skills/next/SKILL.md +137 -0
- package/src/skills/openspec-apply-change/SKILL.md +165 -0
- package/src/skills/openspec-archive-change/SKILL.md +122 -0
- package/src/skills/openspec-bulk-archive-change/SKILL.md +254 -0
- package/src/skills/openspec-continue-change/SKILL.md +126 -0
- package/src/skills/openspec-explore/SKILL.md +299 -0
- package/src/skills/openspec-ff-change/SKILL.md +109 -0
- package/src/skills/openspec-new-change/SKILL.md +82 -0
- package/src/skills/openspec-onboard/SKILL.md +414 -0
- package/src/skills/openspec-sync-specs/SKILL.md +146 -0
- package/src/skills/openspec-verify-change/SKILL.md +176 -0
- package/src/skills/performance-doctor/SKILL.md +303 -0
- package/src/skills/progress/SKILL.md +193 -0
- package/src/skills/project-navigator/SKILL.md +211 -0
- package/src/skills/redis-cache/SKILL.md +333 -0
- package/src/skills/redis-cache/references/listeners.md +23 -0
- package/src/skills/scheduled-jobs/SKILL.md +314 -0
- package/src/skills/security-guard/SKILL.md +353 -0
- package/src/skills/security-guard/references/encrypt-config.md +103 -0
- package/src/skills/security-guard/references/sensitive-strategies.md +42 -0
- package/src/skills/sms-mail/SKILL.md +308 -0
- package/src/skills/sms-mail/references/mail-config.md +88 -0
- package/src/skills/sms-mail/references/sms-config.md +74 -0
- package/src/skills/social-login/SKILL.md +266 -0
- package/src/skills/social-login/references/provider-configs.md +118 -0
- package/src/skills/start/SKILL.md +154 -0
- package/src/skills/store-pc/SKILL.md +366 -0
- package/src/skills/sync/SKILL.md +149 -0
- package/src/skills/task-tracker/SKILL.md +307 -0
- package/src/skills/tech-decision/SKILL.md +393 -0
- package/src/skills/tenant-management/SKILL.md +288 -0
- package/src/skills/tenant-management/references/tenant-scenarios.md +91 -0
- package/src/skills/test-development/SKILL.md +301 -0
- package/src/skills/test-development/references/parameterized-examples.md +119 -0
- package/src/skills/ui-pc/SKILL.md +438 -0
- package/src/skills/update-status/SKILL.md +159 -0
- package/src/skills/utils-toolkit/SKILL.md +362 -0
- package/src/skills/utils-toolkit/references/redis-utils-api.md +56 -0
- package/src/skills/websocket-sse/SKILL.md +271 -0
- package/src/skills/workflow-engine/SKILL.md +321 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: leniu-api-development
|
|
3
|
+
description: |
|
|
4
|
+
leniu-yunshitang-core 项目 API 接口开发规范。基于四层架构的 API 开发指南。
|
|
5
|
+
|
|
6
|
+
触发场景:
|
|
7
|
+
- 设计 leniu 项目 API 接口
|
|
8
|
+
- 编写 leniu Controller 层代码
|
|
9
|
+
- 配置接口权限和参数校验
|
|
10
|
+
- 接口返回值类型选择
|
|
11
|
+
- 多端路由规划(Web/Mobile/Android)
|
|
12
|
+
|
|
13
|
+
适用项目:leniu-tengyun-core(云食堂项目)
|
|
14
|
+
|
|
15
|
+
触发词:API接口、Controller、LeResult、LeResponse、LeRequest、接口开发、路由前缀、分页查询、接口权限
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# leniu API 接口开发规范
|
|
19
|
+
|
|
20
|
+
## HTTP 方法与路径规范
|
|
21
|
+
|
|
22
|
+
| 操作 | HTTP 方法 | 路径示例 | 返回类型 |
|
|
23
|
+
|------|---------|---------|---------|
|
|
24
|
+
| 分页查询 | POST | `/query`, `/page-*` | `Page<VO>` / `PageVO<VO>` |
|
|
25
|
+
| 获取详情 | POST/GET | `/{id}`, `/get-*`, `/info` | VO |
|
|
26
|
+
| 新增 | POST | `/add` | `void` |
|
|
27
|
+
| 修改 | POST/PUT | `/update`, `/modify-*` | `void` |
|
|
28
|
+
| 删除 | DELETE/POST | `/delete`, `/batch/delete` | `void` |
|
|
29
|
+
| 列表(不分页) | POST | `/list-all`, `/type-list` | `List<VO>` |
|
|
30
|
+
| 导出 | POST | `/export`, `/export-async/*` | `void` |
|
|
31
|
+
| 导入 | POST | `/import-excel` | `void` |
|
|
32
|
+
| 树形查询 | POST | `/tree` | `List<Tree<Long>>` |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Controller 标准模板
|
|
37
|
+
|
|
38
|
+
```java
|
|
39
|
+
@Api(tags = "模块-功能描述")
|
|
40
|
+
@RestController
|
|
41
|
+
@RequiresAuthentication
|
|
42
|
+
@RequestMapping("/api/v2/web/module")
|
|
43
|
+
public class XxxWebController {
|
|
44
|
+
|
|
45
|
+
@Autowired
|
|
46
|
+
private XxxBusiness xxxBusiness;
|
|
47
|
+
|
|
48
|
+
@PostMapping("/add")
|
|
49
|
+
@ApiOperation(value = "功能-新增")
|
|
50
|
+
public void add(@Validated(InsertGroup.class) @RequestBody LeRequest<XxxDTO> request) {
|
|
51
|
+
xxxBusiness.add(request.getContent());
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@PostMapping("/update")
|
|
55
|
+
@ApiOperation(value = "功能-修改")
|
|
56
|
+
public void update(@Validated(UpdateGroup.class) @RequestBody LeRequest<XxxDTO> request) {
|
|
57
|
+
xxxBusiness.update(request.getContent());
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@PostMapping("/query")
|
|
61
|
+
@ApiOperation(value = "功能-分页查询")
|
|
62
|
+
public Page<XxxVO> query(@Validated @RequestBody LeRequest<XxxQueryParam> request) {
|
|
63
|
+
return xxxBusiness.page(request.getContent());
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@PostMapping("/delete")
|
|
67
|
+
@ApiOperation(value = "功能-删除")
|
|
68
|
+
public void delete(@RequestBody LeRequest<Long> request) {
|
|
69
|
+
xxxBusiness.delete(request.getContent());
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 请求/响应封装
|
|
77
|
+
|
|
78
|
+
### 请求:LeRequest<T>
|
|
79
|
+
|
|
80
|
+
所有接口入参统一用 `LeRequest<T>` 包装,通过 `request.getContent()` 获取实际参数。
|
|
81
|
+
|
|
82
|
+
```java
|
|
83
|
+
@PostMapping("/query")
|
|
84
|
+
public Page<XxxVO> query(@RequestBody LeRequest<XxxQueryParam> request) {
|
|
85
|
+
return xxxService.page(request.getContent());
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 响应类型选择
|
|
90
|
+
|
|
91
|
+
| 场景 | 返回类型 | 示例 |
|
|
92
|
+
|------|---------|------|
|
|
93
|
+
| 分页 | `Page<VO>` / `PageVO<VO>` | `return xxxService.page(param);` |
|
|
94
|
+
| 单值包装 | `LeResponse<T>` | `return LeResponse.succ(value);` |
|
|
95
|
+
| 复杂对象 | 直接返回 VO | `return xxxService.getDetail(id);` |
|
|
96
|
+
| 报表+合计行 | `ReportBaseTotalVO<VO>` | `return service.pageSummary(param);` |
|
|
97
|
+
| 写操作 | `void` | 无返回值 |
|
|
98
|
+
|
|
99
|
+
```java
|
|
100
|
+
// LeResponse 用法
|
|
101
|
+
@PostMapping("/get-config")
|
|
102
|
+
public LeResponse<String> getConfig(@RequestBody LeRequest<Long> request) {
|
|
103
|
+
return LeResponse.succ(xxxService.getConfig(request.getContent()));
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 参数校验
|
|
110
|
+
|
|
111
|
+
### 分组校验(新增/修改区分)
|
|
112
|
+
|
|
113
|
+
```java
|
|
114
|
+
// 分组接口定义
|
|
115
|
+
public interface InsertGroup {}
|
|
116
|
+
public interface UpdateGroup {}
|
|
117
|
+
|
|
118
|
+
// Controller 使用
|
|
119
|
+
@PostMapping("/add")
|
|
120
|
+
public void add(@Validated(InsertGroup.class) @RequestBody LeRequest<XxxDTO> request) {}
|
|
121
|
+
|
|
122
|
+
@PostMapping("/update")
|
|
123
|
+
public void update(@Validated(UpdateGroup.class) @RequestBody LeRequest<XxxDTO> request) {}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### DTO 校验示例
|
|
127
|
+
|
|
128
|
+
```java
|
|
129
|
+
// 必须用 jakarta.validation(JDK 21)
|
|
130
|
+
import jakarta.validation.constraints.*;
|
|
131
|
+
|
|
132
|
+
@Data
|
|
133
|
+
public class XxxDTO {
|
|
134
|
+
@NotNull(message = "ID不能为空", groups = {UpdateGroup.class})
|
|
135
|
+
private Long id;
|
|
136
|
+
|
|
137
|
+
@NotBlank(message = "名称不能为空", groups = {InsertGroup.class, UpdateGroup.class})
|
|
138
|
+
@Size(max = 100, message = "名称长度不能超过100个字符")
|
|
139
|
+
private String name;
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 简单校验(无分组)
|
|
144
|
+
|
|
145
|
+
```java
|
|
146
|
+
@PostMapping("/query")
|
|
147
|
+
public Page<XxxVO> query(@Validated @RequestBody LeRequest<XxxQueryParam> request) {}
|
|
148
|
+
|
|
149
|
+
@PostMapping("/check")
|
|
150
|
+
public LeResponse<Boolean> check(@Valid @RequestBody LeRequest<XxxDTO> request) {}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 认证注解
|
|
156
|
+
|
|
157
|
+
| 注解 | 用途 | 包路径 |
|
|
158
|
+
|------|------|--------|
|
|
159
|
+
| `@RequiresAuthentication` | 需要登录(类/方法级) | `net.xnzn.framework.secure.filter.annotation` |
|
|
160
|
+
| `@RequiresGuest` | 允许游客访问(方法级覆盖) | `net.xnzn.framework.secure.filter.annotation` |
|
|
161
|
+
|
|
162
|
+
```java
|
|
163
|
+
@RequiresAuthentication // 类级别:所有方法默认需登录
|
|
164
|
+
public class XxxController {
|
|
165
|
+
|
|
166
|
+
@RequiresGuest // 方法级别覆盖:此接口游客可访问
|
|
167
|
+
@PostMapping("/public-list")
|
|
168
|
+
public List<XxxVO> publicList() {}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 常见场景
|
|
175
|
+
|
|
176
|
+
### 带 Redisson 分布式锁的导入
|
|
177
|
+
|
|
178
|
+
```java
|
|
179
|
+
@PostMapping("/import-excel")
|
|
180
|
+
@ApiOperation(value = "Excel导入")
|
|
181
|
+
public void importExcel(@RequestParam(value = "file") MultipartFile file) {
|
|
182
|
+
RLock lock = redissonClient.getLock("import:lock:" + TenantContextHolder.getTenantId());
|
|
183
|
+
if (!lock.tryLock(5, 60, TimeUnit.SECONDS)) {
|
|
184
|
+
throw new LeException("正在处理中,请稍后再试");
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
xxxService.importExcel(file);
|
|
188
|
+
} finally {
|
|
189
|
+
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
|
|
190
|
+
lock.unlock();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### 批量删除
|
|
197
|
+
|
|
198
|
+
```java
|
|
199
|
+
@PutMapping("/batch/delete")
|
|
200
|
+
@ApiOperation(value = "批量删除")
|
|
201
|
+
public void batchDelete(@RequestBody LeRequest<List<Long>> request) {
|
|
202
|
+
xxxService.batchDelete(request.getContent());
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### 树形结构查询
|
|
207
|
+
|
|
208
|
+
```java
|
|
209
|
+
@PostMapping("/tree")
|
|
210
|
+
@ApiOperation(value = "树形结构查询")
|
|
211
|
+
public List<Tree<Long>> getTree(@RequestBody LeRequest<XxxQueryParam> request) {
|
|
212
|
+
return xxxService.getTree(request.getContent());
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### 同步导出
|
|
217
|
+
|
|
218
|
+
```java
|
|
219
|
+
@SneakyThrows
|
|
220
|
+
@PostMapping("/export")
|
|
221
|
+
@ApiOperation(value = "导出")
|
|
222
|
+
public void export(@RequestBody LeRequest<XxxParam> request, HttpServletResponse response) {
|
|
223
|
+
XxxParam param = request.getContent();
|
|
224
|
+
List<XxxVO> list = xxxService.listAll(param);
|
|
225
|
+
EasyExcelUtil.writeExcelByDownLoadIncludeWrite(response, "文件名",
|
|
226
|
+
XxxVO.class, "标题", list, param.getExportCols());
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### 异步导出(大数据量)
|
|
231
|
+
|
|
232
|
+
```java
|
|
233
|
+
@PostMapping("/export-async/xxx")
|
|
234
|
+
@ApiOperation(value = "异步导出")
|
|
235
|
+
public void exportAsync(@RequestBody LeRequest<XxxParam> request) {
|
|
236
|
+
XxxParam param = request.getContent();
|
|
237
|
+
exportApi.startExcelExportTaskByPage(
|
|
238
|
+
"文件名", I18n.getMessage("title.key"),
|
|
239
|
+
XxxVO.class, param.getExportCols(), param.getPage(), null,
|
|
240
|
+
() -> PageVO.of(xxxService.listAll(param)));
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## 路径命名规范
|
|
247
|
+
|
|
248
|
+
| 模式 | 示例 | 说明 |
|
|
249
|
+
|------|------|------|
|
|
250
|
+
| 标准前缀 | `/api/v2/web/module` | 按端区分 |
|
|
251
|
+
| 报表前缀 | `/summary/xxx` | 报表/统计类 |
|
|
252
|
+
| 查询接口 | `/page/detail`, `/query` | 分页查询 |
|
|
253
|
+
| 枚举接口 | `/type-list`, `/state-list` | 枚举数据 |
|
|
254
|
+
| 导出接口 | `/export`, `/export-async/xxx` | 同步/异步 |
|
|
255
|
+
| 导出 Controller 独立 | `XxxExportWebController` | 与查询分离 |
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## 检查清单
|
|
260
|
+
|
|
261
|
+
- [ ] 认证注解:`@RequiresAuthentication` 或 `@RequiresGuest`
|
|
262
|
+
- [ ] 文档注解:`@Api` + `@ApiOperation`
|
|
263
|
+
- [ ] 参数校验:`@Validated(InsertGroup/UpdateGroup.class)` 或 `@Valid`
|
|
264
|
+
- [ ] 请求封装:`LeRequest<T>`
|
|
265
|
+
- [ ] 返回类型:分页 `Page<VO>`、单值 `LeResponse<T>`、写操作 `void`
|
|
266
|
+
- [ ] 路径命名:`/add`、`/update`、`/query`、`/delete`
|
|
267
|
+
- [ ] 敏感操作加分布式锁
|
|
268
|
+
- [ ] 跨模块依赖用 `@Lazy` 避免循环依赖
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## 错误对比
|
|
273
|
+
|
|
274
|
+
```java
|
|
275
|
+
// ❌ 不使用参数封装
|
|
276
|
+
public Long add(@RequestBody XxxDTO dto) {}
|
|
277
|
+
// ✅ 使用 LeRequest
|
|
278
|
+
public void add(@RequestBody LeRequest<XxxDTO> request) { xxxService.add(request.getContent()); }
|
|
279
|
+
|
|
280
|
+
// ❌ 不使用分组校验
|
|
281
|
+
public void add(@Valid @RequestBody LeRequest<XxxDTO> request) {}
|
|
282
|
+
// ✅ 使用分组
|
|
283
|
+
public void add(@Validated(InsertGroup.class) @RequestBody LeRequest<XxxDTO> request) {}
|
|
284
|
+
|
|
285
|
+
// ❌ javax.validation(JDK 21 必须 jakarta)
|
|
286
|
+
import javax.validation.constraints.NotNull;
|
|
287
|
+
// ✅
|
|
288
|
+
import jakarta.validation.constraints.NotNull;
|
|
289
|
+
|
|
290
|
+
// ❌ 缺少认证注解
|
|
291
|
+
@RestController
|
|
292
|
+
public class XxxController {}
|
|
293
|
+
// ✅
|
|
294
|
+
@RestController @RequiresAuthentication
|
|
295
|
+
public class XxxController {}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## 真实代码示例
|
|
301
|
+
|
|
302
|
+
详见 `references/real-examples.md`,包含:
|
|
303
|
+
- ReportAnalysisController(报表模块)
|
|
304
|
+
- OrderInfoExportWebController(订单导出)
|
|
305
|
+
- OrderInfoWebController(订单查询)
|
|
306
|
+
- AttendanceLeaveInfoController(考勤请假)
|
|
307
|
+
- AllocCanteenController(食堂档口)
|
|
308
|
+
- MgrMenuController(菜单权限)
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## 相关技能
|
|
313
|
+
|
|
314
|
+
| 需要了解 | Skill |
|
|
315
|
+
|---------|-------|
|
|
316
|
+
| Service 层 | `leniu-service-development` |
|
|
317
|
+
| Entity 设计 | `leniu-entity-design` |
|
|
318
|
+
| 数据库设计 | `leniu-database-ops` |
|
|
319
|
+
| CRUD 开发 | `leniu-crud-development` |
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# leniu API 真实代码示例
|
|
2
|
+
|
|
3
|
+
## 示例1:ReportAnalysisController(报表模块典型 Controller)
|
|
4
|
+
|
|
5
|
+
```java
|
|
6
|
+
@RestController
|
|
7
|
+
@RequestMapping("/summary/analysis")
|
|
8
|
+
@Api(value = "报表中心/经营分析", tags = "报表/经营分析-新")
|
|
9
|
+
public class ReportAnalysisController {
|
|
10
|
+
|
|
11
|
+
@Autowired
|
|
12
|
+
@Lazy
|
|
13
|
+
protected ExportApi exportApi; // 跨模块依赖用 @Lazy 避免循环依赖
|
|
14
|
+
@Autowired
|
|
15
|
+
private ReportAnalysisTurnoverService reportAnalysisTurnoverService;
|
|
16
|
+
|
|
17
|
+
// 报表查询:返回直接 VO 对象
|
|
18
|
+
@ApiOperation(value = "营业分析-总营业额")
|
|
19
|
+
@PostMapping("/turnover/total")
|
|
20
|
+
public ReportTurnoverPO getTurnoverTotal(@RequestBody LeRequest<ReportAnalysisTurnoverParam> request) {
|
|
21
|
+
return reportAnalysisTurnoverService.getTurnoverTotal(request.getContent());
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// 报表分页:返回 ReportBaseTotalVO(含合计行)
|
|
25
|
+
@ApiOperation(value = "营业分析-营业额报表")
|
|
26
|
+
@PostMapping("/turnover/detail")
|
|
27
|
+
public ReportBaseTotalVO<ReportAnalysisTurnoverDetailVO> getTurnoverDetail(
|
|
28
|
+
@RequestBody LeRequest<ReportAnalysisTurnoverParam> request) {
|
|
29
|
+
return reportAnalysisTurnoverService.getTurnoverDetail(request.getContent());
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 同步导出(小数据量)
|
|
33
|
+
@SneakyThrows
|
|
34
|
+
@ApiOperation(value = "食堂满意度统计导出")
|
|
35
|
+
@PostMapping("/evaluate/export")
|
|
36
|
+
public void exportEvaluateSummary(@RequestBody LeRequest<ReportAnalysisEvaluateParam> request,
|
|
37
|
+
HttpServletResponse response) {
|
|
38
|
+
ReportAnalysisEvaluateParam param = request.getContent();
|
|
39
|
+
ReportBaseTotalVO<ReportAnalysisEvaluateVO> result = reportAnalysisEvaluateService.pageSummary(param);
|
|
40
|
+
List<ReportAnalysisEvaluateVO> list = (List<ReportAnalysisEvaluateVO>)
|
|
41
|
+
CollUtil.addAll(result.getResultPage().getRecords(), result.getTotalLine());
|
|
42
|
+
EasyExcelUtil.writeExcelByDownLoadIncludeWrite(response, "文件",
|
|
43
|
+
ReportAnalysisEvaluateVO.class, ReportConstant.REPORT_TITLE_DETAILS, list, param.getExportCols());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 异步导出(大数据量)
|
|
47
|
+
@ApiOperation(value = "异步导出菜品销售排行")
|
|
48
|
+
@PostMapping("/export-async/dishes/sale/export")
|
|
49
|
+
public void exportDishesSaleSummary(@RequestBody LeRequest<ReportAnalysisDishesSaleParam> request) {
|
|
50
|
+
ReportAnalysisDishesSaleParam param = request.getContent();
|
|
51
|
+
ReportBaseTotalVO<ReportAnalysisDishesSaleVO> result = reportAnalysisDishesSaleService.pageSummary(param);
|
|
52
|
+
List<ReportAnalysisDishesSaleVO> list = (List<ReportAnalysisDishesSaleVO>)
|
|
53
|
+
CollUtil.addAll(result.getResultPage().getRecords(), result.getTotalLine());
|
|
54
|
+
exportApi.startExcelExportTaskByPage(
|
|
55
|
+
"菜品销售排行",
|
|
56
|
+
I18n.getMessage(ReportConstant.REPORT_TITLE_DETAILS),
|
|
57
|
+
ReportAnalysisDishesSaleVO.class,
|
|
58
|
+
param.getExportCols(),
|
|
59
|
+
param.getPage(),
|
|
60
|
+
null,
|
|
61
|
+
() -> PageVO.of(list));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 示例2:OrderInfoExportWebController(订单导出 Controller)
|
|
69
|
+
|
|
70
|
+
```java
|
|
71
|
+
// 导出功能独立到单独的 Controller,与查询 Controller 分离
|
|
72
|
+
@Slf4j
|
|
73
|
+
@RestController
|
|
74
|
+
@RequestMapping(value = "/api/v2/web/order/export")
|
|
75
|
+
public class OrderInfoExportWebController {
|
|
76
|
+
|
|
77
|
+
@Autowired
|
|
78
|
+
@Lazy
|
|
79
|
+
protected OrderWebBusiness orderWebBusiness;
|
|
80
|
+
@Autowired
|
|
81
|
+
@Lazy
|
|
82
|
+
protected OrderClients orderClients;
|
|
83
|
+
|
|
84
|
+
// 使用 orderClients.export() 启动异步导出(通过 client 代理)
|
|
85
|
+
@PostMapping(value = "/detail")
|
|
86
|
+
public void exportOrderDetail(@RequestBody LeRequest<OrderDetailWebDTO> request) {
|
|
87
|
+
OrderDetailWebDTO orderDetailDTO = request.getContent();
|
|
88
|
+
orderClients.export().startExcelExportTaskByPage(
|
|
89
|
+
I18n.getMessage("order.title.order-detail"), // 文件名(国际化)
|
|
90
|
+
orderSheetName(), // 工作表名
|
|
91
|
+
OrderListWebVO.class, // 数据类
|
|
92
|
+
orderDetailDTO.getExportCols(), // 导出列
|
|
93
|
+
orderDetailDTO, // 分页参数
|
|
94
|
+
null, // 合计行(可选)
|
|
95
|
+
() -> orderWebBusiness.queryOrderInfoWebByPage(orderDetailDTO, orderDetailDTO));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 示例3:OrderInfoWebController(订单查询 Controller)
|
|
103
|
+
|
|
104
|
+
```java
|
|
105
|
+
@Validated
|
|
106
|
+
@RestController
|
|
107
|
+
@RequestMapping(value = "/api/v2/web/order")
|
|
108
|
+
public class OrderInfoWebController {
|
|
109
|
+
|
|
110
|
+
@Autowired
|
|
111
|
+
private OrderWebBusiness orderWebBusiness;
|
|
112
|
+
|
|
113
|
+
// 详情查询
|
|
114
|
+
@PostMapping(value = "/info")
|
|
115
|
+
public OrderInfoWebVO info(@Valid @RequestBody LeRequest<OrderInfoWebDTO> request) {
|
|
116
|
+
return orderWebBusiness.getOrderInfo(request.getContent().getOrderId());
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 分页查询:返回 PageVO
|
|
120
|
+
@PostMapping(value = "/page/detail")
|
|
121
|
+
public PageVO<OrderListWebVO> pageOrderDetail(@RequestBody LeRequest<OrderDetailWebDTO> request) {
|
|
122
|
+
OrderDetailWebDTO content = request.getContent();
|
|
123
|
+
return orderWebBusiness.queryOrderInfoWebByPage(PageDTO.of(content.getCurrent(), content.getSize()), content);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 业务失败抛出 LeException
|
|
127
|
+
@PostMapping(value = "/refund")
|
|
128
|
+
public OrderRefundResultVO orderRefundWeb(@RequestBody LeRequest<OrderRefundSubmitWebDTO> request) throws LeCheckedException {
|
|
129
|
+
OrderRefundResultVO result = orderRefundBusiness.orderRefund(
|
|
130
|
+
request.getContent().convertToOrderRefundParam(), OrderRefundBizEnum.WEB);
|
|
131
|
+
if (!result.ifSuccess()) {
|
|
132
|
+
throw new LeException(result.getResultCode(), result.getResultMsg());
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 示例4:AttendanceLeaveInfoController(完整 CRUD)
|
|
142
|
+
|
|
143
|
+
```java
|
|
144
|
+
package net.xnzn.core.attendance.leave.controller;
|
|
145
|
+
|
|
146
|
+
@Api(tags = "zjl-考勤-请假信息")
|
|
147
|
+
@RestController
|
|
148
|
+
@RequiresAuthentication
|
|
149
|
+
@RequestMapping("/attendance/leave-info")
|
|
150
|
+
public class AttendanceLeaveInfoController {
|
|
151
|
+
|
|
152
|
+
@Autowired
|
|
153
|
+
AttendanceLeaveInfoService attendanceLeaveInfoService;
|
|
154
|
+
@Autowired
|
|
155
|
+
RedissonClient redissonClient;
|
|
156
|
+
|
|
157
|
+
@PostMapping("/add")
|
|
158
|
+
@ApiOperation(value = "请假信息-新增")
|
|
159
|
+
public void add(@RequestBody LeRequest<AddOrUpdateAttendanceLeaveInfoDTO> request) {
|
|
160
|
+
attendanceLeaveInfoService.add(request.getContent());
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@PutMapping("/batch/delete")
|
|
164
|
+
@ApiOperation(value = "请假信息-批量删除")
|
|
165
|
+
public void batchDelete(@RequestBody LeRequest<List<Long>> request) {
|
|
166
|
+
attendanceLeaveInfoService.batchDelete(request.getContent());
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@PutMapping("/update")
|
|
170
|
+
@ApiOperation(value = "请假信息-修改")
|
|
171
|
+
public void update(@RequestBody LeRequest<AddOrUpdateAttendanceLeaveInfoDTO> request) {
|
|
172
|
+
attendanceLeaveInfoService.update(request.getContent());
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
@PostMapping("/query")
|
|
176
|
+
@ApiOperation(value = "请假信息-分页查询")
|
|
177
|
+
public Page<QueryAttendanceLeaveInfoVO> query(@RequestBody LeRequest<QueryAttendanceLeaveInfoDTO> request) {
|
|
178
|
+
return attendanceLeaveInfoService.page(request.getContent());
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@PostMapping("/import-excel")
|
|
182
|
+
@ApiOperation(value = "请假信息-excel导入")
|
|
183
|
+
public void importExcel(@RequestParam(value = "leaveInfoExcel") MultipartFile leaveInfoExcel) throws Exception {
|
|
184
|
+
RLock lock = redissonClient.getLock("import:lock:" + TenantContextHolder.getTenantId());
|
|
185
|
+
if (!lock.tryLock(5, TimeUnit.SECONDS)) {
|
|
186
|
+
throw new LeException("考勤-请假数据导入中,请等待...");
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
attendanceLeaveInfoService.importExcel(new ImportLeaveInfoExcelDTO().setLeaveInfoExcel(leaveInfoExcel));
|
|
190
|
+
} finally {
|
|
191
|
+
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
|
|
192
|
+
lock.unlock();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## 示例5:AllocCanteenController(食堂档口,混合认证)
|
|
202
|
+
|
|
203
|
+
```java
|
|
204
|
+
package net.xnzn.core.allocation.canteen.controller;
|
|
205
|
+
|
|
206
|
+
@Slf4j
|
|
207
|
+
@RestController
|
|
208
|
+
@RequestMapping("/api/v2/alloc/canteen")
|
|
209
|
+
@Api(tags = "lsh_食堂档口相关控制器")
|
|
210
|
+
public class AllocCanteenController {
|
|
211
|
+
|
|
212
|
+
@Autowired
|
|
213
|
+
@Lazy
|
|
214
|
+
private AllocCanteenService allocCanteenService;
|
|
215
|
+
|
|
216
|
+
@ApiOperation("分页查询食堂列表")
|
|
217
|
+
@PostMapping("/page-canteen")
|
|
218
|
+
public Page<AllocCanteenVO> pageCanteen(@RequestBody LeRequest<AllocCanteenStallPageParam> request) {
|
|
219
|
+
return allocCanteenService.pageCanteen(request.getContent());
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
@ApiOperation("查询区域食堂档口餐线编号")
|
|
223
|
+
@PostMapping("/get-canteen-num")
|
|
224
|
+
public LeResponse<String> getCanteenNum(@Valid @RequestBody LeRequest<AllocCanteenNumParam> request) {
|
|
225
|
+
return LeResponse.succ(allocCanteenBusiness.getCanteenNum(request.getContent()));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
@ApiOperation("新增食堂")
|
|
229
|
+
@PostMapping("/add-canteen")
|
|
230
|
+
@RequiresGuest
|
|
231
|
+
public void addCanteen(@Valid @RequestBody LeRequest<AllocCanteenModel> request) {
|
|
232
|
+
allocCanteenBusiness.addCanteen(request.getContent());
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## 示例6:MgrMenuController(菜单权限,方法级认证)
|
|
240
|
+
|
|
241
|
+
```java
|
|
242
|
+
package net.xnzn.core.auth.menu.controller;
|
|
243
|
+
|
|
244
|
+
@RestController
|
|
245
|
+
@RequestMapping("/api/v1/mgrmenu")
|
|
246
|
+
@Api(value = "mgrmenu", tags = "菜单权限表管理")
|
|
247
|
+
public class MgrMenuController {
|
|
248
|
+
|
|
249
|
+
@Autowired
|
|
250
|
+
private MgrMenuService mgrMenuService;
|
|
251
|
+
|
|
252
|
+
@ApiOperation(value = "新增菜单")
|
|
253
|
+
@PostMapping("/add")
|
|
254
|
+
@RequiresAuthentication
|
|
255
|
+
public void saveMenu(@Valid @RequestBody LeRequest<MgrMenuVO> request) {
|
|
256
|
+
mgrMenuService.saveMenu(request.getContent());
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
@ApiOperation(value = "更新菜单")
|
|
260
|
+
@PostMapping("/update")
|
|
261
|
+
@RequiresAuthentication
|
|
262
|
+
public boolean updateMenu(@Valid @RequestBody LeRequest<MgrMenuVO> request) {
|
|
263
|
+
return mgrMenuService.updateMenu(request.getContent());
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
@ApiOperation(value = "删除菜单")
|
|
267
|
+
@DeleteMapping("/{id}")
|
|
268
|
+
@RequiresGuest
|
|
269
|
+
public boolean removeById(@PathVariable Long id) {
|
|
270
|
+
return mgrMenuService.removeById(id);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|