Files
Neo-ZQYY/apps/backend/start_uvicorn.py
Neo 95a4500c75 chore(ops): reload 卡死三层预防 + F1-5a 完整走查报告
reload 卡死三层预防(走查中遭遇 uvicorn graceful shutdown 死等触发):
- Layer 1 (apps/backend/start_uvicorn.py 新): 把 reload-excludes
  封装在 Python 字符串内,ps1 命令行只有字面路径,根治 PowerShell
  PSNativeCommandArgumentPassing 在不同 profile 下 wildcard 展开
  行为差异(数组 splatting 和 --% 都不稳)。同时显式设
  timeout-graceful-shutdown=5,5 秒强杀防死等
- Layer 2 (scripts/ops/backend-watchdog.ps1 新): 自主 socket 探针
  (TcpClient + 手写 HTTP/1.1 GET,Connection: close)规避 .NET
  HttpClient pool 复用 + 系统代理误报;3s × 3 = 9s 触发重启;
  进程链 kill 至 pwsh 后端窗口(关闭原窗口);3 次/小时上限自停
- Layer 3 (scripts/ops/start-admin.ps1): 启动时拉起 watchdog,
  菜单 [4] 仅重启后端选项,主菜单退出时一并 kill 看门狗

CLAUDE.md: 新增"后端 reload 卡死预防(强制)"章节,
分级文件风险表 + SOP + 启动菜单速查

走查报告(应查尽查严肃版):
- 后端 6 个改造点 PASS(P1-P4 + GUC + ai_run_logs runtime 字段)
- admin-web 7 页 Playwright 实地走查 → 5 项 UI 不完整登记 F1-5b
- 小程序看板 tab 7 页 weixin-devtools-mcp 实地 + DB 数据核对 →
  board-finance 5/6 项上界裁剪吻合;board-customer 业务日生效;
  board-coach 月度聚合表设计盲区;5 项 sandbox 覆盖盲区登记 F1-5b
- 8 张走查截图归档 docs/audit/changes/screenshots/2026-05-05_f1_5a_walkthrough/

audit_dashboard 刷新到 153 条审计

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 11:53:08 +08:00

45 lines
1.3 KiB
Python

"""后端 uvicorn 启动入口(规避 PowerShell wildcard 展开问题)。
CHANGE 2026-05-05 | F1-5a 走查发现:PS 7.6.1 即使用 --% stop-parsing token
仍在某些环境下展开 reload-exclude 的 wildcard,导致 click 报 "extra arguments"
解决:把所有 wildcard 字符串封装在 Python 内部,ps1 只调本脚本,
PowerShell shell 不接触任何 wildcard 字符串。
参数解析:--port N(默认 8000),其他参数硬编码。
"""
from __future__ import annotations
import argparse
import sys
import uvicorn
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--port", type=int, default=8000)
parser.add_argument("--host", type=str, default="127.0.0.1")
args = parser.parse_args()
uvicorn.run(
"app.main:app",
host=args.host,
port=args.port,
reload=True,
# 业务规则:5 秒 graceful shutdown 失败强杀,避免 reload 卡死
timeout_graceful_shutdown=5,
# F1-5a 走查 reload 卡死预防:
# tests/__pycache__/.md 改动不应触发 reload(浪费且潜在卡死风险)
reload_excludes=[
"tests/*",
"**/__pycache__/*",
"*.md",
],
use_colors=False,
)
return 0
if __name__ == "__main__":
sys.exit(main())