ai-engineering-init 1.16.2 → 1.16.4
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/.claude/skills/code-patterns/SKILL.md +0 -119
- package/.claude/skills/codex-code-review/SKILL.md +0 -39
- package/.claude/skills/fix-bug/SKILL.md +57 -0
- package/.claude/skills/leniu-code-patterns/SKILL.md +2 -179
- package/.claude/skills/leniu-crud-development/SKILL.md +7 -26
- package/.claude/skills/leniu-java-mybatis/SKILL.md +16 -25
- package/.claude/skills/leniu-report-scenario/references/amount-handling.md +448 -0
- package/.claude/skills/leniu-report-scenario/references/analysis-module.md +64 -0
- package/.claude/skills/leniu-report-scenario/references/customization-table-fields.md +93 -0
- package/.claude/skills/leniu-report-scenario/references/export.md +553 -0
- package/.claude/skills/leniu-report-scenario/references/mealtime.md +197 -0
- package/.claude/skills/leniu-report-scenario/references/query-param.md +274 -0
- package/.claude/skills/leniu-report-scenario/references/standard-customization.md +112 -0
- package/.claude/skills/leniu-report-scenario/references/standard-table-fields.md +113 -0
- package/.claude/skills/leniu-report-scenario/references/total-line.md +179 -0
- package/.claude/skills/loki-log-query/SKILL.md +30 -33
- package/.claude/skills/mysql-debug/SKILL.md +17 -35
- package/.codex/skills/code-patterns/SKILL.md +0 -119
- package/.codex/skills/fix-bug/SKILL.md +57 -0
- package/.codex/skills/leniu-code-patterns/SKILL.md +2 -179
- package/.codex/skills/leniu-crud-development/SKILL.md +7 -26
- package/.codex/skills/leniu-java-amount-handling/SKILL.md +461 -0
- package/.codex/skills/leniu-java-mybatis/SKILL.md +16 -25
- package/.codex/skills/leniu-report-scenario/references/amount-handling.md +448 -0
- package/.codex/skills/leniu-report-scenario/references/analysis-module.md +64 -0
- package/.codex/skills/leniu-report-scenario/references/customization-table-fields.md +93 -0
- package/.codex/skills/leniu-report-scenario/references/export.md +553 -0
- package/.codex/skills/leniu-report-scenario/references/mealtime.md +197 -0
- package/.codex/skills/leniu-report-scenario/references/query-param.md +274 -0
- package/.codex/skills/leniu-report-scenario/references/standard-customization.md +112 -0
- package/.codex/skills/leniu-report-scenario/references/standard-table-fields.md +113 -0
- package/.codex/skills/leniu-report-scenario/references/total-line.md +179 -0
- package/.codex/skills/loki-log-query/SKILL.md +55 -25
- package/.codex/skills/mysql-debug/SKILL.md +12 -6
- package/.cursor/skills/code-patterns/SKILL.md +0 -119
- package/.cursor/skills/fix-bug/SKILL.md +57 -0
- package/.cursor/skills/leniu-code-patterns/SKILL.md +2 -179
- package/.cursor/skills/leniu-crud-development/SKILL.md +7 -26
- package/.cursor/skills/leniu-java-amount-handling/SKILL.md +461 -0
- package/.cursor/skills/leniu-java-mybatis/SKILL.md +16 -25
- package/.cursor/skills/leniu-report-scenario/references/amount-handling.md +448 -0
- package/.cursor/skills/leniu-report-scenario/references/analysis-module.md +64 -0
- package/.cursor/skills/leniu-report-scenario/references/customization-table-fields.md +93 -0
- package/.cursor/skills/leniu-report-scenario/references/export.md +553 -0
- package/.cursor/skills/leniu-report-scenario/references/mealtime.md +197 -0
- package/.cursor/skills/leniu-report-scenario/references/query-param.md +274 -0
- package/.cursor/skills/leniu-report-scenario/references/standard-customization.md +112 -0
- package/.cursor/skills/leniu-report-scenario/references/standard-table-fields.md +113 -0
- package/.cursor/skills/leniu-report-scenario/references/total-line.md +179 -0
- package/.cursor/skills/loki-log-query/SKILL.md +30 -33
- package/.cursor/skills/mysql-debug/SKILL.md +20 -36
- package/CLAUDE.md +34 -0
- package/package.json +1 -1
- package/src/skills/fix-bug/SKILL.md +57 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
|
|
2
|
+
# leniu-tengyun-core 合计行(Total Line)规范
|
|
3
|
+
|
|
4
|
+
## 项目特征
|
|
5
|
+
|
|
6
|
+
| 特征 | 说明 |
|
|
7
|
+
|------|------|
|
|
8
|
+
| **包名前缀** | `net.xnzn.core.*` |
|
|
9
|
+
| **JDK 版本** | 21 |
|
|
10
|
+
| **双库切换** | 默认商户库,`Executors.doInSystem()` 切换系统库 |
|
|
11
|
+
| **分页工具** | `PageMethod.startPage()` |
|
|
12
|
+
| **结果封装** | `ReportBaseTotalVO<T>` |
|
|
13
|
+
| **分页封装** | `PageVO.of()` |
|
|
14
|
+
|
|
15
|
+
## 核心原则
|
|
16
|
+
|
|
17
|
+
**合计行SQL只返回需要合计的数值字段**,不返回非数值字段(如日期、名称、ID等)。
|
|
18
|
+
|
|
19
|
+
## Service层实现
|
|
20
|
+
|
|
21
|
+
### 带合计行的分页查询
|
|
22
|
+
|
|
23
|
+
```java
|
|
24
|
+
import net.xnzn.core.common.page.PageMethod;
|
|
25
|
+
import net.xnzn.core.common.page.PageVO;
|
|
26
|
+
import net.xnzn.core.common.vo.ReportBaseTotalVO;
|
|
27
|
+
import cn.hutool.core.collection.CollUtil;
|
|
28
|
+
|
|
29
|
+
// ⚠️ 系统默认在商户库执行,业务查询无需 Executors.readInSystem()
|
|
30
|
+
// Executors.readInSystem() 仅用于需要访问系统库的场景(如全局配置、商户管理)
|
|
31
|
+
|
|
32
|
+
public ReportBaseTotalVO<XxxVO> pageWithTotal(XxxPageParam param) {
|
|
33
|
+
ReportBaseTotalVO<XxxVO> result = new ReportBaseTotalVO<>();
|
|
34
|
+
|
|
35
|
+
// 1. 导出时不查询合计行(避免不必要的性能开销)
|
|
36
|
+
if (CollUtil.isEmpty(param.getExportCols())) {
|
|
37
|
+
XxxVO totalLine = mapper.getSummaryTotal(param);
|
|
38
|
+
result.setTotalLine(totalLine);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 2. 开启分页
|
|
42
|
+
PageMethod.startPage(param.getPage());
|
|
43
|
+
|
|
44
|
+
// 3. 查询数据
|
|
45
|
+
List<XxxVO> list = mapper.getSummaryList(param);
|
|
46
|
+
|
|
47
|
+
// 4. 封装分页结果
|
|
48
|
+
result.setResultPage(PageVO.of(list));
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 单独的合计行查询方法
|
|
54
|
+
|
|
55
|
+
```java
|
|
56
|
+
public XxxVO getSummaryTotal(XxxPageParam param) {
|
|
57
|
+
// 默认在商户库执行,无需 Executors 包装
|
|
58
|
+
return mapper.getSummaryTotal(param);
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Mapper XML规范
|
|
63
|
+
|
|
64
|
+
### 错误示例:返回非数值字段
|
|
65
|
+
|
|
66
|
+
```xml
|
|
67
|
+
<!-- ❌ 不要这样做 -->
|
|
68
|
+
<select id="getSummaryTotal" resultType="XxxVO">
|
|
69
|
+
SELECT
|
|
70
|
+
'合计' AS dateMonth, <!-- ❌ 不要返回字符串 -->
|
|
71
|
+
NULL AS schoolId, <!-- ❌ 不要返回ID -->
|
|
72
|
+
NULL AS schoolName, <!-- ❌ 不要返回名称 -->
|
|
73
|
+
SUM(staffCount) AS staffCount,
|
|
74
|
+
SUM(amount) AS amount
|
|
75
|
+
FROM xxx_table
|
|
76
|
+
</select>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 正确示例:只返回数值字段
|
|
80
|
+
|
|
81
|
+
```xml
|
|
82
|
+
<!-- ✅ 正确做法 -->
|
|
83
|
+
<select id="getSummaryTotal" resultType="XxxVO">
|
|
84
|
+
SELECT
|
|
85
|
+
SUM(staff_count) AS staffCount,
|
|
86
|
+
SUM(basic_salary) AS basicSalary,
|
|
87
|
+
SUM(overtime_salary) AS overtimeSalary,
|
|
88
|
+
SUM(personal_actual_amount) AS personalActualAmount,
|
|
89
|
+
CASE
|
|
90
|
+
WHEN SUM(staff_count) = 0 THEN 0
|
|
91
|
+
ELSE SUM(avg_salary) / COUNT(DISTINCT tenant_id)
|
|
92
|
+
END AS avgSalary
|
|
93
|
+
FROM xxx_table
|
|
94
|
+
<where>
|
|
95
|
+
del_flag = 2
|
|
96
|
+
<if test="startDate != null">
|
|
97
|
+
AND crtime >= #{startDate}
|
|
98
|
+
</if>
|
|
99
|
+
<if test="endDate != null">
|
|
100
|
+
AND crtime <= #{endDate}
|
|
101
|
+
</if>
|
|
102
|
+
</where>
|
|
103
|
+
</select>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## 常见合计字段类型
|
|
107
|
+
|
|
108
|
+
| 字段类型 | 示例 | 合计方式 |
|
|
109
|
+
|---------|------|---------|
|
|
110
|
+
| 数量 | `staff_count`, `order_count` | `SUM()` |
|
|
111
|
+
| 金额 | `amount`, `salary` | `SUM()` |
|
|
112
|
+
| 百分比 | `discount_rate` | `AVG()` 或 `SUM() / COUNT()` |
|
|
113
|
+
| 计数 | `COUNT(DISTINCT id)` | 直接使用 |
|
|
114
|
+
|
|
115
|
+
## 特殊处理:平均值
|
|
116
|
+
|
|
117
|
+
```xml
|
|
118
|
+
<!-- 简单平均值 -->
|
|
119
|
+
SELECT AVG(amount) AS avgAmount
|
|
120
|
+
|
|
121
|
+
<!-- 加权平均值 -->
|
|
122
|
+
SELECT
|
|
123
|
+
CASE
|
|
124
|
+
WHEN SUM(count) = 0 THEN 0
|
|
125
|
+
ELSE SUM(amount * count) / SUM(count)
|
|
126
|
+
END AS weightedAvgAmount
|
|
127
|
+
|
|
128
|
+
<!-- 按维度平均 -->
|
|
129
|
+
SELECT
|
|
130
|
+
CASE
|
|
131
|
+
WHEN COUNT(DISTINCT tenant_id) = 0 THEN 0
|
|
132
|
+
ELSE SUM(total_amount) / COUNT(DISTINCT tenant_id)
|
|
133
|
+
END AS avgByTenant
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Controller层实现
|
|
137
|
+
|
|
138
|
+
```java
|
|
139
|
+
@PostMapping("/page")
|
|
140
|
+
@ApiOperation("分页查询(带合计)")
|
|
141
|
+
@RequiresAuthentication
|
|
142
|
+
public ReportBaseTotalVO<XxxVO> page(@RequestBody LeRequest<XxxParam> request) {
|
|
143
|
+
XxxParam param = request.getContent();
|
|
144
|
+
return xxxService.pageWithTotal(param);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@GetMapping("/total")
|
|
148
|
+
@ApiOperation("单独查询合计行")
|
|
149
|
+
@RequiresAuthentication
|
|
150
|
+
public XxxVO getTotal(XxxParam param) {
|
|
151
|
+
return xxxService.getSummaryTotal(param);
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## 导出时合计行处理
|
|
156
|
+
|
|
157
|
+
```java
|
|
158
|
+
public void exportExcel(XxxParam param, HttpServletResponse response) {
|
|
159
|
+
// 导出时需要查询合计行
|
|
160
|
+
XxxVO totalLine = mapper.getSummaryTotal(param);
|
|
161
|
+
|
|
162
|
+
// 分页查询所有数据(不分页)
|
|
163
|
+
param.getPage().setSize(Integer.MAX_VALUE);
|
|
164
|
+
List<XxxVO> list = mapper.getSummaryList(param);
|
|
165
|
+
|
|
166
|
+
// 将合计行添加到列表末尾
|
|
167
|
+
list.add(totalLine);
|
|
168
|
+
|
|
169
|
+
// 导出
|
|
170
|
+
exportService.export(list, response);
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## 注意事项
|
|
175
|
+
|
|
176
|
+
- 合计SQL只返回数值字段,不返回字符串、ID、名称等
|
|
177
|
+
- 业务查询默认在商户库执行,无需 `Executors` 包装;仅访问系统库数据时才使用 `Executors.doInSystem()`
|
|
178
|
+
- 导出时通过 `exportCols` 判断是否需要合计行
|
|
179
|
+
- 金额字段类型与 Entity 保持一致:订单模块用 `BigDecimal`,钱包模块用 `Long`(详见 leniu-java-amount-handling)
|
|
@@ -25,9 +25,35 @@ description: |
|
|
|
25
25
|
|
|
26
26
|
## 多环境配置
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
### 配置文件查找顺序
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
```
|
|
31
|
+
1. 本地项目:.claude/loki-config.json(新格式)
|
|
32
|
+
2. 本地项目:.claude/skills/loki-log-query/environments.json(旧格式,兼容)
|
|
33
|
+
3. 全局配置:~/.claude/loki-config.json(或 ~/.cursor/loki-config.json)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
本地优先。全局配置用 `npx ai-engineering-init config --type loki --scope global` 创建。
|
|
37
|
+
|
|
38
|
+
### 配置结构(支持 range 范围匹配)
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"active": "monitor-dev",
|
|
43
|
+
"environments": {
|
|
44
|
+
"monitor-dev": {
|
|
45
|
+
"name": "Monitor 开发环境",
|
|
46
|
+
"url": "https://monitor-dev.xnzn.net/grafana",
|
|
47
|
+
"token": "glsa_xxx",
|
|
48
|
+
"aliases": ["mdev", "dev"],
|
|
49
|
+
"range": "dev1~15",
|
|
50
|
+
"projects": ["dev01","dev02","...","dev15"]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 环境列表(默认)
|
|
31
57
|
|
|
32
58
|
| 环境别名 | 名称 | URL | 快捷词 |
|
|
33
59
|
|----------|------|-----|--------|
|
|
@@ -37,31 +63,34 @@ description: |
|
|
|
37
63
|
| `monitor02-dev` | Monitor02 开发环境 | `https://monitor02-dev.xnzn.net/grafana` | m02, monitor02 |
|
|
38
64
|
| `monitor-tyy-dev` | Monitor 体验园开发环境 | `https://monitor-tyy-dev.xnzn.net/grafana` | tyy, 体验园 |
|
|
39
65
|
|
|
40
|
-
###
|
|
66
|
+
### 环境匹配规则(含 range)
|
|
41
67
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
68
|
+
| 用户说法 | 匹配方式 | 结果 |
|
|
69
|
+
|---------|---------|------|
|
|
70
|
+
| "查 test13 的日志" | 精确匹配 key/aliases | `test13` 环境 |
|
|
71
|
+
| "去 dev10 查" | **range 匹配**:dev10 在 monitor-dev 的 projects 中 | `monitor-dev`,`project="dev10"` |
|
|
72
|
+
| "去 monitor-dev 查" | 精确匹配 key | `monitor-dev` 环境 |
|
|
73
|
+
| "切到体验园" | aliases 匹配 | `monitor-tyy-dev` 环境 |
|
|
74
|
+
| 未指定环境 | 使用 `active` 字段 | 默认活跃环境 |
|
|
48
75
|
|
|
49
|
-
###
|
|
76
|
+
### 读取配置(含全局降级 + range 匹配)
|
|
50
77
|
|
|
51
78
|
```bash
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
79
|
+
# 按优先级查找配置文件
|
|
80
|
+
find_config() {
|
|
81
|
+
local PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
|
|
82
|
+
for f in \
|
|
83
|
+
"${PROJECT_DIR}/.claude/loki-config.json" \
|
|
84
|
+
"${PROJECT_DIR}/.claude/skills/loki-log-query/environments.json" \
|
|
85
|
+
"${HOME}/.claude/loki-config.json" \
|
|
86
|
+
"${HOME}/.cursor/loki-config.json"; do
|
|
87
|
+
[ -f "$f" ] && echo "$f" && return
|
|
88
|
+
done
|
|
62
89
|
}
|
|
63
90
|
|
|
64
|
-
|
|
91
|
+
ENV_FILE=$(find_config)
|
|
92
|
+
|
|
93
|
+
# 通过别名或 range 查找环境 key + project
|
|
65
94
|
find_env() {
|
|
66
95
|
python3 -c "
|
|
67
96
|
import json
|
|
@@ -69,10 +98,11 @@ data = json.load(open('${ENV_FILE}'))
|
|
|
69
98
|
alias = '${1}'.lower()
|
|
70
99
|
for key, env in data['environments'].items():
|
|
71
100
|
if alias == key or alias in env.get('aliases', []):
|
|
72
|
-
print(key)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
101
|
+
print(f'{key}|'); exit()
|
|
102
|
+
for key, env in data['environments'].items():
|
|
103
|
+
if alias in env.get('projects', []):
|
|
104
|
+
print(f'{key}|{alias}'); exit()
|
|
105
|
+
print(f'{data[\"active\"]}|')
|
|
76
106
|
"
|
|
77
107
|
}
|
|
78
108
|
```
|
|
@@ -27,22 +27,28 @@ description: |
|
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
-
##
|
|
30
|
+
## 连接信息获取(四级降级,自动查找)
|
|
31
31
|
|
|
32
32
|
当本技能被激活时,**按以下优先级获取数据库连接信息**:
|
|
33
33
|
|
|
34
34
|
### 优先级 1:用户对话中指定(最高优先级)
|
|
35
35
|
|
|
36
36
|
用户直接给出连接信息,或指定环境名:
|
|
37
|
-
- "连 dev 环境查一下" →
|
|
37
|
+
- "连 dev 环境查一下" → 使用配置中 dev 环境
|
|
38
|
+
- "去 dev10 查" → **范围匹配** dev 环境(range: "1~15")
|
|
38
39
|
- 直接给出 host/port/user/password → 直接使用
|
|
39
40
|
|
|
40
|
-
### 优先级 2
|
|
41
|
+
### 优先级 2:本地项目 `.claude/mysql-config.json`
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
**此文件为可选**,主要用于连接非本地环境(dev/prod 远程数据库)。
|
|
43
|
+
项目目录下的配置文件,优先于全局配置。
|
|
44
44
|
|
|
45
|
-
### 优先级 3
|
|
45
|
+
### 优先级 3:全局配置(所有项目共享)
|
|
46
|
+
|
|
47
|
+
全局配置文件(通过 `npx ai-engineering-init config --scope global` 创建)。
|
|
48
|
+
查找路径:`~/.claude/mysql-config.json` 或 `~/.cursor/mysql-config.json`(取决于使用的工具)。
|
|
49
|
+
**推荐**:公司统一的数据库连接信息放全局,项目特定的覆盖放本地。
|
|
50
|
+
|
|
51
|
+
### 优先级 4:工程配置文件(零配置,本地开发默认)
|
|
46
52
|
|
|
47
53
|
从项目的 `bootstrap-dev.yml` 中自动提取连接信息:
|
|
48
54
|
|
|
@@ -101,120 +101,6 @@ feat(order): 新增订单导出功能
|
|
|
101
101
|
Closes #123
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
## 数据类型规范
|
|
105
|
-
|
|
106
|
-
### 布尔语义字段必须使用 Boolean
|
|
107
|
-
|
|
108
|
-
```java
|
|
109
|
-
// ❌ 错误
|
|
110
|
-
private Integer ifNarrow;
|
|
111
|
-
private Integer isEnabled;
|
|
112
|
-
|
|
113
|
-
// ✅ 正确
|
|
114
|
-
private Boolean narrow; // getter 自动生成 isNarrow()
|
|
115
|
-
private Boolean enabled; // getter 自动生成 isEnabled()
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
**规则**:
|
|
119
|
-
- 语义为"是/否"的字段,类型必须为 `Boolean`
|
|
120
|
-
- 字段名不加 `if`/`is`/`has` 前缀(JavaBean 规范中 `Boolean` 的 getter 自动生成 `isXxx()`)
|
|
121
|
-
- 数据库字段使用 `TINYINT(1)` 或 `BIT(1)`
|
|
122
|
-
|
|
123
|
-
### 枚举字段必须提供明确约束
|
|
124
|
-
|
|
125
|
-
```java
|
|
126
|
-
// ❌ 错误:调用方无法知道合法值
|
|
127
|
-
@ApiModelProperty(value = "操作类型")
|
|
128
|
-
private Integer tradeType;
|
|
129
|
-
|
|
130
|
-
// ✅ 方案一:VO/DTO 层直接用枚举(推荐)
|
|
131
|
-
@ApiModelProperty(value = "操作类型")
|
|
132
|
-
private AccTradeTypeEnum tradeType;
|
|
133
|
-
|
|
134
|
-
// ✅ 方案二:保留 Integer 但标注合法值
|
|
135
|
-
@ApiModelProperty(value = "操作类型:1-充值 2-消费 3-退款", allowableValues = "1,2,3")
|
|
136
|
-
private Integer tradeType;
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### 金额字段禁止使用浮点类型
|
|
140
|
-
|
|
141
|
-
```java
|
|
142
|
-
// ❌ 错误
|
|
143
|
-
private Double amount;
|
|
144
|
-
private Float price;
|
|
145
|
-
|
|
146
|
-
// ✅ 正确:Entity/Service 层用 Long(分),VO 层展示用 BigDecimal(元)
|
|
147
|
-
private Long amountFen;
|
|
148
|
-
private BigDecimal amountYuan;
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### 原始类型 vs 包装类型
|
|
152
|
-
|
|
153
|
-
| 场景 | 用原始类型 | 用包装类型 |
|
|
154
|
-
|------|----------|----------|
|
|
155
|
-
| Entity / VO / DTO 字段 | — | ✅ 统一用包装类型 |
|
|
156
|
-
| 方法参数(不允许 null) | ✅ `int count` | — |
|
|
157
|
-
| 方法参数(允许 null) | — | ✅ `Integer count` |
|
|
158
|
-
| 局部变量 | ✅ `int i = 0` | — |
|
|
159
|
-
|
|
160
|
-
## Optional 使用规范
|
|
161
|
-
|
|
162
|
-
```java
|
|
163
|
-
// ❌ 错误:of() 不接受 null,value 为 null 直接 NPE
|
|
164
|
-
Optional.of(value).orElse(defaultValue);
|
|
165
|
-
|
|
166
|
-
// ✅ 正确:ofNullable() 安全处理 null
|
|
167
|
-
Optional.ofNullable(value).orElse(defaultValue);
|
|
168
|
-
|
|
169
|
-
// ❌ 禁止:Optional 作为方法参数或类字段
|
|
170
|
-
public void process(Optional<String> name) { ... }
|
|
171
|
-
private Optional<String> name;
|
|
172
|
-
|
|
173
|
-
// ✅ 允许:Optional 作为方法返回值、链式处理
|
|
174
|
-
public Optional<Entity> findById(Long id) { ... }
|
|
175
|
-
Optional.ofNullable(entity).map(Entity::getConfig).orElse(DEFAULT_VALUE);
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
## @Transactional 规范
|
|
179
|
-
|
|
180
|
-
```java
|
|
181
|
-
// ❌ 错误:默认只回滚 RuntimeException
|
|
182
|
-
@Transactional
|
|
183
|
-
public void createOrder() { ... }
|
|
184
|
-
|
|
185
|
-
// ✅ 正确:显式指定回滚异常
|
|
186
|
-
@Transactional(rollbackFor = Exception.class)
|
|
187
|
-
public void createOrder() { ... }
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
- 所有 `@Transactional` 必须显式写 `rollbackFor = Exception.class`
|
|
191
|
-
- 只读查询不加 `@Transactional`(或用 `readOnly = true`)
|
|
192
|
-
- 事务方法不要 try-catch 吞掉异常,否则事务不回滚
|
|
193
|
-
|
|
194
|
-
## TODO 管理规范
|
|
195
|
-
|
|
196
|
-
```java
|
|
197
|
-
// ❌ 错误:无负责人、无日期、无跟踪
|
|
198
|
-
// TODO 修改一下
|
|
199
|
-
|
|
200
|
-
// ✅ 正确:完整 TODO 格式
|
|
201
|
-
// TODO(@陈沈杰, 2026-03-20, #TASK-1234): 移动端 AppId 赋值逻辑待产品确认
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
- 每个 TODO 必须有对应的任务号
|
|
205
|
-
- 超过 2 个迭代未处理的 TODO 必须清理
|
|
206
|
-
- 不用的代码直接删除,不要注释保留
|
|
207
|
-
|
|
208
|
-
## 代码格式化规范
|
|
209
|
-
|
|
210
|
-
| 项目 | 规范 |
|
|
211
|
-
|------|------|
|
|
212
|
-
| 缩进 | 4 个空格(不用 Tab) |
|
|
213
|
-
| 行宽 | 120 字符 |
|
|
214
|
-
| 大括号 | K&R 风格(同行开始) |
|
|
215
|
-
| 空行 | 方法间 1 个空行,逻辑块间 1 个空行 |
|
|
216
|
-
| import | 分组排序:java → jakarta → org → net → com,组间空行 |
|
|
217
|
-
|
|
218
104
|
## 代码示例
|
|
219
105
|
|
|
220
106
|
### 统一响应格式
|
|
@@ -275,8 +161,3 @@ public enum OrderStatusEnum {
|
|
|
275
161
|
| Git 提交信息写"fix bug" | 写清楚修了什么:`fix(order): 修复金额计算精度丢失` |
|
|
276
162
|
| Boolean 变量名:`flag` | 有意义的名字:`isActive`, `hasPermission` |
|
|
277
163
|
| 缩写命名:`usr`, `mgr` | 完整命名:`user`, `manager` |
|
|
278
|
-
| `Optional.of(可能null值)` | `Optional.ofNullable(value)` |
|
|
279
|
-
| `@Transactional` 无 rollbackFor | `@Transactional(rollbackFor = Exception.class)` |
|
|
280
|
-
| TODO 无负责人和日期 | `// TODO(@负责人, 日期, #任务号): 描述` |
|
|
281
|
-
| 布尔字段用 `Integer` | 用 `Boolean` 类型 |
|
|
282
|
-
| 枚举字段无合法值说明 | `@ApiModelProperty` 标注合法值或直接用枚举类型 |
|
|
@@ -197,6 +197,62 @@ Agent(mysql-runner, "根据日志发现涉及 order_info 表,查询 id=xxx 的
|
|
|
197
197
|
| Bug 描述 + DB 信息 | bug-analyzer + mysql-runner | — |
|
|
198
198
|
| Bug 描述 + traceId + DB 信息 | 全部 | 日志中有新表/ID → mysql-runner 追加查询 |
|
|
199
199
|
|
|
200
|
+
## SQL 报表 Bug 专项流程
|
|
201
|
+
|
|
202
|
+
当 Bug 涉及 **SQL 查询逻辑错误**(SUM/GROUP BY/CASE WHEN/金额计算等),启用以下验证流程:
|
|
203
|
+
|
|
204
|
+
### 修复前:建立基线
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
1. 读取当前 Mapper XML,理解现有 SQL 逻辑
|
|
208
|
+
2. 如果有已修复的参考查询(如同模块的明细查询),先读取其 SQL 逻辑作为正确参考
|
|
209
|
+
3. 查库获取当前错误结果作为"修复前基线"(可选,用户提供 DB 时执行)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### 修复后:交叉验证(必须执行)
|
|
213
|
+
|
|
214
|
+
**两步验证法**(以本项目销售汇总修复为典型案例):
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
步骤 1:直接执行修改后的 SQL,验证基本逻辑
|
|
218
|
+
- 检查各记录类型是否正确处理(称重/按份、正向/逆向、入库/出库)
|
|
219
|
+
- 检查特殊值:NULL 处理、除零保护、边界条件
|
|
220
|
+
|
|
221
|
+
步骤 2:交叉对比验证
|
|
222
|
+
- 方法 A:用已修复的明细查询做 SUM 汇总
|
|
223
|
+
- 方法 B:执行修改后的汇总查询
|
|
224
|
+
- 对比 A vs B:总计必须完全一致
|
|
225
|
+
- 按维度对比(按商品/按门店等):逐行 MATCH/DIFF
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### 验证 SQL 模板
|
|
229
|
+
|
|
230
|
+
```sql
|
|
231
|
+
-- 交叉验证:明细汇总 vs 汇总查询
|
|
232
|
+
SELECT '明细汇总' as source, SUM(saleNum), SUM(cost), ...
|
|
233
|
+
FROM (/* 明细查询逐行 */) detail
|
|
234
|
+
UNION ALL
|
|
235
|
+
SELECT '汇总查询' as source, ...
|
|
236
|
+
FROM /* 汇总查询 */;
|
|
237
|
+
|
|
238
|
+
-- 按维度逐行对比
|
|
239
|
+
SELECT COALESCE(d.name, s.name) as name,
|
|
240
|
+
d.detail_value, s.summary_value,
|
|
241
|
+
CASE WHEN d.detail_value = s.summary_value THEN 'MATCH' ELSE 'DIFF' END as result
|
|
242
|
+
FROM (/* 明细按维度 GROUP BY */) d
|
|
243
|
+
LEFT JOIN (/* 汇总按维度 GROUP BY */) s ON d.id = s.id;
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### SQL 修改注意事项
|
|
247
|
+
|
|
248
|
+
| 规则 | 说明 |
|
|
249
|
+
|------|------|
|
|
250
|
+
| **保留 WHERE 条件** | 新增/修改查询必须包含与现有查询相同的过滤条件 |
|
|
251
|
+
| **GROUP BY 优先** | 优先添加字段到 GROUP BY,不用 ANY_VALUE() |
|
|
252
|
+
| **JOIN 一致性** | 主查询和合计查询的 JOIN 必须一致(如 drp_unit) |
|
|
253
|
+
| **IFNULL 保护** | 除法运算中的分母必须用 IFNULL 防止除零 |
|
|
254
|
+
| **业务类型分支** | CASE WHEN 必须覆盖所有业务类型(称重/按份、入库/出库等) |
|
|
255
|
+
|
|
200
256
|
## 提交规则
|
|
201
257
|
|
|
202
258
|
修复完成后,**必须调用 `Skill(git-workflow)` 再执行 git 操作**。
|
|
@@ -210,3 +266,4 @@ Agent(mysql-runner, "根据日志发现涉及 order_info 表,查询 id=xxx 的
|
|
|
210
266
|
- 简单 Bug 不要过度编排,直接修就行(但仍需先报告再修复)
|
|
211
267
|
- 如果用户没提供 DB/Loki 信息但 Bug 涉及数据问题,主动询问
|
|
212
268
|
- 与 `bug-detective` 技能的区别:`bug-detective` 是排查指南,`fix-bug` 是全流程编排(包含排查+修复+提交)
|
|
269
|
+
- SQL 报表 Bug 修复后**必须执行交叉验证**,不能只看"能跑"就提交
|