@chenmk/superflow 0.1.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/INSTALL.en.md +106 -0
- package/INSTALL.md +664 -0
- package/LICENSE +21 -0
- package/README.md +142 -0
- package/README.zh-CN.md +117 -0
- package/assets/context-templates/business-rules.md +98 -0
- package/assets/context-templates/decisions.md +153 -0
- package/assets/context-templates/external-systems.md +166 -0
- package/assets/context-templates/incidents.md +89 -0
- package/assets/manifest.json +53 -0
- package/assets/prompts/superflow-archive.md +9 -0
- package/assets/prompts/superflow-clarify.md +10 -0
- package/assets/prompts/superflow-design.md +10 -0
- package/assets/prompts/superflow-docs.md +10 -0
- package/assets/prompts/superflow-implement.md +10 -0
- package/assets/prompts/superflow-pipeline.md +13 -0
- package/assets/prompts/superflow-verify.md +10 -0
- package/assets/rules/superflow-phase-guard.md +50 -0
- package/assets/scripts/claude-auto-backup-hook.sh +313 -0
- package/assets/scripts/codex-auto-backup-hook.sh +361 -0
- package/assets/scripts/install-sql-pre-commit.sh +44 -0
- package/assets/scripts/superflow-contract-hooks.sh +744 -0
- package/assets/scripts/superflow-delivery-check.sh +315 -0
- package/assets/scripts/superflow-dependency-update-hook.sh +161 -0
- package/assets/scripts/superflow-enforce-hook.sh +70 -0
- package/assets/scripts/superflow-hook-guard.sh +132 -0
- package/assets/scripts/superflow-integration-evidence-hook.sh +80 -0
- package/assets/scripts/superflow-sql-sync-hook.py +950 -0
- package/assets/scripts/superflow-test-report-lint.py +433 -0
- package/assets/scripts/superflow-verify-integration.sh +90 -0
- package/assets/scripts/sync-settings-json.py +52 -0
- package/assets/skills/api-doc-changelog/SKILL.md +193 -0
- package/assets/skills/openspec-apply-change/SKILL.md +156 -0
- package/assets/skills/openspec-archive-change/SKILL.md +114 -0
- package/assets/skills/openspec-explore/SKILL.md +288 -0
- package/assets/skills/openspec-propose/SKILL.md +110 -0
- package/assets/skills/superflow-archive/SKILL.md +61 -0
- package/assets/skills/superflow-clarify/SKILL.md +146 -0
- package/assets/skills/superflow-clarify/agents/openai.yaml +4 -0
- package/assets/skills/superflow-design/SKILL.md +83 -0
- package/assets/skills/superflow-design/agents/openai.yaml +4 -0
- package/assets/skills/superflow-docs/SKILL.md +316 -0
- package/assets/skills/superflow-docs/agents/openai.yaml +4 -0
- package/assets/skills/superflow-hotfix/SKILL.md +48 -0
- package/assets/skills/superflow-implement/SKILL.md +461 -0
- package/assets/skills/superflow-implement/agents/openai.yaml +4 -0
- package/assets/skills/superflow-pipeline/SKILL.md +844 -0
- package/assets/skills/superflow-pipeline/agents/openai.yaml +4 -0
- package/assets/skills/superflow-pipeline/references/api-design-template.md +431 -0
- package/assets/skills/superflow-pipeline/references/architecture-design-template.md +119 -0
- package/assets/skills/superflow-pipeline/references/batch-prompt-template.md +536 -0
- package/assets/skills/superflow-pipeline/references/batch-split-guide.md +140 -0
- package/assets/skills/superflow-pipeline/references/decision-point.md +30 -0
- package/assets/skills/superflow-pipeline/references/dirty-worktree.md +35 -0
- package/assets/skills/superflow-pipeline/references/document-templates.md +123 -0
- package/assets/skills/superflow-pipeline/references/feature-gated-workflow.md +124 -0
- package/assets/skills/superflow-pipeline/references/implementation-prompt-template.md +1056 -0
- package/assets/skills/superflow-pipeline/references/mock-strategy-guide.md +86 -0
- package/assets/skills/superflow-pipeline/references/openspec-format.md +57 -0
- package/assets/skills/superflow-pipeline/references/orchestration.md +639 -0
- package/assets/skills/superflow-pipeline/references/p0-baseline-template.md +174 -0
- package/assets/skills/superflow-pipeline/references/project-config.md +40 -0
- package/assets/skills/superflow-pipeline/references/prompt-usage-template.md +152 -0
- package/assets/skills/superflow-pipeline/references/quality-gate.md +299 -0
- package/assets/skills/superflow-pipeline/references/quality-standards.md +190 -0
- package/assets/skills/superflow-pipeline/references/reviewer-checklist.md +154 -0
- package/assets/skills/superflow-pipeline/references/sql-risk-review-checklist.md +323 -0
- package/assets/skills/superflow-pipeline/references/subagent-progress.md +90 -0
- package/assets/skills/superflow-pipeline/references/superpower-technical-design-template.md +125 -0
- package/assets/skills/superflow-pipeline/references/test-execution-template.md +220 -0
- package/assets/skills/superflow-pipeline/references/test-guide.md +30 -0
- package/assets/skills/superflow-pipeline/references/traceability-matrix.md +106 -0
- package/assets/skills/superflow-pipeline/references/validation-integrity.md +134 -0
- package/assets/skills/superflow-pipeline/scripts/superflow-archive.sh +178 -0
- package/assets/skills/superflow-pipeline/scripts/superflow-env.sh +118 -0
- package/assets/skills/superflow-pipeline/scripts/superflow-guard.sh +428 -0
- package/assets/skills/superflow-pipeline/scripts/superflow-handoff.sh +296 -0
- package/assets/skills/superflow-pipeline/scripts/superflow-state.sh +574 -0
- package/assets/skills/superflow-pipeline/scripts/superflow-status.sh +172 -0
- package/assets/skills/superflow-pipeline/scripts/superflow-yaml-validate.sh +138 -0
- package/assets/skills/superflow-table-impact-analysis/SKILL.md +77 -0
- package/assets/skills/superflow-tweak/SKILL.md +46 -0
- package/assets/skills/superflow-verify/SKILL.md +112 -0
- package/assets/skills-en/api-doc-changelog/SKILL.md +193 -0
- package/assets/skills-en/openspec-apply-change/SKILL.md +156 -0
- package/assets/skills-en/openspec-archive-change/SKILL.md +114 -0
- package/assets/skills-en/openspec-explore/SKILL.md +288 -0
- package/assets/skills-en/openspec-propose/SKILL.md +110 -0
- package/assets/skills-en/superflow-archive/SKILL.md +61 -0
- package/assets/skills-en/superflow-clarify/SKILL.md +146 -0
- package/assets/skills-en/superflow-clarify/agents/openai.yaml +4 -0
- package/assets/skills-en/superflow-design/SKILL.md +83 -0
- package/assets/skills-en/superflow-design/agents/openai.yaml +4 -0
- package/assets/skills-en/superflow-docs/SKILL.md +316 -0
- package/assets/skills-en/superflow-docs/agents/openai.yaml +4 -0
- package/assets/skills-en/superflow-hotfix/SKILL.md +48 -0
- package/assets/skills-en/superflow-implement/SKILL.md +461 -0
- package/assets/skills-en/superflow-implement/agents/openai.yaml +4 -0
- package/assets/skills-en/superflow-pipeline/SKILL.md +844 -0
- package/assets/skills-en/superflow-pipeline/agents/openai.yaml +4 -0
- package/assets/skills-en/superflow-pipeline/references/api-design-template.md +431 -0
- package/assets/skills-en/superflow-pipeline/references/architecture-design-template.md +119 -0
- package/assets/skills-en/superflow-pipeline/references/batch-prompt-template.md +536 -0
- package/assets/skills-en/superflow-pipeline/references/batch-split-guide.md +140 -0
- package/assets/skills-en/superflow-pipeline/references/decision-point.md +30 -0
- package/assets/skills-en/superflow-pipeline/references/dirty-worktree.md +35 -0
- package/assets/skills-en/superflow-pipeline/references/document-templates.md +123 -0
- package/assets/skills-en/superflow-pipeline/references/feature-gated-workflow.md +124 -0
- package/assets/skills-en/superflow-pipeline/references/implementation-prompt-template.md +1056 -0
- package/assets/skills-en/superflow-pipeline/references/mock-strategy-guide.md +86 -0
- package/assets/skills-en/superflow-pipeline/references/openspec-format.md +57 -0
- package/assets/skills-en/superflow-pipeline/references/orchestration.md +639 -0
- package/assets/skills-en/superflow-pipeline/references/p0-baseline-template.md +174 -0
- package/assets/skills-en/superflow-pipeline/references/project-config.md +40 -0
- package/assets/skills-en/superflow-pipeline/references/prompt-usage-template.md +152 -0
- package/assets/skills-en/superflow-pipeline/references/quality-gate.md +299 -0
- package/assets/skills-en/superflow-pipeline/references/quality-standards.md +190 -0
- package/assets/skills-en/superflow-pipeline/references/reviewer-checklist.md +154 -0
- package/assets/skills-en/superflow-pipeline/references/sql-risk-review-checklist.md +323 -0
- package/assets/skills-en/superflow-pipeline/references/subagent-progress.md +90 -0
- package/assets/skills-en/superflow-pipeline/references/superpower-technical-design-template.md +125 -0
- package/assets/skills-en/superflow-pipeline/references/test-execution-template.md +220 -0
- package/assets/skills-en/superflow-pipeline/references/test-guide.md +30 -0
- package/assets/skills-en/superflow-pipeline/references/traceability-matrix.md +106 -0
- package/assets/skills-en/superflow-pipeline/references/validation-integrity.md +134 -0
- package/assets/skills-en/superflow-pipeline/scripts/superflow-archive.sh +178 -0
- package/assets/skills-en/superflow-pipeline/scripts/superflow-env.sh +118 -0
- package/assets/skills-en/superflow-pipeline/scripts/superflow-guard.sh +428 -0
- package/assets/skills-en/superflow-pipeline/scripts/superflow-handoff.sh +296 -0
- package/assets/skills-en/superflow-pipeline/scripts/superflow-state.sh +574 -0
- package/assets/skills-en/superflow-pipeline/scripts/superflow-status.sh +172 -0
- package/assets/skills-en/superflow-pipeline/scripts/superflow-yaml-validate.sh +138 -0
- package/assets/skills-en/superflow-table-impact-analysis/SKILL.md +77 -0
- package/assets/skills-en/superflow-tweak/SKILL.md +46 -0
- package/assets/skills-en/superflow-verify/SKILL.md +112 -0
- package/dist/cli/index.js +186 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/commands/archive.js +6 -0
- package/dist/commands/archive.js.map +1 -0
- package/dist/commands/clarify.js +6 -0
- package/dist/commands/clarify.js.map +1 -0
- package/dist/commands/design.js +6 -0
- package/dist/commands/design.js.map +1 -0
- package/dist/commands/docs.js +6 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/commands/doctor.js +473 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/implement.js +6 -0
- package/dist/commands/implement.js.map +1 -0
- package/dist/commands/init.js +471 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/pipeline.js +6 -0
- package/dist/commands/pipeline.js.map +1 -0
- package/dist/commands/scan.js +59 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/status.js +173 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/uninstall.js +213 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.js +187 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/verify.js +6 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/core/assets.js +27 -0
- package/dist/core/assets.js.map +1 -0
- package/dist/core/context.js +100 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/dependencies.js +146 -0
- package/dist/core/dependencies.js.map +1 -0
- package/dist/core/detect.js +71 -0
- package/dist/core/detect.js.map +1 -0
- package/dist/core/i18n.js +103 -0
- package/dist/core/i18n.js.map +1 -0
- package/dist/core/integrity.js +46 -0
- package/dist/core/integrity.js.map +1 -0
- package/dist/core/manifest.js +18 -0
- package/dist/core/manifest.js.map +1 -0
- package/dist/core/prompts.js +20 -0
- package/dist/core/prompts.js.map +1 -0
- package/dist/core/registry.js +134 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/rules.js +17 -0
- package/dist/core/rules.js.map +1 -0
- package/dist/core/scripts.js +40 -0
- package/dist/core/scripts.js.map +1 -0
- package/dist/core/skill-check.js +31 -0
- package/dist/core/skill-check.js.map +1 -0
- package/dist/core/skills.js +56 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/state.js +43 -0
- package/dist/core/state.js.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/path.js +11 -0
- package/dist/utils/path.js.map +1 -0
- package/dist/utils/shell.js +29 -0
- package/dist/utils/shell.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SDD 契约检查 Hook
|
|
3
|
+
# 在 Edit/Write 时自动检查代码是否符合前端联调契约。
|
|
4
|
+
# 包含 8 个检查器(A-H),按触发条件精确匹配。
|
|
5
|
+
#
|
|
6
|
+
# 依赖:python3、grep
|
|
7
|
+
# 退出码:0=允许 2=拦截(fail 级别) 0+warning=允许但输出警告
|
|
8
|
+
#
|
|
9
|
+
# 环境变量:
|
|
10
|
+
# SDD_CONTRACT_HOOK_DEBUG=1 开启调试输出
|
|
11
|
+
|
|
12
|
+
INPUT=$(cat)
|
|
13
|
+
|
|
14
|
+
# 提取 file_path
|
|
15
|
+
FILE_PATH=$(echo "$INPUT" | python3 -c "
|
|
16
|
+
import sys, json
|
|
17
|
+
d = json.load(sys.stdin)
|
|
18
|
+
print(d.get('tool_input', {}).get('file_path', ''))
|
|
19
|
+
" 2>/dev/null)
|
|
20
|
+
|
|
21
|
+
if [ -z "$FILE_PATH" ]; then
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# 只检查 Java 和 Markdown 文件
|
|
26
|
+
case "$FILE_PATH" in
|
|
27
|
+
*.java|*.md) ;;
|
|
28
|
+
*) exit 0 ;;
|
|
29
|
+
esac
|
|
30
|
+
|
|
31
|
+
# 只在 SDD 门禁激活时检查
|
|
32
|
+
FILE_DIR=$(dirname "$FILE_PATH")
|
|
33
|
+
REPO_ROOT=$(git -C "$FILE_DIR" rev-parse --show-toplevel 2>/dev/null)
|
|
34
|
+
if [ $? -ne 0 ]; then
|
|
35
|
+
exit 0
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
if [ ! -f "$REPO_ROOT/.sdd-enforced" ]; then
|
|
39
|
+
exit 0
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# 数据库门禁未通过时,不做契约检查(由 superflow-enforce-hook.sh 拦截)
|
|
43
|
+
if [ ! -f "$REPO_ROOT/.db-verified" ]; then
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
DEBUG=""
|
|
48
|
+
if [ "$SDD_CONTRACT_HOOK_DEBUG" = "1" ]; then
|
|
49
|
+
DEBUG="1"
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# 提取 new_string(Edit)或 content(Write)用于分析
|
|
53
|
+
EDIT_CONTENT=$(echo "$INPUT" | python3 -c "
|
|
54
|
+
import sys, json
|
|
55
|
+
d = json.load(sys.stdin)
|
|
56
|
+
ti = d.get('tool_input', {})
|
|
57
|
+
# Edit 模式:提取 new_string
|
|
58
|
+
ns = ti.get('new_string', '')
|
|
59
|
+
if ns:
|
|
60
|
+
print(ns)
|
|
61
|
+
else:
|
|
62
|
+
# Write 模式:提取 content
|
|
63
|
+
print(ti.get('content', ''))
|
|
64
|
+
" 2>/dev/null)
|
|
65
|
+
|
|
66
|
+
if [ -z "$EDIT_CONTENT" ]; then
|
|
67
|
+
exit 0
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
WARNINGS=""
|
|
71
|
+
HAS_FAIL=""
|
|
72
|
+
|
|
73
|
+
log_warning() {
|
|
74
|
+
WARNINGS="${WARNINGS}
|
|
75
|
+
⚠️ [SDD 契约检查] WARNING: $1"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
log_fail() {
|
|
79
|
+
HAS_FAIL="1"
|
|
80
|
+
WARNINGS="${WARNINGS}
|
|
81
|
+
🚫 [SDD 契约检查] FAIL: $1"
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
log_debug() {
|
|
85
|
+
if [ "$DEBUG" = "1" ]; then
|
|
86
|
+
echo "[DEBUG] $1" >&2
|
|
87
|
+
fi
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# 读取文件当前内容(如果文件存在)
|
|
91
|
+
FILE_CONTENT=""
|
|
92
|
+
if [ -f "$FILE_PATH" ]; then
|
|
93
|
+
FILE_CONTENT=$(cat "$FILE_PATH" 2>/dev/null)
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
# 合并当前文件内容和编辑内容用于检查
|
|
97
|
+
CHECK_CONTENT="${FILE_CONTENT}
|
|
98
|
+
${EDIT_CONTENT}"
|
|
99
|
+
|
|
100
|
+
###############################################################################
|
|
101
|
+
# Hook A:文件流接口检查
|
|
102
|
+
#
|
|
103
|
+
# 触发条件:
|
|
104
|
+
# - 变更的 Controller 方法中,路径或方法名包含:
|
|
105
|
+
# export / download / template / 导出 / 下载 / 模板
|
|
106
|
+
# - 或 @ApiOperation 包含"导出""下载模板""模板下载"
|
|
107
|
+
#
|
|
108
|
+
# 排除条件:
|
|
109
|
+
# - 路径包含 status/progress/result
|
|
110
|
+
# - 明确是 JSON 查询结果
|
|
111
|
+
# - 方法注释写明"返回 JSON"
|
|
112
|
+
#
|
|
113
|
+
# 检查规则:
|
|
114
|
+
# - 如果是成功文件接口,不应返回 Response<T>
|
|
115
|
+
# - 应包含 HttpServletResponse
|
|
116
|
+
# - 应设置 Content-Type
|
|
117
|
+
# - 应设置 Content-Disposition
|
|
118
|
+
# - tests.md 或单测应验证 header / 文件流 / 文件可打开
|
|
119
|
+
#
|
|
120
|
+
# 严重级别:
|
|
121
|
+
# - 路径包含 /export 且返回 Response:fail
|
|
122
|
+
# - 其他缺失:warning
|
|
123
|
+
###############################################################################
|
|
124
|
+
|
|
125
|
+
hook_a_file_stream() {
|
|
126
|
+
# 只检查 Java Controller 文件
|
|
127
|
+
case "$FILE_PATH" in
|
|
128
|
+
*Controller*.java|*controller*.java) ;;
|
|
129
|
+
*) return ;;
|
|
130
|
+
esac
|
|
131
|
+
|
|
132
|
+
# 检查是否包含导出/下载相关关键词
|
|
133
|
+
local is_file_interface=0
|
|
134
|
+
local method_names=$(echo "$EDIT_CONTENT" | grep -oiE '(export|download|template|导出|下载|模板)' || true)
|
|
135
|
+
local api_op=$(echo "$EDIT_CONTENT" | grep -oiE '@ApiOperation.*"(.*导出.*|.*下载.*|.*模板.*)"' || true)
|
|
136
|
+
|
|
137
|
+
if [ -z "$method_names" ] && [ -z "$api_op" ]; then
|
|
138
|
+
return
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
log_debug "Hook A: 检测到文件接口关键词"
|
|
142
|
+
|
|
143
|
+
# 排除条件:status/progress/result 或 "返回 JSON"
|
|
144
|
+
local exclude=$(echo "$EDIT_CONTENT" | grep -oiE '(status|progress|result|返回 JSON|返回JSON)' || true)
|
|
145
|
+
if [ -n "$exclude" ]; then
|
|
146
|
+
log_debug "Hook A: 命中排除条件,跳过"
|
|
147
|
+
return
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
# 检查是否返回 Response<T>
|
|
151
|
+
local returns_response=$(echo "$EDIT_CONTENT" | grep -oiE 'Response<[^>]*>' | head -1 || true)
|
|
152
|
+
local has_servlet_response=$(echo "$EDIT_CONTENT" | grep -oi 'HttpServletResponse' || true)
|
|
153
|
+
local has_content_type=$(echo "$EDIT_CONTENT" | grep -oi 'Content-Type\|setContentType\|contentType' || true)
|
|
154
|
+
local has_disposition=$(echo "$EDIT_CONTENT" | grep -oi 'Content-Disposition\|setContentDisposition\|setHeader.*attachment' || true)
|
|
155
|
+
|
|
156
|
+
local is_export=$(echo "$EDIT_CONTENT" | grep -oi '/export' || true)
|
|
157
|
+
|
|
158
|
+
if [ -n "$is_export" ] && [ -n "$returns_response" ] && [ -z "$has_servlet_response" ]; then
|
|
159
|
+
log_fail "Hook A [文件流接口]: 路径含 /export 且返回 Response<T>,应直接操作 HttpServletResponse 返回文件流"
|
|
160
|
+
else
|
|
161
|
+
if [ -n "$method_names" ] || [ -n "$api_op" ]; then
|
|
162
|
+
if [ -z "$has_servlet_response" ]; then
|
|
163
|
+
log_warning "Hook A [文件流接口]: 疑似文件流接口但未使用 HttpServletResponse"
|
|
164
|
+
fi
|
|
165
|
+
if [ -z "$has_content_type" ]; then
|
|
166
|
+
log_warning "Hook A [文件流接口]: 文件流接口未设置 Content-Type"
|
|
167
|
+
fi
|
|
168
|
+
if [ -z "$has_disposition" ]; then
|
|
169
|
+
log_warning "Hook A [文件流接口]: 文件流接口未设置 Content-Disposition"
|
|
170
|
+
fi
|
|
171
|
+
fi
|
|
172
|
+
fi
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
###############################################################################
|
|
176
|
+
# Hook B:导入失败码检查
|
|
177
|
+
#
|
|
178
|
+
# 触发条件:
|
|
179
|
+
# - Controller 方法路径包含 /import
|
|
180
|
+
# - 且返回类型或方法体出现 ImportResult
|
|
181
|
+
# - 或 data 中有 failureCount/failureRows
|
|
182
|
+
#
|
|
183
|
+
# 排除条件:
|
|
184
|
+
# - 普通异步导入任务创建接口,不直接返回导入结果
|
|
185
|
+
# - 只返回任务 ID 的接口
|
|
186
|
+
#
|
|
187
|
+
# 检查规则:
|
|
188
|
+
# - 不允许无条件 Response.ok(result)
|
|
189
|
+
# - 必须存在 failureCount > 0 时返回非 0 业务 code 的分支
|
|
190
|
+
# - 文档必须说明失败行时前端如何判断失败
|
|
191
|
+
#
|
|
192
|
+
# 严重级别:
|
|
193
|
+
# - 有 failureCount 且无失败码分支:fail
|
|
194
|
+
###############################################################################
|
|
195
|
+
|
|
196
|
+
hook_b_import_failure_code() {
|
|
197
|
+
case "$FILE_PATH" in
|
|
198
|
+
*Controller*.java|*controller*.java|*Service*.java|*service*.java|*ServiceImpl*.java) ;;
|
|
199
|
+
*) return ;;
|
|
200
|
+
esac
|
|
201
|
+
|
|
202
|
+
# 触发条件:包含 /import 或 ImportResult 或 failureCount
|
|
203
|
+
local has_import_path=$(echo "$EDIT_CONTENT" | grep -oi '/import' || true)
|
|
204
|
+
local has_import_result=$(echo "$EDIT_CONTENT" | grep -oi 'ImportResult\|ImportResp\|failureCount\|failureRows' || true)
|
|
205
|
+
|
|
206
|
+
if [ -z "$has_import_path" ] && [ -z "$has_import_result" ]; then
|
|
207
|
+
return
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
log_debug "Hook B: 检测到导入接口关键词"
|
|
211
|
+
|
|
212
|
+
# 排除条件:只返回任务 ID
|
|
213
|
+
local task_only=$(echo "$EDIT_CONTENT" | grep -oiE '(taskId|task_id|任务ID|异步导入)' || true)
|
|
214
|
+
if [ -n "$task_only" ] && [ -z "$has_import_result" ]; then
|
|
215
|
+
log_debug "Hook B: 只返回任务 ID,跳过"
|
|
216
|
+
return
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
# 检查是否有 failureCount 但无条件返回 Response.ok
|
|
220
|
+
local has_failure_count=$(echo "$EDIT_CONTENT" | grep -oi 'failureCount' || true)
|
|
221
|
+
local has_ok_unconditional=$(echo "$EDIT_CONTENT" | grep -oiE 'Response\.ok\(.*result' || true)
|
|
222
|
+
local has_failure_branch=$(echo "$EDIT_CONTENT" | grep -oiE '(failureCount\s*>\s*0|failureCount\s*!=\s*0|getFailureCount|hasFail)' || true)
|
|
223
|
+
|
|
224
|
+
if [ -n "$has_failure_count" ] && [ -z "$has_failure_branch" ]; then
|
|
225
|
+
log_fail "Hook B [导入失败码]: 存在 failureCount 但无失败码判断分支,failureCount > 0 时必须返回非 0 业务 code"
|
|
226
|
+
elif [ -n "$has_failure_count" ] && [ -n "$has_failure_branch" ]; then
|
|
227
|
+
log_debug "Hook B: 存在失败码分支,通过"
|
|
228
|
+
fi
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
###############################################################################
|
|
232
|
+
# Hook C:Excel 日期格式检查
|
|
233
|
+
#
|
|
234
|
+
# 触发条件:
|
|
235
|
+
# - 变更文件含 @ExcelProperty
|
|
236
|
+
# - 且列名包含"时间""日期"
|
|
237
|
+
# - 或类名/文件名包含 Import/Excel/Template
|
|
238
|
+
#
|
|
239
|
+
# 排除条件:
|
|
240
|
+
# - 纯导出 DTO,不参与导入解析
|
|
241
|
+
# - 文档明确要求前端只能传完整时间且有前端校验证据
|
|
242
|
+
#
|
|
243
|
+
# 检查规则:
|
|
244
|
+
# - 日期解析应支持 yyyy-MM-dd,或文档明确说明不支持
|
|
245
|
+
# - tests.md 或单测必须覆盖 yyyy-MM-dd
|
|
246
|
+
# - 如果只有日期,应说明是否补 00:00:00
|
|
247
|
+
#
|
|
248
|
+
# 严重级别:
|
|
249
|
+
# - 导入字段含日期但无格式说明:warning
|
|
250
|
+
# - 已知前端模板为日期但后端只支持完整时间:fail
|
|
251
|
+
###############################################################################
|
|
252
|
+
|
|
253
|
+
hook_c_excel_date() {
|
|
254
|
+
case "$FILE_PATH" in
|
|
255
|
+
*.java) ;;
|
|
256
|
+
*) return ;;
|
|
257
|
+
esac
|
|
258
|
+
|
|
259
|
+
# 触发条件
|
|
260
|
+
local has_excel_prop=$(echo "$EDIT_CONTENT" | grep -oi '@ExcelProperty' || true)
|
|
261
|
+
local has_date_col=$(echo "$EDIT_CONTENT" | grep -oiE '(时间|日期|date|time|Date|Time)' || true)
|
|
262
|
+
local has_import_name=$(echo "$FILE_PATH" | grep -oiE '(Import|Excel|Template)' || true)
|
|
263
|
+
|
|
264
|
+
if [ -z "$has_excel_prop" ]; then
|
|
265
|
+
return
|
|
266
|
+
fi
|
|
267
|
+
|
|
268
|
+
if [ -z "$has_date_col" ] && [ -z "$has_import_name" ]; then
|
|
269
|
+
return
|
|
270
|
+
fi
|
|
271
|
+
|
|
272
|
+
log_debug "Hook C: 检测到 Excel 日期字段"
|
|
273
|
+
|
|
274
|
+
# 排除条件:纯导出 DTO
|
|
275
|
+
local is_export_only=$(echo "$FILE_PATH" | grep -oiE '(Export|导出)' || true)
|
|
276
|
+
local has_export_annotation=$(echo "$EDIT_CONTENT" | grep -oi 'class.*Export.*DTO\|class.*Export.*Vo\|class.*Export.*Model' || true)
|
|
277
|
+
if [ -n "$is_export_only" ] || [ -n "$has_export_annotation" ]; then
|
|
278
|
+
log_debug "Hook C: 纯导出 DTO,跳过"
|
|
279
|
+
return
|
|
280
|
+
fi
|
|
281
|
+
|
|
282
|
+
# 检查日期格式是否兼容
|
|
283
|
+
local has_short_date=$(echo "$EDIT_CONTENT" | grep -oi 'yyyy-MM-dd[^-]' || true)
|
|
284
|
+
local has_full_date=$(echo "$EDIT_CONTENT" | grep -oi 'yyyy-MM-dd HH:mm:ss' || true)
|
|
285
|
+
local has_date_format_annotation=$(echo "$EDIT_CONTENT" | grep -oiE '@DateTimeFormat|@JsonFormat|DateTimeFormatter|SimpleDateFormat' || true)
|
|
286
|
+
|
|
287
|
+
if [ -n "$has_date_col" ] && [ -n "$has_import_name" ]; then
|
|
288
|
+
if [ -z "$has_date_format_annotation" ]; then
|
|
289
|
+
log_warning "Hook C [Excel 日期]: 导入 DTO 含日期字段但未指定日期格式,应至少兼容 yyyy-MM-dd 和 yyyy-MM-dd HH:mm:ss"
|
|
290
|
+
elif [ -n "$has_full_date" ] && [ -z "$has_short_date" ]; then
|
|
291
|
+
log_fail "Hook C [Excel 日期]: 后端只支持 yyyy-MM-dd HH:mm:ss,但 Excel 默认日期格式为 yyyy-MM-dd,应兼容两种格式"
|
|
292
|
+
fi
|
|
293
|
+
elif [ -n "$has_date_col" ]; then
|
|
294
|
+
log_warning "Hook C [Excel 日期]: @ExcelProperty 含日期/时间列,确认日期格式是否兼容 yyyy-MM-dd"
|
|
295
|
+
fi
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
###############################################################################
|
|
299
|
+
# Hook D:Excel 模板列头一致性检查
|
|
300
|
+
#
|
|
301
|
+
# 触发条件:
|
|
302
|
+
# - 变更包含 EasyExcel.write 或 EasyExcel.read
|
|
303
|
+
# - 或变更包含 @ExcelProperty
|
|
304
|
+
#
|
|
305
|
+
# 排除条件:
|
|
306
|
+
# - 与 Excel 无关的普通 DTO
|
|
307
|
+
#
|
|
308
|
+
# 检查规则:
|
|
309
|
+
# - 模板下载 DTO 和导入解析 DTO 的列头应一致
|
|
310
|
+
# - api.md/tests.md 应列出列头
|
|
311
|
+
# - 不得出现隐藏字段、内部 ID、状态列等已明确禁止字段
|
|
312
|
+
#
|
|
313
|
+
# 严重级别:
|
|
314
|
+
# - 模板列头和导入列头明显不一致:fail
|
|
315
|
+
# - 文档未列头:warning
|
|
316
|
+
###############################################################################
|
|
317
|
+
|
|
318
|
+
hook_d_excel_header_consistency() {
|
|
319
|
+
case "$FILE_PATH" in
|
|
320
|
+
*.java) ;;
|
|
321
|
+
*) return ;;
|
|
322
|
+
esac
|
|
323
|
+
|
|
324
|
+
local has_excel_prop=$(echo "$EDIT_CONTENT" | grep -oi '@ExcelProperty' || true)
|
|
325
|
+
local has_easyexcel=$(echo "$EDIT_CONTENT" | grep -oi 'EasyExcel' || true)
|
|
326
|
+
|
|
327
|
+
if [ -z "$has_excel_prop" ] && [ -z "$has_easyexcel" ]; then
|
|
328
|
+
return
|
|
329
|
+
fi
|
|
330
|
+
|
|
331
|
+
log_debug "Hook D: 检测到 EasyExcel 或 @ExcelProperty"
|
|
332
|
+
|
|
333
|
+
# 检查是否有内部字段(不应出现在模板中的)
|
|
334
|
+
local has_internal_id=$(echo "$EDIT_CONTENT" | grep -oiE '@ExcelProperty.*"(id|ID|状态|status|内部|internal)"' || true)
|
|
335
|
+
local has_hidden_field=$(echo "$EDIT_CONTENT" | grep -oiE '@ExcelProperty.*"(create_time|update_time|create_by|update_by|deleted|is_deleted)"' || true)
|
|
336
|
+
|
|
337
|
+
if [ -n "$has_internal_id" ] || [ -n "$has_hidden_field" ]; then
|
|
338
|
+
log_warning "Hook D [Excel 列头]: @ExcelProperty 包含内部字段(id/status/create_time 等),确认是否应出现在导入模板中"
|
|
339
|
+
fi
|
|
340
|
+
|
|
341
|
+
# 如果是模板 DTO,检查是否有对应的导入 DTO
|
|
342
|
+
local is_template=$(echo "$FILE_PATH" | grep -oiE '(Template|模板)' || true)
|
|
343
|
+
local is_import=$(echo "$FILE_PATH" | grep -oiE '(Import|导入)' || true)
|
|
344
|
+
|
|
345
|
+
if [ -n "$is_template" ] || [ -n "$is_import" ]; then
|
|
346
|
+
# 提取列头
|
|
347
|
+
local headers=$(echo "$EDIT_CONTENT" | grep -oiE '@ExcelProperty\("([^"]+)"' | sed 's/.*"\([^"]*\)".*/\1/' | sort | tr '\n' ',' || true)
|
|
348
|
+
if [ -n "$headers" ]; then
|
|
349
|
+
log_warning "Hook D [Excel 列头]: 检测到 DTO 列头: [${headers%,}],请确认模板下载 DTO 和导入解析 DTO 列头一致"
|
|
350
|
+
fi
|
|
351
|
+
fi
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
###############################################################################
|
|
355
|
+
# Hook E:api.md 与 Controller 路由一致性检查
|
|
356
|
+
#
|
|
357
|
+
# 触发条件:
|
|
358
|
+
# - Controller 新增/修改 @GetMapping/@PostMapping/@PutMapping/@DeleteMapping
|
|
359
|
+
#
|
|
360
|
+
# 排除条件:
|
|
361
|
+
# - /internal/ 路径
|
|
362
|
+
# - 第三方回调
|
|
363
|
+
# - 注释标记 legacy/兼容/历史
|
|
364
|
+
#
|
|
365
|
+
# 检查规则:
|
|
366
|
+
# - Controller 新增 mapping 应能在 api.md 找到
|
|
367
|
+
# - 同一资源下存在固定路径和 {id} 时提示路由抢占风险
|
|
368
|
+
#
|
|
369
|
+
# 严重级别:
|
|
370
|
+
# - Controller 存在但 api.md 缺失:warning
|
|
371
|
+
# - {id} 抢占风险:warning
|
|
372
|
+
###############################################################################
|
|
373
|
+
|
|
374
|
+
hook_e_route_consistency() {
|
|
375
|
+
case "$FILE_PATH" in
|
|
376
|
+
*Controller*.java|*controller*.java) ;;
|
|
377
|
+
*) return ;;
|
|
378
|
+
esac
|
|
379
|
+
|
|
380
|
+
# 检测新增的 mapping
|
|
381
|
+
local has_new_mapping=$(echo "$EDIT_CONTENT" | grep -oiE '@(Get|Post|Put|Delete|Patch|Request)Mapping' || true)
|
|
382
|
+
if [ -z "$has_new_mapping" ]; then
|
|
383
|
+
return
|
|
384
|
+
fi
|
|
385
|
+
|
|
386
|
+
log_debug "Hook E: 检测到 Controller mapping 变更"
|
|
387
|
+
|
|
388
|
+
# 排除条件
|
|
389
|
+
local is_internal=$(echo "$EDIT_CONTENT" | grep -oiE '/internal/' || true)
|
|
390
|
+
local is_callback=$(echo "$EDIT_CONTENT" | grep -oiE '(callback|回调|notify|通知)' || true)
|
|
391
|
+
local is_legacy=$(echo "$EDIT_CONTENT" | grep -oiE '(legacy|兼容|历史|deprecated)' || true)
|
|
392
|
+
|
|
393
|
+
if [ -n "$is_internal" ] || [ -n "$is_legacy" ]; then
|
|
394
|
+
log_debug "Hook E: 命中排除条件,跳过"
|
|
395
|
+
return
|
|
396
|
+
fi
|
|
397
|
+
|
|
398
|
+
# 提取路径
|
|
399
|
+
local paths=$(echo "$EDIT_CONTENT" | grep -oiE '@(Get|Post|Put|Delete)Mapping\s*\(\s*"?(/[^"]*)"?|value\s*=\s*"?(/[^"]*)"?|path\s*=\s*"?(/[^"]*)"?' | grep -oiE '/[^"]+' | sed 's/"//g;s/)$//' || true)
|
|
400
|
+
|
|
401
|
+
if [ -z "$paths" ]; then
|
|
402
|
+
return
|
|
403
|
+
fi
|
|
404
|
+
|
|
405
|
+
# 检查 {id} 与固定路径冲突
|
|
406
|
+
local has_id_path=$(echo "$paths" | grep -oi '{id}' || true)
|
|
407
|
+
local has_fixed_path=$(echo "$EDIT_CONTENT" | grep -oiE '@(Get|Post|Put|Delete)Mapping\s*\(\s*"?(/[a-zA-Z]+)"?' | grep -vi '{' || true)
|
|
408
|
+
|
|
409
|
+
if [ -n "$has_id_path" ] && [ -n "$has_fixed_path" ]; then
|
|
410
|
+
log_warning "Hook E [路由一致性]: 同一 Controller 下存在固定路径和 {id} 路径变量,存在路由抢占风险。建议固定路径排在 {id} 之前或使用正则约束"
|
|
411
|
+
fi
|
|
412
|
+
|
|
413
|
+
# 检查是否有对应的 api.md
|
|
414
|
+
local api_md_path=$(find "$REPO_ROOT/doc" -name "api.md" -type f 2>/dev/null | head -1)
|
|
415
|
+
if [ -n "$api_md_path" ] && [ -f "$api_md_path" ]; then
|
|
416
|
+
for p in $paths; do
|
|
417
|
+
local clean_path=$(echo "$p" | sed 's/[{}]//g' | sed 's/^\///')
|
|
418
|
+
local found=$(grep -oi "$clean_path" "$api_md_path" 2>/dev/null || true)
|
|
419
|
+
if [ -z "$found" ]; then
|
|
420
|
+
log_warning "Hook E [路由一致性]: Controller 新增路径 $p 未在 api.md 中找到对应定义,请补充接口文档"
|
|
421
|
+
fi
|
|
422
|
+
done
|
|
423
|
+
fi
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
###############################################################################
|
|
427
|
+
# Hook F:数据权限决策检查(Data Permission Decision Check)
|
|
428
|
+
#
|
|
429
|
+
# 不判断具体字段名,不替用户决定是否需要数据权限。
|
|
430
|
+
# 只检查文档是否已做出三选一结论:
|
|
431
|
+
# - 需要数据权限,并说明复用模式
|
|
432
|
+
# - 不需要数据权限,并说明原因
|
|
433
|
+
# - 不确定,列入待确认问题并阻塞实现
|
|
434
|
+
#
|
|
435
|
+
# 触发条件:
|
|
436
|
+
# - Controller/Service/Mapper 改动
|
|
437
|
+
# - 且方法名/路径/注释含访问类动作关键词
|
|
438
|
+
#
|
|
439
|
+
# 排除条件:
|
|
440
|
+
# - 登录/验证码/健康检查/公开字典/枚举/静态配置
|
|
441
|
+
# - 文档明确说明由统一注解/AOP/网关处理
|
|
442
|
+
#
|
|
443
|
+
# 严重级别:
|
|
444
|
+
# - 无数据权限判断文档:warning
|
|
445
|
+
# - 写操作无权限迹象且无文档说明:high warning
|
|
446
|
+
# - 文档标记"不确定":info,提示阻塞实现
|
|
447
|
+
###############################################################################
|
|
448
|
+
|
|
449
|
+
hook_f_permission_decision() {
|
|
450
|
+
case "$FILE_PATH" in
|
|
451
|
+
*Controller*.java|*controller*.java|*Service*.java|*service*.java|*ServiceImpl*.java|*.xml) ;;
|
|
452
|
+
*) return ;;
|
|
453
|
+
esac
|
|
454
|
+
|
|
455
|
+
# 触发条件:方法名/路径含访问类动作
|
|
456
|
+
local has_access_action=$(echo "$EDIT_CONTENT" | grep -oiE '(list|page|query|search|detail|get|export|update|delete|refund|approve|分页|列表|查询|详情|导出|编辑|删除|退款|审批)' || true)
|
|
457
|
+
|
|
458
|
+
if [ -z "$has_access_action" ]; then
|
|
459
|
+
return
|
|
460
|
+
fi
|
|
461
|
+
|
|
462
|
+
log_debug "Hook F: 检测到访问类动作关键词"
|
|
463
|
+
|
|
464
|
+
# 排除条件:公开接口、登录、健康检查等
|
|
465
|
+
# 注意:Java 方法声明中的 public 不是公开接口标记,需排除
|
|
466
|
+
local content_without_java_modifiers=$(echo "$EDIT_CONTENT" | sed 's/public\s\+class//g; s/public\s\+\(static\)\?//g; s/private\s\+//g; s/protected\s\+//g' 2>/dev/null)
|
|
467
|
+
local is_public=$(echo "$content_without_java_modifiers" | grep -oiE '(login|captcha|验证码|健康检查|health|dict|字典|enum|枚举|公开接口|no-data-scope)' || true)
|
|
468
|
+
if [ -n "$is_public" ]; then
|
|
469
|
+
log_debug "Hook F: 公开/登录/字典接口,跳过"
|
|
470
|
+
return
|
|
471
|
+
fi
|
|
472
|
+
|
|
473
|
+
# 提取接口方法名用于提示
|
|
474
|
+
local method_name=$(echo "$EDIT_CONTENT" | grep -oiE '(list[A-Z]\w+|page[A-Z]\w+|query[A-Z]\w+|search[A-Z]\w+|get[A-Z]\w+|detail[A-Z]\w+|export[A-Z]\w+|update[A-Z]\w+|delete[A-Z]\w+|refund[A-Z]\w+|approve[A-Z]\w+)' | head -1 || true)
|
|
475
|
+
|
|
476
|
+
# 检查是否为写操作
|
|
477
|
+
local is_write_op=$(echo "$EDIT_CONTENT" | grep -oiE '(update|delete|refund|approve|编辑|删除|退款|审批|@PostMapping|@PutMapping|@DeleteMapping)' || true)
|
|
478
|
+
|
|
479
|
+
# 查找 api.md 和 design.md
|
|
480
|
+
local api_md_path=$(find "$REPO_ROOT/doc" -name "api.md" -type f 2>/dev/null | head -1)
|
|
481
|
+
local design_md_path=$(find "$REPO_ROOT/doc" -name "design.md" -type f 2>/dev/null | head -1)
|
|
482
|
+
|
|
483
|
+
# 检查文档中是否有数据权限说明
|
|
484
|
+
local has_permission_doc=""
|
|
485
|
+
for md_file in "$api_md_path" "$design_md_path"; do
|
|
486
|
+
if [ -f "$md_file" ]; then
|
|
487
|
+
has_permission_doc=$(grep -oiE '(数据权限|权限边界|权限决策|角色.*范围|越权|待确认.*权限|不需要.*权限|统一.*AOP|统一.*注解|网关.*控制)' "$md_file" 2>/dev/null || true)
|
|
488
|
+
if [ -n "$has_permission_doc" ]; then
|
|
489
|
+
break
|
|
490
|
+
fi
|
|
491
|
+
fi
|
|
492
|
+
done
|
|
493
|
+
|
|
494
|
+
# 检查代码中是否使用了权限相关模式
|
|
495
|
+
local has_permission_code=$(echo "$EDIT_CONTENT" | grep -oiE '(DataScope|DataPermission|@RequiresPermissions|@PreAuthorize|@Secured|checkPermission|getDataScope|getCurrentUser|getLoginUser|SecurityUtils|UserContext|TenantContext|@DataFilter)' || true)
|
|
496
|
+
|
|
497
|
+
# 检查文档是否标记"不确定"
|
|
498
|
+
local has_pending=$(echo "$has_permission_doc" | grep -oi '待确认' || true)
|
|
499
|
+
|
|
500
|
+
if [ -n "$has_pending" ]; then
|
|
501
|
+
# 文档标记不确定,提示阻塞实现
|
|
502
|
+
log_warning "Hook F [数据权限决策]: api.md 已标记数据权限\"不确定\",实现应阻塞,等待用户确认角色数据范围"
|
|
503
|
+
return
|
|
504
|
+
fi
|
|
505
|
+
|
|
506
|
+
if [ -z "$has_permission_doc" ] && [ -z "$has_permission_code" ]; then
|
|
507
|
+
# 无文档也无代码权限迹象
|
|
508
|
+
if [ -n "$is_write_op" ]; then
|
|
509
|
+
# 写操作高风险
|
|
510
|
+
log_warning "Hook F [数据权限决策][HIGH] 检测到写操作接口 ${method_name:-未识别方法名}。
|
|
511
|
+
未发现当前用户上下文、权限注解、归属校验或既有权限工具调用;
|
|
512
|
+
文档也未说明由统一 AOP/网关控制。
|
|
513
|
+
请确认是否存在越权风险;如由统一机制处理,请在 design.md/api.md 中注明。"
|
|
514
|
+
else
|
|
515
|
+
# 读操作普通风险
|
|
516
|
+
log_warning "Hook F [数据权限决策] 检测到新增接口 ${method_name:-未识别方法名}。
|
|
517
|
+
未在 api.md/design.md 中找到数据权限说明。
|
|
518
|
+
请确认:
|
|
519
|
+
1. 该接口是否访问业务数据;
|
|
520
|
+
2. 若访问业务数据,是否需要数据权限(需要/不需要/不确定);
|
|
521
|
+
3. 若需要,复用当前仓库哪个已有权限模式;
|
|
522
|
+
4. 若不确定,请列为待确认问题并询问用户。"
|
|
523
|
+
fi
|
|
524
|
+
fi
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
###############################################################################
|
|
528
|
+
# Hook G:外部 SDK 参数契约检查
|
|
529
|
+
#
|
|
530
|
+
# 触发条件:
|
|
531
|
+
# - 变更包含 SDK client、RPC client、MQ client、第三方 API client
|
|
532
|
+
# - 或 pom 里 SDK 版本变化
|
|
533
|
+
#
|
|
534
|
+
# 排除条件:
|
|
535
|
+
# - 纯内部工具类
|
|
536
|
+
#
|
|
537
|
+
# 检查规则:
|
|
538
|
+
# - api.md/design.md 必须写 SDK 参数来源
|
|
539
|
+
# - tests.md 必须写真实响应或阻塞证据
|
|
540
|
+
# - SDK 版本变更必须记录原因
|
|
541
|
+
#
|
|
542
|
+
# 严重级别:
|
|
543
|
+
# - SDK 参数新增但无来源说明:warning
|
|
544
|
+
# - SDK 版本变更但无说明:warning
|
|
545
|
+
###############################################################################
|
|
546
|
+
|
|
547
|
+
hook_g_sdk_contract() {
|
|
548
|
+
case "$FILE_PATH" in
|
|
549
|
+
*.java|*.xml|pom.xml|build.gradle) ;;
|
|
550
|
+
*) return ;;
|
|
551
|
+
esac
|
|
552
|
+
|
|
553
|
+
# 触发条件
|
|
554
|
+
local has_sdk=$(echo "$EDIT_CONTENT" | grep -oiE '(Client|Sdk|SDK|RPC|Feign|Dubbo|MQ|Kafka|RabbitMQ|RocketMQ|HttpClient|RestTemplate|WebClient)' || true)
|
|
555
|
+
local has_sdk_import=$(echo "$EDIT_CONTENT" | grep -oiE 'import.*\.(sdk|client|rpc|feign|dubbo|mq)' || true)
|
|
556
|
+
|
|
557
|
+
if [ -z "$has_sdk" ] && [ -z "$has_sdk_import" ]; then
|
|
558
|
+
return
|
|
559
|
+
fi
|
|
560
|
+
|
|
561
|
+
# 排除条件:纯内部工具类
|
|
562
|
+
local is_utility=$(echo "$FILE_PATH" | grep -oiE '(util|Utils|helper|Helper|tool|Tool)' || true)
|
|
563
|
+
if [ -n "$is_utility" ] && [ -z "$has_sdk_import" ]; then
|
|
564
|
+
log_debug "Hook G: 纯工具类,跳过"
|
|
565
|
+
return
|
|
566
|
+
fi
|
|
567
|
+
|
|
568
|
+
log_debug "Hook G: 检测到外部 SDK/RPC/MQ 调用"
|
|
569
|
+
|
|
570
|
+
# 检查参数来源说明
|
|
571
|
+
local has_param_source=$(echo "$EDIT_CONTENT" | grep -oiE '(参数来源|来源说明|param.*from|参数.*传)' || true)
|
|
572
|
+
|
|
573
|
+
# 检查 api.md/design.md
|
|
574
|
+
local api_md_path=$(find "$REPO_ROOT/doc" -name "api.md" -type f 2>/dev/null | head -1)
|
|
575
|
+
local design_md_path=$(find "$REPO_ROOT/doc" -name "design.md" -type f 2>/dev/null | head -1)
|
|
576
|
+
|
|
577
|
+
local sdk_doc_found=""
|
|
578
|
+
for md_file in "$api_md_path" "$design_md_path"; do
|
|
579
|
+
if [ -f "$md_file" ]; then
|
|
580
|
+
sdk_doc_found=$(grep -oi '外部依赖契约\|SDK.*参数来源\|SDK.*版本' "$md_file" 2>/dev/null || true)
|
|
581
|
+
if [ -n "$sdk_doc_found" ]; then
|
|
582
|
+
break
|
|
583
|
+
fi
|
|
584
|
+
fi
|
|
585
|
+
done
|
|
586
|
+
|
|
587
|
+
if [ -z "$sdk_doc_found" ]; then
|
|
588
|
+
log_warning "Hook G [SDK 契约]: 检测到外部 SDK/RPC/MQ 调用,但 api.md/design.md 未定义外部依赖契约(参数来源、SDK 版本、响应样例)"
|
|
589
|
+
fi
|
|
590
|
+
|
|
591
|
+
# 检查 pom.xml 版本变更
|
|
592
|
+
case "$FILE_PATH" in
|
|
593
|
+
*pom.xml*|*build.gradle*)
|
|
594
|
+
local version_change=$(echo "$EDIT_CONTENT" | grep -oiE '(version|<version>)' || true)
|
|
595
|
+
if [ -n "$version_change" ]; then
|
|
596
|
+
log_warning "Hook G [SDK 契约]: 检测到依赖版本变更,请确认是否有版本变更说明"
|
|
597
|
+
fi
|
|
598
|
+
;;
|
|
599
|
+
esac
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
###############################################################################
|
|
603
|
+
# Hook H:跨仓数据合同 / MyBatis-Plus 实体字段检查
|
|
604
|
+
#
|
|
605
|
+
# 触发条件:
|
|
606
|
+
# - Java/XML 变更包含 @TableName、BaseMapper、@TableField
|
|
607
|
+
# - 或包含 QueryWrapper/LambdaQueryWrapper/resultMap/列映射
|
|
608
|
+
# - 或文档声称真实链路/联调通过
|
|
609
|
+
#
|
|
610
|
+
# 检查规则:
|
|
611
|
+
# - MyBatis-Plus 实体字段必须能在真实表结构中找到
|
|
612
|
+
# - 旧字段应删除或 @TableField(exist = false)
|
|
613
|
+
# - 多仓共享表必须列出消费仓并做字段对账
|
|
614
|
+
# - mock/test controller 证据不能替代真实入口验收
|
|
615
|
+
#
|
|
616
|
+
# 严重级别:
|
|
617
|
+
# - warning/high warning,不直接全局失败;由 SDD quality gate 阻塞交付
|
|
618
|
+
###############################################################################
|
|
619
|
+
|
|
620
|
+
hook_h_cross_schema_contract() {
|
|
621
|
+
case "$FILE_PATH" in
|
|
622
|
+
*.java|*.xml|*.md) ;;
|
|
623
|
+
*) return ;;
|
|
624
|
+
esac
|
|
625
|
+
|
|
626
|
+
local has_schema_mapping=$(echo "$EDIT_CONTENT" | grep -oiE '(@TableName|BaseMapper<|@TableField|LambdaQueryWrapper|QueryWrapper|resultMap|<result[[:space:]]+column=|<id[[:space:]]+column=)' || true)
|
|
627
|
+
local has_real_claim=$(echo "$EDIT_CONTENT" | grep -oiE '(真实链路通过|真实入口通过|联调通过|Passed|Real integration passed)' || true)
|
|
628
|
+
local has_mock_signal=$(echo "$EDIT_CONTENT" | grep -oiE '(mock|Mock|测试端点|test controller|绕过鉴权|bypass)' || true)
|
|
629
|
+
|
|
630
|
+
if [ -n "$has_schema_mapping" ]; then
|
|
631
|
+
log_warning "Hook H [跨仓数据合同][HIGH] 检测到 MyBatis/MyBatis-Plus 实体、Mapper 或查询条件变更。
|
|
632
|
+
请完成跨仓数据合同对账:
|
|
633
|
+
1. 明确表结构真源(版本总 SQL / database-contract / SHOW CREATE TABLE);
|
|
634
|
+
2. 列出全部消费仓(当前服务、sibling service、互联互通、回调、定时任务、测试端点);
|
|
635
|
+
3. 对照 @TableName 实体字段、BaseMapper 默认 SELECT、Mapper XML/resultMap、手写 SQL 与真实库字段;
|
|
636
|
+
4. 不存在列必须删除或 @TableField(exist = false),不得给测试库补废弃字段绕过;
|
|
637
|
+
5. 字段迁移后同步查询条件,例如状态字段派生、多站点 JSON 快照等。"
|
|
638
|
+
fi
|
|
639
|
+
|
|
640
|
+
case "$FILE_PATH" in
|
|
641
|
+
*.md)
|
|
642
|
+
if [ -n "$has_real_claim" ] && [ -n "$has_mock_signal" ]; then
|
|
643
|
+
log_warning "Hook H [真实入口验收][HIGH] 文档同时出现真实通过结论和 mock/测试端点证据。
|
|
644
|
+
请把 Mock 验证、测试端点验证、真实入口验证分开记录;
|
|
645
|
+
测试 Controller、mock endpoint、绕过鉴权端点只能证明局部路径;
|
|
646
|
+
真实入口通过必须包含 payload、响应、trace 日志、DB 证据。"
|
|
647
|
+
fi
|
|
648
|
+
;;
|
|
649
|
+
esac
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
###############################################################################
|
|
653
|
+
# Hook I:Superpowers HOW/合同权责边界检查
|
|
654
|
+
#
|
|
655
|
+
# 触发条件:
|
|
656
|
+
# - Markdown 变更包含 superpower / Superpower
|
|
657
|
+
# - 尤其是 prompt 或 design 相关文档
|
|
658
|
+
#
|
|
659
|
+
# 检查规则:
|
|
660
|
+
# - OpenSpec/SDD 文档是 WHAT/API/DB/tests 合同事实源
|
|
661
|
+
# - Superpowers 可以接管源码级 HOW 技术详设
|
|
662
|
+
# - Superpowers 不得重写 design.md/api.md/tests 或覆盖 API/DB/字段语义合同
|
|
663
|
+
#
|
|
664
|
+
# 严重级别:
|
|
665
|
+
# - 明确要求 Superpowers 覆盖合同:fail
|
|
666
|
+
# - 使用 Superpowers 但缺少技术详设继承章节:warning
|
|
667
|
+
###############################################################################
|
|
668
|
+
|
|
669
|
+
hook_i_superpower_design_boundary() {
|
|
670
|
+
case "$FILE_PATH" in
|
|
671
|
+
*.md) ;;
|
|
672
|
+
*) return ;;
|
|
673
|
+
esac
|
|
674
|
+
|
|
675
|
+
local has_superpower=$(echo "$CHECK_CONTENT" | grep -oiE 'superpower|Superpower' || true)
|
|
676
|
+
if [ -z "$has_superpower" ]; then
|
|
677
|
+
return
|
|
678
|
+
fi
|
|
679
|
+
|
|
680
|
+
local risky_line=$(echo "$CHECK_CONTENT" | python3 -c '
|
|
681
|
+
import re, sys
|
|
682
|
+
text = sys.stdin.read()
|
|
683
|
+
allow = re.compile(r"(禁止|不得|不允许|不能|事实源|canonical|must not|do not|forbid)", re.I)
|
|
684
|
+
risky = re.compile(
|
|
685
|
+
r"(superpower|Superpower).{0,40}(重写|替代|覆盖).{0,40}(design\.md|api\.md|tests\.md|API|DB|SQL|字段语义|合同|验收)"
|
|
686
|
+
r"|"
|
|
687
|
+
r"(重写|替代|覆盖).{0,40}(design\.md|api\.md|tests\.md|API|DB|SQL|字段语义|合同|验收).{0,40}(superpower|Superpower)",
|
|
688
|
+
re.I,
|
|
689
|
+
)
|
|
690
|
+
for line in text.splitlines():
|
|
691
|
+
if risky.search(line) and not allow.search(line):
|
|
692
|
+
print(line[:240])
|
|
693
|
+
break
|
|
694
|
+
' 2>/dev/null)
|
|
695
|
+
|
|
696
|
+
if [ -n "$risky_line" ]; then
|
|
697
|
+
log_fail "Hook I [Superpowers 边界]: 检测到疑似让 Superpowers 覆盖 OpenSpec/SDD 合同的指令:$risky_line
|
|
698
|
+
OpenSpec/SDD 的 design.md、api.md、tests.md、SQL/DB、字段语义和验收门禁是合同事实源;
|
|
699
|
+
Superpowers 可以接管源码级 HOW 技术详设,但不能覆盖合同。"
|
|
700
|
+
fi
|
|
701
|
+
|
|
702
|
+
case "$FILE_PATH" in
|
|
703
|
+
*/prompt/*.md|*prompt*.md)
|
|
704
|
+
local has_strategy=$(echo "$CHECK_CONTENT" | grep -oiE 'Superpower 技术详设继承|technical_design|Superpowers 技术详设|源码级 HOW|Superpower 执行策略继承' || true)
|
|
705
|
+
if [ -z "$has_strategy" ]; then
|
|
706
|
+
log_warning "Hook I [Superpowers 边界]: prompt 使用了 Superpowers,但未看到 'Superpower 技术详设继承' 或 technical_design 边界。
|
|
707
|
+
请从 .sdd/state.yaml 的 technical_design 继承源码级 HOW,并声明 OpenSpec/SDD 合同不可覆盖。"
|
|
708
|
+
fi
|
|
709
|
+
local has_field_status_risk=$(echo "$CHECK_CONTENT" | grep -oiE '字段值|状态|枚举|online|offline|上线|下线|删除|恢复|同步标记|payment|refund|支付|退款|第三方状态|running_status|offline_time' || true)
|
|
710
|
+
local has_reverse_impact=$(echo "$CHECK_CONTENT" | grep -oiE '字段/状态反向影响面|Field And Status Reverse Impact|读取/过滤点|派生/同步点|跨模块消费方' || true)
|
|
711
|
+
if [ -n "$has_field_status_risk" ] && [ -z "$has_reverse_impact" ]; then
|
|
712
|
+
log_warning "Hook I [字段/状态反向影响面]: prompt 涉及字段/状态/枚举/同步值,但未看到反向影响面矩阵。
|
|
713
|
+
请从 Superpowers technical_design 继承 writers/readers/filters/sync/consumer/test 矩阵,避免只改直接写入点。"
|
|
714
|
+
fi
|
|
715
|
+
;;
|
|
716
|
+
esac
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
###############################################################################
|
|
720
|
+
# 执行所有检查器
|
|
721
|
+
###############################################################################
|
|
722
|
+
|
|
723
|
+
hook_a_file_stream
|
|
724
|
+
hook_b_import_failure_code
|
|
725
|
+
hook_c_excel_date
|
|
726
|
+
hook_d_excel_header_consistency
|
|
727
|
+
hook_e_route_consistency
|
|
728
|
+
hook_f_permission_decision
|
|
729
|
+
hook_g_sdk_contract
|
|
730
|
+
hook_h_cross_schema_contract
|
|
731
|
+
hook_i_superpower_design_boundary
|
|
732
|
+
|
|
733
|
+
# 输出结果
|
|
734
|
+
if [ -n "$WARNINGS" ]; then
|
|
735
|
+
echo "$WARNINGS"
|
|
736
|
+
echo ""
|
|
737
|
+
echo "[SDD 契约检查] 详细说明请参考 superflow-pipeline/references/api-design-template.md"
|
|
738
|
+
fi
|
|
739
|
+
|
|
740
|
+
if [ -n "$HAS_FAIL" ]; then
|
|
741
|
+
exit 2
|
|
742
|
+
fi
|
|
743
|
+
|
|
744
|
+
exit 0
|