ai-engineering-init 1.4.3 → 1.6.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/README.md +20 -236
- package/bin/index.js +437 -7
- 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,404 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: leniu-crud-development
|
|
3
|
+
description: |
|
|
4
|
+
leniu 项目 CRUD 开发规范。基于 pigx-framework 四层架构(Controller -> Business -> Service -> Mapper)。
|
|
5
|
+
涵盖命名规范、代码模板、分页模式、事务管理、并发处理、代码质量要点。
|
|
6
|
+
|
|
7
|
+
触发场景:
|
|
8
|
+
- 新建 leniu 业务模块的 CRUD 功能
|
|
9
|
+
- 创建 Entity、DTO、VO、Service、Mapper、Controller
|
|
10
|
+
- 分页查询(PageHelper / MyBatis-Plus)
|
|
11
|
+
- 事务管理(多表操作、self 自注入)
|
|
12
|
+
- 报表 Service 模式(含数据权限、并发查询)
|
|
13
|
+
|
|
14
|
+
适用项目:
|
|
15
|
+
- leniu-tengyun-core(云食堂核心服务)
|
|
16
|
+
- leniu-yunshitang(云食堂业务服务)
|
|
17
|
+
|
|
18
|
+
触发词:CRUD、增删改查、新建模块、Business层、Service、Mapper、Controller、分页查询、LeRequest、PageDTO、PageVO、事务管理、报表Service
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# leniu CRUD 开发规范
|
|
22
|
+
|
|
23
|
+
> 完整代码模板见 `references/templates.md`
|
|
24
|
+
|
|
25
|
+
## 项目路径
|
|
26
|
+
|
|
27
|
+
| 项目 | 路径 |
|
|
28
|
+
|------|------|
|
|
29
|
+
| leniu-tengyun-core | `/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun-core` |
|
|
30
|
+
| leniu-yunshitang | `/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun/leniu-yunshitang` |
|
|
31
|
+
| 包名前缀 | `net.xnzn.core.*` |
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 架构概览
|
|
36
|
+
|
|
37
|
+
| 项 | 规范 |
|
|
38
|
+
|----|------|
|
|
39
|
+
| 架构 | Controller -> Business -> Service -> Mapper(四层) |
|
|
40
|
+
| 无 DAO 层 | Service 直接注入 Mapper |
|
|
41
|
+
| 对象转换 | `BeanUtil.copyProperties()` (Hutool) |
|
|
42
|
+
| Entity 基类 | 无基类,自定义审计字段 |
|
|
43
|
+
| 请求封装 | `LeRequest<T>` |
|
|
44
|
+
| 响应封装 | `Page<T>` / `LeResponse<T>` / `void` |
|
|
45
|
+
| 分组校验 | `InsertGroup` / `UpdateGroup` |
|
|
46
|
+
| 认证注解 | `@RequiresAuthentication` / `@RequiresGuest` |
|
|
47
|
+
| 异常类 | `LeException` |
|
|
48
|
+
| 审计字段 | crby/crtime/upby/uptime |
|
|
49
|
+
| 逻辑删除 | del_flag(1=删除, 2=正常) |
|
|
50
|
+
| 主键 | 雪花ID `Id.next()` 或自增 |
|
|
51
|
+
| Mapper XML | 与 Java 同目录(非 resources/mapper) |
|
|
52
|
+
| 验证包 | `jakarta.validation.*`(JDK 21) |
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 标准包结构
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
net.xnzn.core.[module]/
|
|
60
|
+
+-- controller/ # 按端分:web/mobile/android
|
|
61
|
+
+-- business/impl/ # 业务编排(跨 Service 协调)
|
|
62
|
+
+-- service/impl/ # 单表 CRUD、事务
|
|
63
|
+
+-- mapper/ # Mapper + XML(同目录)
|
|
64
|
+
+-- model/ # Entity
|
|
65
|
+
+-- vo/ # 响应对象
|
|
66
|
+
+-- dto/ # 请求参数
|
|
67
|
+
+-- constants/ # 枚举和常量
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 命名规范
|
|
73
|
+
|
|
74
|
+
| 类型 | 命名 | 示例 |
|
|
75
|
+
|------|------|------|
|
|
76
|
+
| Entity | `Xxx` / `XxxEntity` | `OrderInfo` |
|
|
77
|
+
| DTO | `XxxDTO` | `OrderInfoDTO` |
|
|
78
|
+
| VO | `XxxVO` | `OrderInfoVO` |
|
|
79
|
+
| Service 接口 | `XxxService` | `OrderInfoService` |
|
|
80
|
+
| Service 实现 | `XxxServiceImpl` | `OrderInfoServiceImpl` |
|
|
81
|
+
| Mapper | `XxxMapper` | `OrderInfoMapper` |
|
|
82
|
+
| Controller (Web) | `XxxWebController` | `OrderInfoWebController` |
|
|
83
|
+
| Business | `XxxWebBusiness` | `OrderWebBusiness` |
|
|
84
|
+
|
|
85
|
+
### Controller 路由前缀
|
|
86
|
+
|
|
87
|
+
| 端 | 前缀 |
|
|
88
|
+
|----|------|
|
|
89
|
+
| Web 管理端 | `/api/v2/web/{module}` |
|
|
90
|
+
| 移动端 | `/api/v2/mobile/{module}` |
|
|
91
|
+
| 设备端 | `/api/v2/android/{module}` |
|
|
92
|
+
| 开放接口 | `/api/v2/open/{module}` |
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 核心代码片段
|
|
97
|
+
|
|
98
|
+
### Entity 审计字段
|
|
99
|
+
|
|
100
|
+
```java
|
|
101
|
+
@TableField(value = "crby", fill = FieldFill.INSERT)
|
|
102
|
+
private String crby;
|
|
103
|
+
@TableField(value = "crtime", fill = FieldFill.INSERT)
|
|
104
|
+
private LocalDateTime crtime;
|
|
105
|
+
@TableField(value = "upby", fill = FieldFill.INSERT_UPDATE)
|
|
106
|
+
private String upby;
|
|
107
|
+
@TableField(value = "uptime", fill = FieldFill.INSERT_UPDATE)
|
|
108
|
+
private LocalDateTime uptime;
|
|
109
|
+
@TableField("del_flag")
|
|
110
|
+
private Integer delFlag; // 1=删除, 2=正常
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Service 注入模式
|
|
114
|
+
|
|
115
|
+
```java
|
|
116
|
+
@Slf4j
|
|
117
|
+
@Service
|
|
118
|
+
public class XxxServiceImpl implements XxxService {
|
|
119
|
+
@Resource
|
|
120
|
+
private XxxMapper xxxMapper; // 直接注入 Mapper,无 DAO 层
|
|
121
|
+
|
|
122
|
+
// 不继承 ServiceImpl,只实现接口
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Controller 请求封装
|
|
127
|
+
|
|
128
|
+
```java
|
|
129
|
+
@PostMapping("/add")
|
|
130
|
+
@RequiresAuthentication
|
|
131
|
+
public Long add(@Validated(InsertGroup.class) @RequestBody LeRequest<XxxDTO> request) {
|
|
132
|
+
return xxxService.add(request.getContent());
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@GetMapping("/get/{id}")
|
|
136
|
+
@RequiresGuest
|
|
137
|
+
public XxxVO getById(@PathVariable Long id) {
|
|
138
|
+
return xxxService.getById(id);
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 查询条件构建
|
|
143
|
+
|
|
144
|
+
```java
|
|
145
|
+
private LambdaQueryWrapper<XxxEntity> buildWrapper(XxxDTO dto) {
|
|
146
|
+
LambdaQueryWrapper<XxxEntity> wrapper = Wrappers.lambdaQuery();
|
|
147
|
+
wrapper.eq(XxxEntity::getDelFlag, 2); // 只查正常数据
|
|
148
|
+
// String -> like, 非 String -> eq/in/between
|
|
149
|
+
if (StrUtil.isNotBlank(dto.getName())) {
|
|
150
|
+
wrapper.like(XxxEntity::getName, dto.getName());
|
|
151
|
+
}
|
|
152
|
+
if (ObjectUtil.isNotNull(dto.getStatus())) {
|
|
153
|
+
wrapper.eq(XxxEntity::getStatus, dto.getStatus());
|
|
154
|
+
}
|
|
155
|
+
wrapper.orderByDesc(XxxEntity::getCrtime);
|
|
156
|
+
return wrapper;
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 对象转换与空值防护
|
|
161
|
+
|
|
162
|
+
```java
|
|
163
|
+
// 新增
|
|
164
|
+
XxxEntity entity = BeanUtil.copyProperties(dto, XxxEntity.class);
|
|
165
|
+
entity.setDelFlag(2);
|
|
166
|
+
xxxMapper.insert(entity);
|
|
167
|
+
|
|
168
|
+
// 查询判空
|
|
169
|
+
XxxEntity entity = Optional.ofNullable(xxxMapper.selectById(id))
|
|
170
|
+
.orElseThrow(() -> new LeException("记录不存在"));
|
|
171
|
+
return BeanUtil.copyProperties(entity, XxxVO.class);
|
|
172
|
+
|
|
173
|
+
// 列表空值兜底
|
|
174
|
+
List<XxxEntity> list = xxxMapper.selectList(wrapper);
|
|
175
|
+
if (CollUtil.isEmpty(list)) {
|
|
176
|
+
return Collections.emptyList();
|
|
177
|
+
}
|
|
178
|
+
return BeanUtil.copyToList(list, XxxVO.class);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 分页查询
|
|
184
|
+
|
|
185
|
+
### MyBatis-Plus 分页
|
|
186
|
+
|
|
187
|
+
```java
|
|
188
|
+
public Page<XxxVO> page(XxxDTO dto) {
|
|
189
|
+
LambdaQueryWrapper<XxxEntity> wrapper = buildWrapper(dto);
|
|
190
|
+
Page<XxxEntity> page = new Page<>(dto.getPageNum(), dto.getPageSize());
|
|
191
|
+
Page<XxxEntity> result = xxxMapper.selectPage(page, wrapper);
|
|
192
|
+
|
|
193
|
+
Page<XxxVO> voPage = new Page<>();
|
|
194
|
+
BeanUtil.copyProperties(result, voPage, "records");
|
|
195
|
+
voPage.setRecords(BeanUtil.copyToList(result.getRecords(), XxxVO.class));
|
|
196
|
+
return voPage;
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### PageHelper 分页(报表场景)
|
|
201
|
+
|
|
202
|
+
```java
|
|
203
|
+
public PageVO<XxxVO> pageList(XxxPageParam param) {
|
|
204
|
+
if (Objects.nonNull(param.getPage())) {
|
|
205
|
+
PageMethod.startPage(param.getPage()); // 传 PageDTO,紧接查询前调用
|
|
206
|
+
}
|
|
207
|
+
List<XxxVO> records = xxxMapper.pageList(param);
|
|
208
|
+
return PageVO.of(records);
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### 带合计行的分页
|
|
213
|
+
|
|
214
|
+
```java
|
|
215
|
+
public ReportBaseTotalVO<XxxVO> pageWithTotal(XxxPageParam param) {
|
|
216
|
+
MgrUserAuthPO authPO = mgrAuthApi.getUserAuthPO();
|
|
217
|
+
ReportDataPermissionParam dp = reportDataPermissionService.getDataPermission(authPO);
|
|
218
|
+
|
|
219
|
+
if (Objects.nonNull(param.getPage())) {
|
|
220
|
+
PageMethod.startPage(param.getPage());
|
|
221
|
+
}
|
|
222
|
+
List<XxxVO> list = xxxMapper.getSummaryList(param, authPO, dp);
|
|
223
|
+
XxxVO totalLine = Optional.ofNullable(xxxMapper.getSummaryTotal(param, authPO, dp))
|
|
224
|
+
.orElse(new XxxVO());
|
|
225
|
+
return new ReportBaseTotalVO<XxxVO>()
|
|
226
|
+
.setResultPage(PageVO.of(list))
|
|
227
|
+
.setTotalLine(totalLine);
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**分页关键规则**:
|
|
232
|
+
1. `PageMethod.startPage(param.getPage())` 传 PageDTO 对象,紧接查询前调用
|
|
233
|
+
2. startPage 与查询之间不能插入其他查询
|
|
234
|
+
3. Mapper 方法返回 List 即可,PageHelper 自动转换
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## 事务管理
|
|
239
|
+
|
|
240
|
+
### 多表操作必须加事务
|
|
241
|
+
|
|
242
|
+
```java
|
|
243
|
+
@Transactional(rollbackFor = Exception.class)
|
|
244
|
+
public void createOrderWithStock(OrderDTO dto) {
|
|
245
|
+
orderMapper.insert(order);
|
|
246
|
+
orderDetailMapper.insert(details);
|
|
247
|
+
stockMapper.deduct(dto.getStockId(), dto.getQuantity());
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Self 自注入(同类事务调用)
|
|
252
|
+
|
|
253
|
+
```java
|
|
254
|
+
@Slf4j
|
|
255
|
+
@Service
|
|
256
|
+
public class OrderPlaceBusiness {
|
|
257
|
+
@Autowired @Lazy
|
|
258
|
+
private OrderPlaceBusiness self; // 自注入,触发 AOP 代理
|
|
259
|
+
|
|
260
|
+
public void doSave(OrderSavePO po) {
|
|
261
|
+
self.save(po, false, false); // 通过 self 调用,@Transactional 生效
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
@Transactional(rollbackFor = Exception.class)
|
|
265
|
+
public void save(OrderSavePO po, boolean orderExists, boolean removeDetails) {
|
|
266
|
+
// 多表操作...
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**规则**:同类方法互调,被调用方有 `@Transactional` -> 必须 `self.xxx()` 而非 `this.xxx()`
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## 报表 Service 模式
|
|
276
|
+
|
|
277
|
+
```java
|
|
278
|
+
@Slf4j
|
|
279
|
+
@Service
|
|
280
|
+
public class ReportXxxService { // 无接口,直接 @Service 类
|
|
281
|
+
@Autowired private ReportXxxMapper reportXxxMapper;
|
|
282
|
+
@Autowired private MgrAuthV2Api mgrAuthApi;
|
|
283
|
+
@Autowired private ReportDataPermissionService reportDataPermissionService;
|
|
284
|
+
@Resource(name = "yunshitangTaskExecutor")
|
|
285
|
+
private AsyncTaskExecutor asyncTaskExecutor;
|
|
286
|
+
|
|
287
|
+
public ReportBaseTotalVO<XxxVO> pageXxx(XxxParam param) {
|
|
288
|
+
long start = System.currentTimeMillis();
|
|
289
|
+
MgrUserAuthPO authPO = mgrAuthApi.getUserAuthPO();
|
|
290
|
+
ReportDataPermissionParam dp = reportDataPermissionService.getDataPermission(authPO);
|
|
291
|
+
// ... 查询 + 合计行
|
|
292
|
+
log.info("pageXxx耗时:{}", System.currentTimeMillis() - start);
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**关键点**:`mgrAuthApi.getUserAuthPO()` 获取权限、`reportDataPermissionService.getDataPermission()` 获取数据权限、线程池 `yunshitangTaskExecutor`
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## 并发处理
|
|
303
|
+
|
|
304
|
+
### CompletableFuture 并行查询
|
|
305
|
+
|
|
306
|
+
```java
|
|
307
|
+
@Resource(name = "yunshitangTaskExecutor")
|
|
308
|
+
private AsyncTaskExecutor asyncTaskExecutor;
|
|
309
|
+
|
|
310
|
+
CompletableFuture<List<A>> futureA = CompletableFuture
|
|
311
|
+
.supplyAsync(() -> mapper.getTypeA(param, authPO, dp), asyncTaskExecutor);
|
|
312
|
+
CompletableFuture<List<B>> futureB = CompletableFuture
|
|
313
|
+
.supplyAsync(() -> mapper.getTypeB(param, authPO, dp), asyncTaskExecutor);
|
|
314
|
+
CompletableFuture.allOf(futureA, futureB).join();
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Redisson 分布式锁
|
|
318
|
+
|
|
319
|
+
```java
|
|
320
|
+
RLock lock = redissonClient.getLock("import:lock:" + TenantContextHolder.getTenantId());
|
|
321
|
+
if (!lock.tryLock(5, 60, TimeUnit.SECONDS)) {
|
|
322
|
+
throw new LeException("正在处理中,请稍后再试");
|
|
323
|
+
}
|
|
324
|
+
try {
|
|
325
|
+
doImport(file);
|
|
326
|
+
} finally {
|
|
327
|
+
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
|
|
328
|
+
lock.unlock();
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## 建表 SQL 模板
|
|
336
|
+
|
|
337
|
+
```sql
|
|
338
|
+
CREATE TABLE `xxx_table` (
|
|
339
|
+
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
|
340
|
+
`name` VARCHAR(100) NOT NULL COMMENT '名称',
|
|
341
|
+
`status` TINYINT(1) DEFAULT 1 COMMENT '状态(0停用 1启用)',
|
|
342
|
+
`del_flag` TINYINT(1) DEFAULT 2 COMMENT '删除标识(1删除 2正常)',
|
|
343
|
+
`revision` INT DEFAULT 0 COMMENT '乐观锁版本号',
|
|
344
|
+
`crby` VARCHAR(64) DEFAULT NULL COMMENT '创建人',
|
|
345
|
+
`crtime` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
|
346
|
+
`upby` VARCHAR(64) DEFAULT NULL COMMENT '更新人',
|
|
347
|
+
`uptime` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
|
348
|
+
PRIMARY KEY (`id`),
|
|
349
|
+
KEY `idx_status` (`status`),
|
|
350
|
+
KEY `idx_crtime` (`crtime`),
|
|
351
|
+
KEY `idx_del_flag` (`del_flag`)
|
|
352
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='XXX表';
|
|
353
|
+
-- 无需 tenant_id(双库物理隔离)
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## 禁止项速查
|
|
359
|
+
|
|
360
|
+
```java
|
|
361
|
+
// ---- 错误 ---- | ---- 正确 ----
|
|
362
|
+
package org.dromara.xxx; // -> net.xnzn.core.xxx
|
|
363
|
+
import javax.validation.Valid; // -> jakarta.validation.Valid
|
|
364
|
+
@Validated(AddGroup.class) // -> InsertGroup.class
|
|
365
|
+
private String createBy; // -> crby
|
|
366
|
+
entity.setDelFlag(0); // -> setDelFlag(2) 表示正常
|
|
367
|
+
throw new ServiceException("..."); // -> throw new LeException("...")
|
|
368
|
+
MapstructUtils.convert(src, Dst.class); // -> BeanUtil.copyProperties(src, Dst.class)
|
|
369
|
+
extends ServiceImpl<XxxMapper, Xxx> // -> implements XxxService(不继承)
|
|
370
|
+
@Resource private XxxDao xxxDao; // -> @Resource private XxxMapper xxxMapper
|
|
371
|
+
// XML 放 resources/mapper/ // -> 与 Java 同目录
|
|
372
|
+
return null; // -> return Collections.emptyList()
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## 生成前检查清单
|
|
378
|
+
|
|
379
|
+
- [ ] 包名 `net.xnzn.core.*`
|
|
380
|
+
- [ ] Service 只实现接口,不继承基类
|
|
381
|
+
- [ ] Service 直接注入 Mapper(无 DAO)
|
|
382
|
+
- [ ] 审计字段 crby/crtime/upby/uptime
|
|
383
|
+
- [ ] delFlag: 1=删除, 2=正常
|
|
384
|
+
- [ ] `BeanUtil.copyProperties()` 转换对象
|
|
385
|
+
- [ ] `jakarta.validation.*` 校验
|
|
386
|
+
- [ ] `InsertGroup` / `UpdateGroup` 分组
|
|
387
|
+
- [ ] Mapper XML 与 Java 同目录
|
|
388
|
+
- [ ] `LeException` 抛异常
|
|
389
|
+
- [ ] `@RequiresAuthentication` / `@RequiresGuest` 认证
|
|
390
|
+
- [ ] `LeRequest<T>` 请求封装
|
|
391
|
+
- [ ] 多表操作加 `@Transactional(rollbackFor = Exception.class)`
|
|
392
|
+
- [ ] 返回 List 有空集合兜底
|
|
393
|
+
- [ ] selectOne/selectById 结果判空
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## 参考代码
|
|
398
|
+
|
|
399
|
+
| 类型 | 路径 |
|
|
400
|
+
|------|------|
|
|
401
|
+
| Controller | `core-attendance/.../controller/AttendanceLeaveInfoController.java` |
|
|
402
|
+
| Service | `core-attendance/.../service/impl/AttendanceLeaveInfoServiceImpl.java` |
|
|
403
|
+
| Mapper | `core-attendance/.../mapper/AttendanceLeaveInfoMapper.java` |
|
|
404
|
+
| Entity | `core-attendance/.../model/AttendanceLeaveInfo.java` |
|