Files
group_fqcd_jr/tools/make_launcher_bat.py
T
张胜宇 e239eb778b docs: 品牌全量口径统一为「南方基金」+ 作废文档清理
1) 客服 Agent 四份交付文档 + 构建脚手架:品牌由包装占位 XX科技 / 旧名 南方财富
   统一为南方基金(热线 400-889-8899 / 官网 nffund.com),系统名改为「智能服务系统」;
   同步追加 §0.4 修订记录行,工程记录行保留原占位字面以支撑硬编码扫描验收。
2) 开发文档:清理 28 份已作废/残留文档(14 份移出归档 + 14 份仓库副本),
   新增《文档规整方案与开发前待决事项-2026-09-17》。
3) 客服agent 四份交付文档首次纳入本分支。
2026-09-17 15:15:22 +08:00

164 lines
6.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""生成"双击即用"的启动 bat。
## 为什么用脚本生成而不是直接写 .bat 文件
两个编码陷阱,靠普通文本工具写必然踩:
1. **cmd 按系统 ANSI 解析批处理**(简体中文下是 GBK)。同样一段中文,
存成 UTF-8 后在 cmd 里就是乱码 —— 所以 bat 必须写成 **GBK**。
2. **`start.ps1` 必须带 BOM**(见该文件自己的 .NOTES)。本脚本顺带校验,
少了就补,避免"双击后满屏语法错误"。
## 为什么 bat 里只调 start.ps1
启动逻辑(解释器发现、依赖检查、行情刷新、幂等起窗口、就绪探测)都在
`start.ps1` 里,且已经能单独跑。bat 只负责"双击"这一层:
定位项目 → 切代码页 → 交给 PowerShell → 留住窗口让人看到账号和报错。
逻辑只有一份,不会两边漂移。
"""
from __future__ import annotations
import sys
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[1]
BAT_TEMPLATE = r"""@echo off
chcp 936 >nul
title 金融 Agent 平台 · 一键启动
rem ============================================================
rem 双击本文件即可启动平台:
rem 依赖检查(MySQL/Redis/Milvus) -> 刷新基金行情 -> API(含前端) -> Agent Worker
rem 真正的启动逻辑在项目里的 start.ps1,这里只做入口。
rem 如果项目换了位置,改下面这一行 PROJ 即可。
rem ============================================================
set "PROJ={project}"
echo ============================================================
echo 金融 Agent 平台 · 一键启动
echo ============================================================
echo.
if not exist "%PROJ%\start.ps1" (
echo [错误] 找不到启动脚本:
echo %PROJ%\start.ps1
echo.
echo 项目可能被移动或改名了。用记事本打开本文件,
echo 把 set "PROJ=..." 那一行改成实际路径。
echo.
pause
exit /b 1
)
cd /d "%PROJ%"
echo 项目目录:%PROJ%
echo 即将启动:API(前端+接口) 、Agent Worker 、并刷新基金行情
echo 就绪后会自动打开浏览器,请稍候...
echo.
rem %%* 让参数能透传给 start.ps1,例如:
rem 启动平台.bat -NoBrowser 不自动开浏览器
rem 启动平台.bat -Port 8100 换端口
rem 启动平台.bat -SkipPriceSync 不联网刷新行情
powershell -NoProfile -ExecutionPolicy Bypass -File "%PROJ%\start.ps1" %*
set "RC=%ERRORLEVEL%"
echo.
echo ============================================================
if "%RC%"=="0" (
echo 启动完成。服务在**独立窗口**里运行,关掉本窗口不影响它们。
) else (
echo 启动过程返回了错误码 %RC%,请往上翻看红色输出。
)
echo.
echo 服务窗口说明:
echo - 平台 API :接口 + 前端页面,地址 http://127.0.0.1:8000/portal/
echo - Agent Worker:客服对话、知识向量同步、风控扫描,必须一起开着
echo.
echo 常用命令(在本目录新开一个终端执行):
echo python tools\seed_demo_data.py 首次使用,准备演示数据
echo python tools\sync_market_prices.py 下单报"行情已过期"时补刷
echo python tools\e2e_smoke_test.py 交付自检
echo ============================================================
echo.
pause
"""
def build(project_root: Path) -> str:
return BAT_TEMPLATE.format(project=project_root)
def write_bat(path: Path, content: str) -> None:
"""按 GBK + CRLF 落盘:这是 cmd 能正确解析并显示中文的唯一组合。
⚠️ **换行必须是 CRLF**,这一点踩过坑:`cmd` 对 LF-only 的批处理会行边界错乱,
把本该 `echo` 出去的说明文字当成命令执行(实测出现 `AT 命令已弃用`、
`']' 不是内部或外部命令` 这种莫名其妙的报错,还会真的去跑脚本)。
"""
try:
encoded = content.encode("gbk")
except UnicodeEncodeError as exc: # pragma: no cover - 防御性
raise SystemExit(
f"[失败] 内容含 GBK 无法表示的字符({exc.object[exc.start:exc.end]!r})。"
"bat 里只能用 GBK 能表示的字符。"
) from exc
# 先把 CRLF 归一成 LF,再统一展开 —— 否则模板里已经是 CRLF 的行会变成 CRCRLF。
encoded = encoded.replace(b"\r\n", b"\n").replace(b"\n", b"\r\n")
path.write_bytes(encoded)
# 校验(不是"我觉得写对了",是真的读回来验):无 BOM、纯 CRLF、GBK 可逆。
raw = path.read_bytes()
assert not raw.startswith(b"\xef\xbb\xbf"), "bat 不能带 BOM:cmd 会把 BOM 当命令的一部分"
assert b"\n" not in raw.replace(b"\r\n", b""), "存在裸 LF:cmd 会错乱解析"
# 比编码是否可逆时先把两边都归一成 LF,否则比的是"换行风格"而不是"内容"。
assert raw.decode("gbk").replace("\r\n", "\n") == content.replace("\r\n", "\n"), \
"GBK 往返不一致"
print(f"[OK] 已写入 {path}({len(raw)} 字节,GBK/CRLF,无 BOM)")
def ensure_ps1_bom(ps1_path: Path) -> None:
"""`start.ps1` 缺 BOM 会让 Windows PowerShell 5.1 按 GBK 解析、直接抛语法错。"""
raw = ps1_path.read_bytes()
if raw.startswith(b"\xef\xbb\xbf"):
print(f"[OK] {ps1_path.name} 已带 BOM")
return
ps1_path.write_bytes(b"\xef\xbb\xbf" + raw)
print(f"[修好] {ps1_path.name} 缺 BOM,已补上(否则 PowerShell 5.1 解析中文会报语法错)")
def main() -> int:
target_dir = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else Path.home() / "Desktop"
if not target_dir.is_dir():
print(f"[失败] 目标目录不存在:{target_dir}")
return 1
ps1_path = PROJECT_ROOT / "start.ps1"
if not ps1_path.is_file():
print(f"[失败] 找不到 {ps1_path}")
return 1
ensure_ps1_bom(ps1_path)
content = build(PROJECT_ROOT)
# 桌面:用户双击的那个
write_bat(target_dir / "启动金融Agent平台.bat", content)
# 项目根:随仓库走,别人 clone 下来也能双击(路径指向自己所在目录)
write_bat(PROJECT_ROOT / "启动平台.bat", content)
print()
print("完成。双击桌面的「启动金融Agent平台.bat」即可:")
print(" 依赖检查 → 刷新基金行情 → API(前端) → Agent Worker → 自动开浏览器")
print("重复双击是安全的:API 按端口判断、Worker 按进程判断,都不会起第二份。")
return 0
if __name__ == "__main__":
sys.exit(main())