Commit b7e53da7 authored by 刘正强's avatar 刘正强

谁上传就属谁:拜访报告签名强制=上传者(git 用户),校验脚本/hook/文档同步更新

parent d8770d0c
__pycache__/
*.pyc
*.pyo
......@@ -6,6 +6,7 @@
1. 报告文件:`客户/<客户名>/YYYY-MM-DD_客户名_帛丝人员_拜访报告.md`
2. 文件名中的"客户名"必须与所在文件夹名一致;日期用 `YYYY-MM-DD`
3. 每个客户一个文件夹,不得混放其它客户报告。
4. **署名(谁上传就属谁)**:文件名中的"帛丝人员"与正文"拜访人(帛丝人员)"必须等于上传者本人(当前 git 用户名)。校验时用 `--owner <git用户>` 强制;不符即拒绝。
## 报告必填格式(必填门)
报告必须包含以下五节,每节必须填写**实际内容**(去除 HTML 注释 `<!-- -->` 与占位符 `<…>` 后仍有文字):
......@@ -19,14 +20,14 @@
- 四、问题清单及下一步计划
- 五、其他补充
> 缺节、空节、仅注释/占位符、命名/路径不符 → 一律判为不合规。
> 缺节、空节、仅注释/占位符、命名/路径不符、署名非本人 → 一律判为不合规。
## 准入动作(铁律)
1. 生成或修改任何 `*_拜访报告.md` 后,**必须**先运行校验:
1. 生成或修改任何 `*_拜访报告.md` 后,**必须**先运行校验(带署名校验时传 `--owner`,值取 `git config user.name`
```bash
python _validation/validate_report.py "<报告路径>"
python _validation/validate_report.py "<报告路径>" --owner "<git用户名>"
```
2. 校验失败 → 不得 commit / push,应列出缺哪节、缺什么,让用户补全后再提交。
2. 校验失败 → 不得 commit / push,应列出缺哪节、缺什么、署名是否本人,让用户补全后再提交。
3. 可整体扫描:
```bash
python _validation/validate_report.py
......@@ -35,5 +36,6 @@
5. 报告被视为受控对象:写入即留痕(git),如需改历史请走正常 git 提交,不删信息源。
## 属性默认值
- 客户名、日期、拜访人(帛丝人员):从文件名解析(`日期_客户_人员`),不必在正文重复,但正文元数据行建议保留。
- 客户名、日期:从文件名解析(`日期_客户_人员`)。
- **拜访人(帛丝人员)=上传者本人(git 用户名),由 pre-push / 校验脚本自动比对,不由 AI 代填或推测。**
- 报告补充项(如项目负责人、系统、服务状态):为可选项,不作为必填门。
......@@ -44,7 +44,8 @@ laokehu-project-kb/
- 文件:`YYYY-MM-DD_客户名_帛丝人员_拜访报告.md`
- 位置:`客户/<客户名>/`(客户名与文件名中的客户名必须一致)
- 示例:`客户/丰隆/2026-08-20_丰隆_戴堂亮_拜访报告.md`
- 示例:`客户/丰隆/2026-08-20_丰隆_刘正强_拜访报告.md`
- **署名(谁上传就属谁)**:文件名里的"帛丝人员"以及正文里的"拜访人(帛丝人员)",**必须等于你本人(当前 git 用户名)**`pre-push` 会自动校验,署名不符会被拒绝上传,不允许替别人或让别人的名字上报。
## 校验方式
......
......@@ -2,7 +2,9 @@
> 内部项目记录|<YYYY-MM-DD>
>
> 客户名称:<客户名> | 拜访人(帛丝人员):<姓名> | 日期:<YYYY-MM-DD>
> 客户名称:<客户名> | 拜访人(帛丝人员):<你的姓名,且必须与 git 用户名一致> | 日期:<YYYY-MM-DD>
>
> 署名规则:谁上传就属谁 —— 文件名里的"帛丝人员"和上面这行的拜访人,必须等于当前 git 用户名本人,否则会被拒绝上传。
## 一、项目背景
......
......@@ -7,11 +7,13 @@ laokehu-project-kb 拜访报告校验器(必填门)
1. 文件必须位于 客户/<客户名>/ 下,且为 *_拜访报告.md
2. 文件名必须符合命名约定:YYYY-MM-DD_客户名_帛丝人员_拜访报告.md,且"客户名"与所在文件夹名一致
3. 必须包含五大必填节标题,且每个必填节都有实际内容(去掉 HTML 注释和占位符后非空)
4. (可选 --owner)署名归属:报告"拜访人"必须等于上传者(当前 git 用户)——谁上传就属谁
用法:
python _validation/validate_report.py <报告.md> 校验单个文件
python _validation/validate_report.py --tree 扫描 客户/ 目录下所有报告,汇总结果(默认)
python _validation/validate_report.py --stdin --name xxx.md 从 stdin 读 blob 内容校验(供服务器 hook 用)
python _validation/validate_report.py <报告.md> 校验单个文件
python _validation/validate_report.py <报告.md> --owner <git用户> 校验单个文件并校验归属
python _validation/validate_report.py --tree 扫描 客户/ 目录下所有报告,汇总结果(默认)
python _validation/validate_report.py --stdin --name xxx.md --owner <git用户> 从 stdin 读 blob 校验(服务器 hook 用)
可选:--quiet(只打印通过/失败),--json(结构化输出),--report-path <glob>(扫描子集)
退出码:
......@@ -126,7 +128,26 @@ def check_path(path):
return parts, []
def validate_file(path, quiet=False):
# ---- 署名归属:谁上传就属谁 ---- (从文件名/正文取拜访人,与当前 git 用户比对)
OWNER_IN_TEXT = re.compile(r"拜访人(帛丝人员)\s*[::]\s*([^||\s ]+)")
def check_owner(filename, text, owner):
"""校验报告归属 = 上传者(当前 git 用户)。只校验文件名与正文中声明的拜访人。"""
if not owner:
return []
errors = []
m = FILENAME_RE.match(filename)
if m and m.group(3) != owner:
errors.append(f"文件署名与上传者不一致:文件名=“{m.group(3)}”,上传者(git 用户)=“{owner}”")
if text:
tm = OWNER_IN_TEXT.search(text)
if tm and tm.group(1).strip() != owner:
errors.append(f"正文拜访人与上传者不一致:正文=“{tm.group(1).strip()}”,上传者(git 用户)=“{owner}”")
return errors
def validate_file(path, quiet=False, owner=None):
path = Path(path)
name = path.name
if not path.exists():
......@@ -149,6 +170,7 @@ def validate_file(path, quiet=False):
if text is not None:
problems += check_content(text)
problems += check_owner(name, text, owner)
ok = len(problems) == 0
if not quiet:
......@@ -176,6 +198,10 @@ def main():
args = sys.argv[1:]
quiet = "--quiet" in args
as_json = "--json" in args
owner = None
if "--owner" in args:
i = args.index("--owner")
owner = args[i + 1]
report_glob = None
if "--report-path" in args:
i = args.index("--report-path")
......@@ -188,6 +214,7 @@ def main():
name = args[i + 1]
text = sys.stdin.read()
problems = check_content(text)
problems += check_owner(name or "", text, owner)
if name:
m = FILENAME_RE.match(name)
if not m:
......@@ -202,7 +229,7 @@ def main():
return 0 if ok else 1
if args and not args[0].startswith("--"):
ok, problems = validate_file(args[0], quiet=quiet)
ok, problems = validate_file(args[0], quiet=quiet, owner=owner)
if as_json:
print(json.dumps({"ok": ok, "file": args[0], "errors": problems}, ensure_ascii=False))
return 0 if ok else 1
......
......@@ -7,6 +7,9 @@ RC=0
ROOT=$(git rev-parse --show-toplevel) || exit 1
VALIDATOR="$ROOT/_validation/validate_report.py"
# 谁上传就属谁:以当前 git 用户作为"拜访人"归属
PUSHER=$(git config user.name)
# 收集本次推送涉及的 .md 报告文件
REPORTS=""
......@@ -38,7 +41,7 @@ fi
echo "laokehu 校验:本次推送涉及 $(printf '%s\n' "$REPORTS" | sed '/^$/d' | wc -l | tr -d ' ') 份拜访报告,正在校验…"
for f in $REPORTS; do
if python "$VALIDATOR" "$ROOT/$f" >/dev/null 2>&1; then
if python "$VALIDATOR" "$ROOT/$f" --owner "$PUSHER" >/dev/null 2>&1; then
:
else
echo "✗ 不合规:$f"
......@@ -51,5 +54,6 @@ if [ "$RC" -ne 0 ]; then
echo "提交被拒:有拜访报告未满足《必填门》格式要求,请按模板补全后再推送。"
echo " - 五必备节:一、项目背景 | 二、拜访事前准备 | 三、本次实际拜访结果 | 四、问题清单及下一步计划 | 五、其他补充"
echo " - 命名:YYYY-MM-DD_客户名_帛丝人员_拜访报告.md(放置于 客户/<客户名>/ 下)"
echo " - 署名:拜访人必须是你本人(当前 git 用户:$PUSHER)——谁上传就属谁"
fi
exit $RC
......@@ -20,10 +20,12 @@ while read old new ref; do
RANGE="$old..$new"
fi
CHANGED=$(git -c core.quotepath=false diff --name-only --diff-filter=ACMRT "$RANGE" 2>/dev/null)
# 谁上传就属谁:以本次提交头部的作者名作为"拜访人"归属
OWNER=$(git log -1 --format='%an' "$new" 2>/dev/null)
for p in $CHANGED; do
case "$p" in
客户/*_拜访报告.md)
if git show "$new:$p" 2>/dev/null | "$PYTHON" "$VALIDATOR" --path "$p" --name "$(basename "$p")" -; then
if git show "$new:$p" 2>/dev/null | "$PYTHON" "$VALIDATOR" --path "$p" --name "$(basename "$p")" --owner "$OWNER" -; then
:
else
echo "✗ [pre-receive] 不合规:$p" >&2
......@@ -43,6 +45,7 @@ if [ "$RC" -ne 0 ]; then
printf '%s\n' "$BAD_PATHS" | sed '/^$/d' | sed 's/^/ - /' >&2
echo "规则:五大必填节(项目背景/拜访事前准备/本次实际拜访结果/问题清单及下一步计划/其他补充)均须有实际内容;" >&2
echo "命名:YYYY-MM-DD_客户名_帛丝人员_拜访报告.md,位于 客户/<客户名>/ 下。" >&2
echo "署名:拜访人必须等于提交者本人(谁上传就属谁)。" >&2
exit $RC
fi
......
......@@ -10,6 +10,7 @@ import sys
import json
FILENAME_RE = re.compile(r"^(\d{4}-\d{2}-\d{2})_(.+)_(.+)_拜访报告\.md$")
OWNER_IN_TEXT = re.compile(r"拜访人(帛丝人员)\s*[::]\s*([^||\s ]+)")
HTML_COMMENT = re.compile(r"<!--.*?-->", re.S)
PLACEHOLDER_TAG = re.compile(r"<[^>]*>|【[^】]*】|[[^]]*]|\(填写\)|(填写)|待填|填写|xxx", re.S)
HEADING = re.compile(r"^\s*#{1,6}\s", re.M)
......@@ -74,14 +75,31 @@ def check_content(text):
return errors
def check_owner(name, text, owner):
"""谁上传就属谁:文件名/正文声明的拜访人必须等于 owner。"""
if not owner:
return []
errors = []
m = FILENAME_RE.match(name)
if m and m.group(3) != owner:
errors.append(f"文件署名与上传者不一致:文件名=“{m.group(3)}”,上传者=“{owner}”")
tm = OWNER_IN_TEXT.search(text)
if tm and tm.group(1).strip() != owner:
errors.append(f"正文拜访人与上传者不一致:正文=“{tm.group(1).strip()}”,上传者=“{owner}”")
return errors
def main():
args = sys.argv[1:]
name = None
path = None
owner = None
if "--name" in args:
name = args[args.index("--name") + 1]
if "--path" in args:
path = args[args.index("--path") + 1]
if "--owner" in args:
owner = args[args.index("--owner") + 1]
text = sys.stdin.read()
......@@ -93,6 +111,7 @@ def main():
if not m:
problems.append(f"文件名不符合命名约定:{name}")
problems += check_content(text)
problems += check_owner(name or "", text, owner)
ok = len(problems) == 0
if "--json" in args:
......
......@@ -17,7 +17,7 @@
```
YYYY-MM-DD_客户名_帛丝人员_拜访报告.md
```
例:`客户/丰隆/2026-08-20_丰隆_戴堂亮_拜访报告.md`
例:`客户/丰隆/2026-08-20_丰隆_刘正强_拜访报告.md`
2. 建议从 `_template/拜访报告_模板.md` 复制内容再填。
3. 把五节里的 `<!-- 填写:… -->` **替换成真实内容**(删除注释,写入内容)。五节:
- 一、项目背景
......@@ -26,6 +26,7 @@
- 四、问题清单及下一步计划
- 五、其他补充
4. 正文顶部元数据行:`客户名称 / 拜访人 / 日期` 填上。
5. **署名=你本人**:文件名里的"帛丝人员"以及正文"拜访人(帛丝人员)"必须写**你自己(当前 git 用户名)**。上传时脚本会自动取当前 git 用户比对,写到别人名字会被拒绝。想确认自己的 git 用户名:`git config user.name`。
## 三、提交与推送
```bash
......@@ -33,13 +34,14 @@ git add 客户/
git commit -m "拜访报告:<客户名> <日期>"
git push
```
- 若格式不合规,push 会被拒绝,屏幕会提示缺哪节/哪项,按提示补全后重新 `git add && git commit && git push`。
- 也可提前自查:
- 若格式不合规,push 会被拒绝,屏幕会提示缺哪节/哪项/署名是否本人,按提示补全后重新 `git add && git commit && git push`。
- 也可提前自查(带署名校验)
```bash
python _validation/validate_report.py "客户/<客户名>/<文件>.md"
python _validation/validate_report.py "客户/<客户名>/<文件>.md" --owner "$(git config user.name)"
```
## 四、常见问题
- **我用的 GitLab / 网页上传**:网页上传不经过本地 hook,无法自动拦截,我只能在你上传后扫描。建议尽量用 git 客户端 push。
- **想提交但校验说"缺少必填节"**:确认 markdown 里五节标题一个不少,且每节下有真实文字(不是空的注释)。
- **提示客户名不一致**:检查文件名里的客户名是否和所在文件夹名完全一致。
- **提示"署名与上传者不一致"**:说明文件里的拜访人(文件名或正文)写了别人,或你本机 git 用户名没设对。先 `git config user.name` 看你的用户名,再把报告署名改成你自己。
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment