ai-engineering-init 1.6.0 → 1.7.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.
Files changed (108) hide show
  1. package/.claude/agents/code-reviewer.md +3 -130
  2. package/.claude/hooks/skill-forced-eval.js +2 -0
  3. package/.claude/hooks/stop.js +24 -1
  4. package/.claude/skills/codex-code-review/SKILL.md +327 -0
  5. package/.claude/skills/leniu-report-customization/SKILL.md +82 -2
  6. package/.claude/skills/leniu-report-standard-customization/SKILL.md +65 -2
  7. package/.claude/skills/loki-log-query/SKILL.md +400 -0
  8. package/.claude/skills/mysql-debug/SKILL.md +58 -22
  9. package/.claude/skills/skill-creator/LICENSE.txt +202 -0
  10. package/.claude/skills/skill-creator/SKILL.md +479 -0
  11. package/.claude/skills/skill-creator/agents/analyzer.md +274 -0
  12. package/.claude/skills/skill-creator/agents/comparator.md +202 -0
  13. package/.claude/skills/skill-creator/agents/grader.md +223 -0
  14. package/.claude/skills/skill-creator/assets/eval_review.html +146 -0
  15. package/.claude/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  16. package/.claude/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  17. package/.claude/skills/skill-creator/references/schemas.md +430 -0
  18. package/.claude/skills/skill-creator/scripts/__init__.py +0 -0
  19. package/.claude/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  20. package/.claude/skills/skill-creator/scripts/generate_report.py +326 -0
  21. package/.claude/skills/skill-creator/scripts/improve_description.py +248 -0
  22. package/.claude/skills/skill-creator/scripts/package_skill.py +136 -0
  23. package/.claude/skills/skill-creator/scripts/quick_validate.py +103 -0
  24. package/.claude/skills/skill-creator/scripts/run_eval.py +310 -0
  25. package/.claude/skills/skill-creator/scripts/run_loop.py +332 -0
  26. package/.claude/skills/skill-creator/scripts/utils.py +47 -0
  27. package/.claude/skills/sync-back-merge/SKILL.md +66 -0
  28. package/.claude/skills/yunxiao-task-management/SKILL.md +489 -0
  29. package/.codex/skills/leniu-report-customization/SKILL.md +82 -2
  30. package/.codex/skills/leniu-report-standard-customization/SKILL.md +65 -2
  31. package/.codex/skills/loki-log-query/SKILL.md +400 -0
  32. package/.codex/skills/loki-log-query/environments.json +45 -0
  33. package/.codex/skills/mysql-debug/SKILL.md +58 -22
  34. package/.codex/skills/skill-creator/LICENSE.txt +202 -0
  35. package/.codex/skills/skill-creator/SKILL.md +479 -0
  36. package/.codex/skills/skill-creator/agents/analyzer.md +274 -0
  37. package/.codex/skills/skill-creator/agents/comparator.md +202 -0
  38. package/.codex/skills/skill-creator/agents/grader.md +223 -0
  39. package/.codex/skills/skill-creator/assets/eval_review.html +146 -0
  40. package/.codex/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  41. package/.codex/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  42. package/.codex/skills/skill-creator/references/schemas.md +430 -0
  43. package/.codex/skills/skill-creator/scripts/__init__.py +0 -0
  44. package/.codex/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  45. package/.codex/skills/skill-creator/scripts/generate_report.py +326 -0
  46. package/.codex/skills/skill-creator/scripts/improve_description.py +248 -0
  47. package/.codex/skills/skill-creator/scripts/package_skill.py +136 -0
  48. package/.codex/skills/skill-creator/scripts/quick_validate.py +103 -0
  49. package/.codex/skills/skill-creator/scripts/run_eval.py +310 -0
  50. package/.codex/skills/skill-creator/scripts/run_loop.py +332 -0
  51. package/.codex/skills/skill-creator/scripts/utils.py +47 -0
  52. package/.codex/skills/sync-back-merge/SKILL.md +66 -0
  53. package/.codex/skills/yunxiao-task-management/SKILL.md +489 -0
  54. package/.cursor/hooks/stop.js +23 -1
  55. package/.cursor/skills/leniu-report-customization/SKILL.md +82 -2
  56. package/.cursor/skills/leniu-report-standard-customization/SKILL.md +65 -2
  57. package/.cursor/skills/loki-log-query/SKILL.md +400 -0
  58. package/.cursor/skills/loki-log-query/environments.json +45 -0
  59. package/.cursor/skills/mysql-debug/SKILL.md +58 -22
  60. package/.cursor/skills/skill-creator/LICENSE.txt +202 -0
  61. package/.cursor/skills/skill-creator/SKILL.md +479 -0
  62. package/.cursor/skills/skill-creator/agents/analyzer.md +274 -0
  63. package/.cursor/skills/skill-creator/agents/comparator.md +202 -0
  64. package/.cursor/skills/skill-creator/agents/grader.md +223 -0
  65. package/.cursor/skills/skill-creator/assets/eval_review.html +146 -0
  66. package/.cursor/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  67. package/.cursor/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  68. package/.cursor/skills/skill-creator/references/schemas.md +430 -0
  69. package/.cursor/skills/skill-creator/scripts/__init__.py +0 -0
  70. package/.cursor/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  71. package/.cursor/skills/skill-creator/scripts/generate_report.py +326 -0
  72. package/.cursor/skills/skill-creator/scripts/improve_description.py +248 -0
  73. package/.cursor/skills/skill-creator/scripts/package_skill.py +136 -0
  74. package/.cursor/skills/skill-creator/scripts/quick_validate.py +103 -0
  75. package/.cursor/skills/skill-creator/scripts/run_eval.py +310 -0
  76. package/.cursor/skills/skill-creator/scripts/run_loop.py +332 -0
  77. package/.cursor/skills/skill-creator/scripts/utils.py +47 -0
  78. package/.cursor/skills/sync-back-merge/SKILL.md +66 -0
  79. package/.cursor/skills/yunxiao-task-management/SKILL.md +489 -0
  80. package/bin/index.js +606 -24
  81. package/package.json +1 -1
  82. package/src/platform-map.json +4 -0
  83. package/src/skills/codex-code-review/SKILL.md +261 -69
  84. package/src/skills/leniu-report-customization/SKILL.md +82 -2
  85. package/src/skills/leniu-report-standard-customization/SKILL.md +65 -2
  86. package/src/skills/loki-log-query/SKILL.md +400 -0
  87. package/src/skills/loki-log-query/environments.json +45 -0
  88. package/src/skills/mysql-debug/SKILL.md +58 -22
  89. package/src/skills/skill-creator/LICENSE.txt +202 -0
  90. package/src/skills/skill-creator/SKILL.md +479 -0
  91. package/src/skills/skill-creator/agents/analyzer.md +274 -0
  92. package/src/skills/skill-creator/agents/comparator.md +202 -0
  93. package/src/skills/skill-creator/agents/grader.md +223 -0
  94. package/src/skills/skill-creator/assets/eval_review.html +146 -0
  95. package/src/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  96. package/src/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  97. package/src/skills/skill-creator/references/schemas.md +430 -0
  98. package/src/skills/skill-creator/scripts/__init__.py +0 -0
  99. package/src/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  100. package/src/skills/skill-creator/scripts/generate_report.py +326 -0
  101. package/src/skills/skill-creator/scripts/improve_description.py +248 -0
  102. package/src/skills/skill-creator/scripts/package_skill.py +136 -0
  103. package/src/skills/skill-creator/scripts/quick_validate.py +103 -0
  104. package/src/skills/skill-creator/scripts/run_eval.py +310 -0
  105. package/src/skills/skill-creator/scripts/run_loop.py +332 -0
  106. package/src/skills/skill-creator/scripts/utils.py +47 -0
  107. package/src/skills/sync-back-merge/SKILL.md +66 -0
  108. package/src/skills/yunxiao-task-management/SKILL.md +489 -0
