用「WCF + 通用存储过程网关」接口替代 Selenium(D://Learning//Python//工单自动化)导入工单: 单 Sprint 12 个项目 175s 跑完(旧方案 25~35 分钟),失败可断点续跑且不重复建单。 - 分层:transport(HTTP/协议)/ api(存储过程语义)/ domain(纯规则)/ workflow(阶段编排)/ store(SQLite 台账)/ report / CLI - 幂等:台账驱动断点续跑;dry-run 与跳过行不污染进度;--rollback-on-fail 清理半成品 - 命令:plan / apply / report / status,真实写入需 --yes - tools:只读探针、存储过程源码导出(sp_helptext)、导入诊断 - docs:调研与设计文档(含联调踩到的 5 个坑)+ 6 个关键存储过程源码 实测:Sprint 2026 Sprint 10 的 12 个项目全部完成建单→导入→确认→推送 TFS, 工单 225134–225145,35 个功能模块,重跑 plan 全部 skip-pushed。69 个单测通过。
QTImport
通过接口(而非浏览器自动化)把工单数据批量导入 QuickTeam 的项目。
替代 D:\Learning\Python\工单自动化\auto_quickteam.py(Selenium 版):单项目 2~3 分钟、依赖 DOM、易随前端改版失效,失败重跑还可能重复建单。
状态
| 阶段 | 内容 | 状态 |
|---|---|---|
| M0 | 接口调研 + 只读探针 | ✅ 完成 |
| M1 | 读链路(登录/项目/工单/上传/TFS 上下文) | ✅ 完成,已在生产实测 |
| M2 | 写入链路(建单/填单/导入/确认/推送) | ✅ 完成,单项目已真实写入并核对落库 |
| M3 | 幂等台账、断点续跑、回滚、报告 | ✅ 完成,69 个单测通过 |
| M4 | 真实项目联调 + 灰度 | ✅ 12 个项目已真实执行(工单 225134–225145) |
全量实测:Sprint 2026 Sprint 10 的 12 个项目全部建单 → 导入明细(33 个功能模块,逐文件与 Excel 核对一致)
→ 确认 → 推送 TFS,整轮 175 s(旧方案同流程约 25~35 分钟),重跑 plan 全部 skip-pushed。设计文档与联调踩坑记录:
docs/QuickTeam-API导入-调研与设计.md §8。
为什么可行
QuickTeam 前端是 Vue SPA,业务数据全部经一层「WCF + 通用存储过程网关」读写:
POST /Services/CommonService.svc/ExecTable
{ "connectionName": "QuickTeam", "param": "<encodeURIComponent(SPName+SQLParams)>" }
前端本身就是这套接口的客户端,复刻调用序列即可绕开浏览器与 DOM。实测:登录 0.5s、215 个项目 1.7s、 单项目工单查询 1.05s、文件上传 0.19s;12 个项目的完整演练 19s(旧方案约 25~35 分钟)。
快速开始
cp config/config.example.json config/config.json # 填账号、Sprint、目录、起止日期
凭据可用环境变量覆盖,避免落盘:export QT_PASSWORD=***。
# 1. 只读预演:能做什么、为什么跳过
python -m qtimport plan --config config/config.json
# 2. 演练完整链路(不发任何写请求,打印每一步的真实参数)
python -m qtimport apply --config config/config.json --dry-run
# 3. 真实执行(必须显式 --yes)
python -m qtimport apply --config config/config.json --yes
# 4. 看状态与报告
python -m qtimport status --config config/config.json
python -m qtimport report --config config/config.json
plan 输出:
项目 动作 说明
信托 - 上海信托 create 本项目该 Sprint 无工单
资管 - 华泰保险资管 create 本项目该 Sprint 无工单
资管 - 华泰资管 create 本项目该 Sprint 无工单
幂等与续跑
- 业务唯一键
(ProjectId, SystemName):同一项目同一 Sprint 只建一张工单。 - 台账在
state/qtimport.db(SQLite),每推进一个阶段落一次,中断后重跑自动从断点继续。 - 已存在但无台账记录的工单默认跳过(不接管别人的单);确认要接管加
--adopt。 - 已推送 TFS 的不再推;失败时可用
--rollback-on-fail删掉本次新建的半成品(已推送的绝不删)。
常用参数:
| 参数 | 用途 |
|---|---|
--project "资管 - 粤海资本" |
只处理指定项目(可多次) |
--limit 1 |
只跑前 1 个,联调用 |
--skip-tfs |
只建单+导入+确认,不推送 |
--adopt |
接管无台账记录的既有工单 |
--rollback-on-fail |
失败时回滚本次新建的半成品 |
--workers 3 |
并发数(默认取配置,写路径建议 ≤4) |
--force |
跳过 Excel 模板预检 |
联调(唯一剩余的验证)
第一次真实写入建议这样跑,只影响一个项目:
python -m qtimport apply --config config/config.json \
--project "资管 - 粤海资本" --limit 1 --skip-tfs --rollback-on-fail --yes
预期:建单 → 填 17 个字段 → 上传 xlsx → 服务器解析明细 → 确认工单(技术责任人确认版 → 客户责任人确认版)。
然后到 QuickTeam 页面上核对:工单数、明细行数、三个责任人、起止日期、状态。
核对无误后再去掉 --skip-tfs 验证推送,最后 --limit 1 换成全量。
注意:config.start_time / end_time 会原样写进工单,执行前务必确认为该 Sprint 的真实区间。
目录
QTImport/
├── config/
│ ├── config.example.json # 模板
│ └── config.json # 含凭据,已 gitignore
├── docs/
│ └── QuickTeam-API导入-调研与设计.md
├── qtimport/
│ ├── transport.py # WCF 网关:双层 JSON、错误解析、裸字节上传
│ ├── api.py # 存储过程语义封装
│ ├── domain.py # 项目匹配、动作判定、版本推进、Excel 预检
│ ├── workflow.py # 阶段编排、并发、回滚
│ ├── store.py # SQLite 台账
│ ├── report.py # summary.md / detail.csv / JSONL
│ └── __main__.py # CLI
├── tools/
│ └── api_probe.py # 只读探针(零依赖,可独立交付)
└── tests/ # 69 个用例,无需网络
测试
python -m unittest discover -s tests -t .
覆盖:双层 JSON 解码、WCF 错误分类、项目名精确匹配(避免 contains 误命中)、
动作判定全分支、版本推进、Excel 预检、17 个参数构造、Column1 判定、TFS 路径解析、
dry-run 隔离、断点续跑、失败归属与回滚。
环境
Python 3.12+,零第三方依赖,不需要浏览器。需能访问 http://192.168.1.213。
注意
ExecTable网关无身份校验,本工具只在内网执行;请使用个人账号(Operator会写入真实姓名,便于审计)。- 凭据不要提交:
config/config.json、state/、reports/、logs/均在.gitignore中。