@@ -286,7 +286,69 @@ public ReportBaseTotalVO<XxxVO> pageSummary(XxxParam param) {
286
286
 
287
287
  ---
288
288
 
289
- ## 九、开发检查清单
289
+ ## 九、MySQL only_full_group_by 规范(必须遵守)
290
+
291
+ > MySQL 默认开启 `sql_mode=ONLY_FULL_GROUP_BY`,SELECT 中所有非聚合列必须出现在 GROUP BY 中,且 GROUP BY 表达式必须与 SELECT 表达式**完全一致**。
292
+
293
+ ### 核心规则
294
+
295
+ **SELECT 的表达式 == GROUP BY 的表达式**(字符级别完全一致)
296
+
297
+ ### ❌ 错误示例(GROUP BY 与 SELECT 不一致)
298
+
299
+ ```xml
300
+ <!-- 报错:Expression #1 of SELECT list is not in GROUP BY clause -->
301
+ SELECT
302
+ DATE_FORMAT(roi.consume_time, '%Y-%m-%d') AS statisticDate,
303
+ SUM(roi.real_amount) AS totalAmount
304
+ FROM report_order_info roi
305
+ GROUP BY DATE(roi.consume_time) <!-- ❌ DATE() ≠ DATE_FORMAT(..., '%Y-%m-%d') -->
306
+ ORDER BY DATE(roi.consume_time)
307
+ ```
308
+
309
+ ### ✅ 正确示例(GROUP BY 与 SELECT 完全一致)
310
+
311
+ ```xml
312
+ SELECT
313
+ DATE_FORMAT(roi.consume_time, '%Y-%m-%d') AS statisticDate,
314
+ SUM(roi.real_amount) AS totalAmount
315
+ FROM report_order_info roi
316
+ GROUP BY DATE_FORMAT(roi.consume_time, '%Y-%m-%d') <!-- ✅ 与 SELECT 完全一致 -->
317
+ ORDER BY DATE_FORMAT(roi.consume_time, '%Y-%m-%d') <!-- ✅ ORDER BY 也保持一致 -->
318
+ ```
319
+
320
+ ### ❌ 错误示例(SELECT 含非聚合列未加入 GROUP BY)
321
+
322
+ ```xml
323
+ SELECT
324
+ roi.canteen_id,
325
+ roi.canteen_name, <!-- ❌ 非聚合列未在 GROUP BY 中 -->
326
+ SUM(roi.real_amount) AS totalAmount
327
+ FROM report_order_info roi
328
+ GROUP BY roi.canteen_id
329
+ ```
330
+
331
+ ### ✅ 正确示例(所有非聚合列都在 GROUP BY 中)
332
+
333
+ ```xml
334
+ SELECT
335
+ roi.canteen_id,
336
+ roi.canteen_name,
337
+ SUM(roi.real_amount) AS totalAmount
338
+ FROM report_order_info roi
339
+ GROUP BY roi.canteen_id, roi.canteen_name <!-- ✅ 所有非聚合列都在 GROUP BY -->
340
+ ```
341
+
342
+ ### 检查清单
343
+
344
+ - [ ] SELECT 中按日期分组时使用 `DATE_FORMAT(col, '%Y-%m-%d')`,**不要用 `DATE()`**
345
+ - [ ] GROUP BY 表达式与 SELECT 中对应列**逐字相同**(复制粘贴而非重写)
346
+ - [ ] ORDER BY 中同样使用与 GROUP BY 一致的表达式
347
+ - [ ] SELECT 中所有非聚合列(无 SUM/COUNT/AVG/MAX/MIN 包裹)都出现在 GROUP BY 中
348
+
349
+ ---
350
+
351
+ ## 十、开发检查清单
290
352
 
291
353
  ### 建表
292
354
  - [ ] 分组维度 + 金额汇总 + 审计字段(crby/crtime/upby/uptime/del_flag),无 tenant_id
@@ -302,10 +364,11 @@ public ReportBaseTotalVO<XxxVO> pageSummary(XxxParam param) {
302
364
 
303
365
  ### 查询
304
366
  - [ ] ReportBaseTotalVO + CompletableFuture 并行 + MgrUserAuthPO 权限
367
+ - [ ] GROUP BY / ORDER BY 表达式与 SELECT 完全一致(only_full_group_by)
305
368
 
306
369
  ---
307
370
 
308
- ## 十、关键代码位置
371
+ ## 十一、关键代码位置
309
372
 
310
373
  > 路径前缀均为 `core-report/.../statistics/`
311
374
 
@@ -0,0 +1,400 @@
1
+ ---
2
+ name: loki-log-query
3
+ description: |
4
+ 通过 Grafana Loki 查询线上日志,支持多环境切换、traceId 链路追踪、接口路径查询、关键词搜索。
5
+
6
+ 触发场景:
7
+ - 需要查看线上日志排查问题
8
+ - 通过 traceId 查询完整请求链路
9
+ - 通过接口路径查询该接口的所有日志
10
+ - 搜索特定错误日志或异常堆栈
11
+ - 切换不同日志服务环境
12
+
13
+ 触发词:查日志、traceId、链路追踪、Loki、日志查询、线上日志、生产日志、错误日志、异常堆栈、日志排查、切换环境、monitor
14
+ ---
15
+
16
+ # Loki 日志查询技能
17
+
18
+ ## 概述
19
+
20
+ 通过 Grafana Loki API 查询线上日志。支持:
21
+ 1. **多环境管理**:5 个 Grafana 日志服务,按需切换
22
+ 2. **traceId 查询**:通过日志ID获取完整请求链路
23
+ 3. **接口路径查询**:通过 API 接口路径查询相关日志
24
+ 4. **关键词搜索**:错误信息、类名、自定义关键词
25
+
26
+ ## 多环境配置
27
+
28
+ 配置文件:`.claude/skills/loki-log-query/environments.json`
29
+
30
+ ### 环境列表
31
+
32
+ | 环境别名 | 名称 | URL | 快捷词 |
33
+ |----------|------|-----|--------|
34
+ | `test13` | 测试13(主测试环境) | `https://test13.xnzn.net/grafana` | test13, 13 |
35
+ | `monitor-test` | Monitor 测试环境 | `https://monitor-test.xnzn.net/grafana` | mtest |
36
+ | `monitor-dev` | Monitor 开发环境 | `https://monitor-dev.xnzn.net/grafana` | mdev, dev |
37
+ | `monitor02-dev` | Monitor02 开发环境 | `https://monitor02-dev.xnzn.net/grafana` | m02, monitor02 |
38
+ | `monitor-tyy-dev` | Monitor 体验园开发环境 | `https://monitor-tyy-dev.xnzn.net/grafana` | tyy, 体验园 |
39
+
40
+ ### 环境匹配规则
41
+
42
+ 用户说的话 → 匹配环境:
43
+ - "查 test13 的日志" → `test13`
44
+ - "去 monitor-dev 查" → `monitor-dev`
45
+ - "切到体验园" → `monitor-tyy-dev`
46
+ - "去 m02 查一下" → `monitor02-dev`
47
+ - 未指定环境 → 使用 `active` 字段指定的默认环境
48
+
49
+ ### 读取配置
50
+
51
+ ```bash
52
+ SKILL_DIR="$CLAUDE_PROJECT_DIR/.claude/skills/loki-log-query"
53
+ ENV_FILE="${SKILL_DIR}/environments.json"
54
+
55
+ # 读取指定环境(参数: 环境别名)
56
+ read_env() {
57
+ local ENV_KEY="${1:-$(python3 -c "import json; print(json.load(open('${ENV_FILE}'))['active'])")}"
58
+ GRAFANA_URL=$(python3 -c "import json; print(json.load(open('${ENV_FILE}'))['environments']['${ENV_KEY}']['url'])")
59
+ TOKEN=$(python3 -c "import json; print(json.load(open('${ENV_FILE}'))['environments']['${ENV_KEY}']['token'])")
60
+ API="${GRAFANA_URL}/api/datasources/proxy/uid/loki/loki/api/v1"
61
+ echo "Environment: ${ENV_KEY} → ${GRAFANA_URL}"
62
+ }
63
+
64
+ # 通过别名查找环境 key
65
+ find_env() {
66
+ python3 -c "
67
+ import json
68
+ data = json.load(open('${ENV_FILE}'))
69
+ alias = '${1}'.lower()
70
+ for key, env in data['environments'].items():
71
+ if alias == key or alias in env.get('aliases', []):
72
+ print(key)
73
+ break
74
+ else:
75
+ print(data['active'])
76
+ "
77
+ }
78
+ ```
79
+
80
+ ### 切换活跃环境
81
+
82
+ ```bash
83
+ # 切换默认环境为 monitor-dev
84
+ python3 -c "
85
+ import json
86
+ data = json.load(open('${ENV_FILE}'))
87
+ data['active'] = 'monitor-dev'
88
+ json.dump(data, open('${ENV_FILE}', 'w'), indent=2, ensure_ascii=False)
89
+ print('Switched to:', data['active'])
90
+ "
91
+ ```
92
+
93
+ ### 更新 Token
94
+
95
+ ```bash
96
+ # 为某个环境设置 Token
97
+ python3 -c "
98
+ import json
99
+ data = json.load(open('${ENV_FILE}'))
100
+ data['environments']['monitor-dev']['token'] = 'glsa_新的token值'
101
+ json.dump(data, open('${ENV_FILE}', 'w'), indent=2, ensure_ascii=False)
102
+ print('Token updated for monitor-dev')
103
+ "
104
+ ```
105
+
106
+ ## API 基础
107
+
108
+ | 项目 | 值 |
109
+ |------|-----|
110
+ | 数据源 | Loki(uid: `loki`) |
111
+ | **API 路径** | `{GRAFANA_URL}/api/datasources/proxy/uid/loki/loki/api/v1/query_range` |
112
+ | 认证 | `Authorization: Bearer {TOKEN}` |
113
+ | 默认标签 | `app="yunshitang"` |
114
+
115
+ > **重要**:Loki API 必须通过 Grafana datasource proxy 访问,直接 `/loki/api/v1/` 会返回 404。
116
+ > 查询时不限 project 标签(`{app="yunshitang"}`),可覆盖该 Grafana 下所有环境。
117
+
118
+ ## 日志格式
119
+
120
+ ```
121
+ 2026-03-07 09:16:53.039,bcf6d955-fa26-45a5-9628-748f7ac4eed2,,, INFO 1 --- [线程名] 类名 : 行号 : 消息内容
122
+ ```
123
+
124
+ | 位置 | 字段 | 示例 |
125
+ |------|------|------|
126
+ | 第1段 | 时间戳 | `2026-03-07 09:16:53.039` |
127
+ | 第2段 | traceId | `bcf6d955-fa26-45a5-9628-748f7ac4eed2` 或 `a53dd0b0cc62bf4a79a63e77444f6f3f` |
128
+ | 第3段 | 商户ID | `553722740746489856` |
129
+ | 第4段 | 用户ID | `553723188689768448` |
130
+ | 第5段 | 日志内容 | `INFO 1 --- [thread] Class : 123 : msg` |
131
+
132
+ > traceId 可能是 UUID 格式(带横线)或 32位hex(不带横线),都支持。
133
+
134
+ ## 查询场景
135
+
136
+ ### 场景 1:按 traceId 查完整链路(最常用)
137
+
138
+ 用户说:"用 `a53dd0b0cc62bf4a79a63e77444f6f3f` 查日志"
139
+
140
+ ```bash
141
+ TRACE_ID="a53dd0b0cc62bf4a79a63e77444f6f3f"
142
+ END=$(date +%s)000000000
143
+ START=$(( $(date +%s) - 86400 ))000000000
144
+
145
+ curl -s "${API}/query_range" \
146
+ -H "Authorization: Bearer ${TOKEN}" \
147
+ --data-urlencode "query={app=\"yunshitang\"} |= \"${TRACE_ID}\"" \
148
+ --data-urlencode "start=${START}" \
149
+ --data-urlencode "end=${END}" \
150
+ --data-urlencode "limit=500" \
151
+ --data-urlencode "direction=forward" \
152
+ | python3 -c "
153
+ import sys, json
154
+ data = json.load(sys.stdin)
155
+ if data.get('status') == 'success':
156
+ for stream in data['data']['result']:
157
+ labels = stream.get('stream', {})
158
+ print(f'--- {labels.get(\"app\",\"?\")}/{labels.get(\"project\",\"?\")} ---')
159
+ for ts, line in stream['values']:
160
+ print(line)
161
+ else:
162
+ print('Error:', data)
163
+ "
164
+ ```
165
+
166
+ ### 场景 2:按接口路径查日志
167
+
168
+ 用户说:"查 `/security/summary/order/mealtime/classify/page` 这个接口的日志"
169
+
170
+ 接口路径出现在请求日志的 `RequestLoggingFilter` 中(`>>> POST /xxx` 或 `>>> GET /xxx`)。
171
+
172
+ ```bash
173
+ API_PATH="/security/summary/order/mealtime/classify/page"
174
+ END=$(date +%s)000000000
175
+ START=$(( $(date +%s) - 3600 ))000000000 # 最近1小时
176
+
177
+ curl -s "${API}/query_range" \
178
+ -H "Authorization: Bearer ${TOKEN}" \
179
+ --data-urlencode "query={app=\"yunshitang\"} |= \"${API_PATH}\"" \
180
+ --data-urlencode "start=${START}" \
181
+ --data-urlencode "end=${END}" \
182
+ --data-urlencode "limit=200" \
183
+ --data-urlencode "direction=forward" \
184
+ | python3 -c "
185
+ import sys, json, re
186
+ data = json.load(sys.stdin)
187
+ if data.get('status') != 'success':
188
+ print('Error:', data); sys.exit()
189
+
190
+ # 从匹配的请求日志中提取 traceId
191
+ trace_ids = set()
192
+ all_lines = []
193
+ for stream in data['data']['result']:
194
+ for ts, line in stream['values']:
195
+ all_lines.append(line)
196
+ # 提取 traceId(第2个逗号分隔字段)
197
+ parts = line.split(',')
198
+ if len(parts) >= 2:
199
+ tid = parts[1].strip()
200
+ if len(tid) >= 32:
201
+ trace_ids.add(tid)
202
+
203
+ print(f'Found {len(all_lines)} lines, {len(trace_ids)} unique traceIds')
204
+ print()
205
+ for tid in list(trace_ids)[:10]:
206
+ print(f' traceId: {tid}')
207
+ print()
208
+ for line in all_lines[:30]:
209
+ print(line)
210
+ if len(all_lines) > 30:
211
+ print(f'... and {len(all_lines)-30} more lines')
212
+ "
213
+ ```
214
+
215
+ **进阶**:找到 traceId 后,再用场景 1 查该 traceId 的完整链路。
216
+
217
+ ### 场景 3:关键词搜索
218
+
219
+ 用户说:"搜一下 LeException" 或 "查 ERROR 日志"
220
+
221
+ ```bash
222
+ KEYWORD="LeException"
223
+ END=$(date +%s)000000000
224
+ START=$(( $(date +%s) - 3600 ))000000000
225
+
226
+ curl -s "${API}/query_range" \
227
+ -H "Authorization: Bearer ${TOKEN}" \
228
+ --data-urlencode "query={app=\"yunshitang\"} |= \"${KEYWORD}\"" \
229
+ --data-urlencode "start=${START}" \
230
+ --data-urlencode "end=${END}" \
231
+ --data-urlencode "limit=100" \
232
+ --data-urlencode "direction=backward"
233
+ ```
234
+
235
+ **常用关键词组合**:
236
+
237
+ ```logql
238
+ # 所有 ERROR 日志
239
+ {app="yunshitang"} |= "ERROR"
240
+
241
+ # 业务异常
242
+ {app="yunshitang"} |= "LeException"
243
+
244
+ # SQL 错误
245
+ {app="yunshitang"} |~ "SQLSyntaxError|DataAccessException|BadSqlGrammar"
246
+
247
+ # 空指针
248
+ {app="yunshitang"} |= "NullPointerException"
249
+
250
+ # 按类名搜索
251
+ {app="yunshitang"} |= "OrderInfoService"
252
+
253
+ # 组合:ERROR + 特定类
254
+ {app="yunshitang"} |= "ERROR" |= "OrderInfoService"
255
+
256
+ # 排除健康检查噪音
257
+ {app="yunshitang"} |= "ERROR" != "health" != "actuator"
258
+ ```
259
+
260
+ ### 场景 4:按接口查完整链路(组合查询)
261
+
262
+ 用户说:"查 `/api/v2/web/order/list` 接口的全链路日志"
263
+
264
+ **两步走**:
265
+ 1. 先按接口路径搜索,拿到 traceId
266
+ 2. 再按 traceId 查完整链路
267
+
268
+ ```bash
269
+ # Step 1: 找 traceId
270
+ API_PATH="/api/v2/web/order/list"
271
+ END=$(date +%s)000000000
272
+ START=$(( $(date +%s) - 3600 ))000000000
273
+
274
+ TRACE_IDS=$(curl -s "${API}/query_range" \
275
+ -H "Authorization: Bearer ${TOKEN}" \
276
+ --data-urlencode "query={app=\"yunshitang\"} |= \"${API_PATH}\" |= \">>>\"" \
277
+ --data-urlencode "start=${START}" \
278
+ --data-urlencode "end=${END}" \
279
+ --data-urlencode "limit=10" \
280
+ --data-urlencode "direction=backward" \
281
+ | python3 -c "
282
+ import sys, json
283
+ data = json.load(sys.stdin)
284
+ for stream in data.get('data',{}).get('result',[]):
285
+ for ts, line in stream['values']:
286
+ parts = line.split(',')
287
+ if len(parts) >= 2 and len(parts[1].strip()) >= 32:
288
+ print(parts[1].strip())
289
+ ")
290
+
291
+ echo "Found traceIds:"
292
+ echo "${TRACE_IDS}" | head -5
293
+
294
+ # Step 2: 用第一个 traceId 查完整链路
295
+ FIRST_TID=$(echo "${TRACE_IDS}" | head -1)
296
+ if [ -n "${FIRST_TID}" ]; then
297
+ curl -s "${API}/query_range" \
298
+ -H "Authorization: Bearer ${TOKEN}" \
299
+ --data-urlencode "query={app=\"yunshitang\"} |= \"${FIRST_TID}\"" \
300
+ --data-urlencode "start=${START}" \
301
+ --data-urlencode "end=${END}" \
302
+ --data-urlencode "limit=500" \
303
+ --data-urlencode "direction=forward" \
304
+ | python3 -c "
305
+ import sys, json
306
+ data = json.load(sys.stdin)
307
+ for stream in data.get('data',{}).get('result',[]):
308
+ for ts, line in stream['values']:
309
+ print(line)
310
+ "
311
+ fi
312
+ ```
313
+
314
+ ### 场景 5:指定 project 环境查询
315
+
316
+ 如果用户指定了具体的 project(如 test20):
317
+
318
+ ```logql
319
+ {app="yunshitang",project="test20"} |= "traceId值"
320
+ ```
321
+
322
+ ## LogQL 语法速查
323
+
324
+ ### 流选择器
325
+
326
+ | 操作 | 语法 | 示例 |
327
+ |------|------|------|
328
+ | 精确匹配 | `=` | `{app="yunshitang"}` |
329
+ | 不等于 | `!=` | `{app!="test"}` |
330
+ | 正则匹配 | `=~` | `{project=~"test2.*"}` |
331
+
332
+ ### 行过滤
333
+
334
+ | 操作 | 语法 | 示例 |
335
+ |------|------|------|
336
+ | 包含 | `\|=` | `\|= "ERROR"` |
337
+ | 不包含 | `!=` | `!= "DEBUG"` |
338
+ | 正则匹配 | `\|~` | `\|~ "Exception\|Error"` |
339
+ | 正则排除 | `!~` | `!~ "health\|ping"` |
340
+
341
+ ### 查看可用标签值
342
+
343
+ ```bash
344
+ # 列出所有 project
345
+ curl -s "${API}/label/project/values" -H "Authorization: Bearer ${TOKEN}"
346
+
347
+ # 列出所有 app
348
+ curl -s "${API}/label/app/values" -H "Authorization: Bearer ${TOKEN}"
349
+ ```
350
+
351
+ ## Bug 修复工作流
352
+
353
+ ```
354
+ 1. 获取线索(traceId / 接口路径 / 错误关键词)
355
+
356
+ 2. 确定环境(哪个 Grafana?读取 environments.json)
357
+
358
+ 3. 查询日志(场景 1-4 选择合适的查询方式)
359
+
360
+ 4. 分析日志(ERROR/异常堆栈/SQL 错误/耗时)
361
+
362
+ 5. 定位代码(类名:行号 → 项目搜索)
363
+
364
+ 6. 修复 + 更新云效任务状态
365
+ ```
366
+
367
+ ### 日志分析重点
368
+
369
+ 1. **ERROR 级别日志** — 异常根因
370
+ 2. **异常堆栈** — `at net.xnzn.core.xxx` 开头的行
371
+ 3. **SQL 错误** — `SQLSyntaxErrorException`、`DataAccessException`
372
+ 4. **业务异常** — `LeException` 抛出的位置
373
+ 5. **耗时** — `Completed xxx in Nms`,识别慢请求
374
+ 6. **请求/响应** — `RequestLoggingFilter` 的 `>>>` 和 `<<<` 日志
375
+
376
+ ### 联动技能
377
+
378
+ | 场景 | 联动技能 |
379
+ |------|---------|
380
+ | 排查 Bug | `bug-detective` |
381
+ | 查询云效任务 | `yunxiao-task-management` |
382
+ | 查数据库 | `mysql-debug` |
383
+ | 性能问题 | `performance-doctor` |
384
+
385
+ ## Grafana Service Account Token 创建
386
+
387
+ 每个 Grafana 环境需要独立创建 Token:
388
+
389
+ 1. 登录对应 Grafana URL
390
+ 2. 左侧菜单 → Administration → Service accounts
391
+ 3. Add service account(名称:`claude-loki-reader`),角色:`Viewer`
392
+ 4. Add service account token → 复制
393
+ 5. 更新 `environments.json` 中对应环境的 `token` 字段
394
+
395
+ ## 注意
396
+
397
+ - 如果 Token 为空会报 `invalid API key`,需要先为该环境创建 Token
398
+ - 查询默认不限 project,可覆盖该 Grafana 下所有项目环境
399
+ - `direction=forward` 按时间正序,`direction=backward` 按时间倒序(最新优先)
400
+ - 如果是 Bug 排查完整流程,同时使用 `bug-detective`
@@ -27,41 +27,73 @@ description: |
27
27
 
28
28
  ---
29
29
 
30
- ## 首次使用引导(必须执行)
30
+ ## 连接信息获取(三级降级,自动查找)
31
31
 
32
- 当本技能被激活时,**必须先执行以下两项检查**,任一不通过则输出引导提示并停止查库操作:
32
+ 当本技能被激活时,**按以下优先级获取数据库连接信息**:
33
33
 
34
- ### 检查 1:配置文件是否就绪
34
+ ### 优先级 1:用户对话中指定(最高优先级)
35
35
 
36
- 读取 `.claude/mysql-config.json`,确定当前环境(用户指定 或 `default` 字段),检查该环境的 password 是否仍为占位符 `YOUR_PASSWORD`:
36
+ 用户直接给出连接信息,或指定环境名:
37
+ - "连 dev 环境查一下" → 使用 `.claude/mysql-config.json` 中 dev 环境的配置
38
+ - 直接给出 host/port/user/password → 直接使用
37
39
 
40
+ ### 优先级 2:`.claude/mysql-config.json`(显式配置,可选)
41
+
42
+ 如果文件存在且当前环境的 password 不是占位符 `YOUR_PASSWORD`,使用该配置。
43
+ **此文件为可选**,主要用于连接非本地环境(dev/prod 远程数据库)。
44
+
45
+ ### 优先级 3:工程配置文件(零配置,本地开发默认)
46
+
47
+ 从项目的 `bootstrap-dev.yml` 中自动提取连接信息:
48
+
49
+ **搜索路径**:
38
50
  ```
39
- 如果文件不存在 或 当前环境的 password == "YOUR_PASSWORD":
40
- → 输出引导提示(见下方模板),不执行查库
51
+ **/bootstrap-dev.yml
52
+ **/bootstrap*.yml(降级)
41
53
  ```
42
54
 
43
- ### 检查 2:mysql CLI 是否可用
44
-
45
- 执行 `which mysql`,检查是否返回有效路径:
55
+ **解析位置**:`spring.dataset.system.master` 节点
46
56
 
57
+ **解析规则**:
58
+ ```yaml
59
+ # 从以下字段提取:
60
+ jdbcUrl: jdbc:mysql://127.0.0.1:3306/system_xxx
61
+ username: root
62
+ password: xxx
47
63
  ```
48
- 如果 mysql 命令不存在:
49
- → 输出引导提示(见下方模板),不执行查库
64
+
65
+ **提取结果**:
66
+ - host = `127.0.0.1`(从 jdbcUrl 解析)
67
+ - port = `3306`(从 jdbcUrl 解析)
68
+ - user = `root`(username 字段)
69
+ - password = `xxx`(password 字段)
70
+ - 注意:jdbcUrl 中的数据库名(`system_xxx`)为系统库,**不作为查询目标数据库**。查询目标数据库 = 租户ID(从日志提取或用户指定)
71
+
72
+ ### mysql CLI 查找(多路径)
73
+
74
+ 按顺序查找 mysql 客户端:
75
+
76
+ ```bash
77
+ which mysql \
78
+ || ls /usr/local/mysql/bin/mysql \
79
+ || ls /opt/homebrew/bin/mysql \
80
+ || ls /opt/homebrew/opt/mysql-client/bin/mysql
50
81
  ```
51
82
 
52
- ### 引导提示模板
83
+ 找到任一即可使用。如果全部不存在,输出安装提示:
84
+ ```
85
+ brew install mysql-client
86
+ ```
53
87
 
54
- 当检查不通过时,输出以下提示:
88
+ ### 引导提示(仅当连接信息和 mysql CLI 都无法获取时)
55
89
 
56
90
  ```
57
91
  ⚠️ 数据库查询功能需要先完成配置:
58
92
 
59
- 1. 编辑 .claude/mysql-config.json,填入你的 MySQL 连接信息:
60
- 配置文件支持多环境(local / dev / prod),至少配置一个环境:
61
- - host: 数据库地址
62
- - port: 端口(默认 3306)
63
- - user: 用户名
64
- - password: 数据库密码(替换 YOUR_PASSWORD)
93
+ 1. 连接信息获取失败:
94
+ - 未找到 bootstrap-dev.yml 工程配置文件
95
+ - 未找到 .claude/mysql-config.json 配置文件
96
+ → 请创建 .claude/mysql-config.json 或确保项目中存在 bootstrap-dev.yml
65
97
 
66
98
  2. 安装 MySQL 客户端:
67
99
  brew install mysql-client
@@ -209,11 +241,15 @@ mysql -h 127.0.0.1 -P 3306 -u root -p'password' 546198574447230976 -e "SELECT ..
209
241
 
210
242
  ### 连接方式
211
243
 
212
- 读取 `.claude/mysql-config.json` 获取 host/port/user/password,数据库名从日志提取或用户指定:
244
+ 按"连接信息获取"章节的优先级获取 host/port/user/password,数据库名从日志提取或用户指定:
213
245
 
214
246
  ```bash
215
- # 从配置读取连接信息 + 动态数据库名
216
- mysql -h {host} -P {port} -u {user} -p'{password}' {database_from_log} -e "{SQL}"
247
+ # 连接信息来源:用户指定 > mysql-config.json > bootstrap-dev.yml 自动提取
248
+ # 数据库名来源:用户指定 > 日志提取的租户ID
249
+ mysql -h {host} -P {port} -u {user} -p'{password}' {database_from_log_or_user} -e "{SQL}"
250
+
251
+ # mysql 路径:如果 which mysql 失败,尝试绝对路径
252
+ /opt/homebrew/opt/mysql-client/bin/mysql -h {host} ...
217
253
  ```
218
254
 
219
255
  ### 常用排查查询模板