# 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](docs/QuickTeam-API导入-调研与设计.md) §8。 ## 为什么可行 QuickTeam 前端是 Vue SPA,业务数据全部经一层「WCF + 通用存储过程网关」读写: ``` POST /Services/CommonService.svc/ExecTable { "connectionName": "QuickTeam", "param": "" } ``` 前端本身就是这套接口的客户端,复刻调用序列即可绕开浏览器与 DOM。实测:登录 0.5s、215 个项目 1.7s、 单项目工单查询 1.05s、文件上传 0.19s;12 个项目的完整演练 19s(旧方案约 25~35 分钟)。 ## 快速开始 ```bash cp config/config.example.json config/config.json # 填账号、Sprint、目录、起止日期 ``` 凭据可用环境变量覆盖,避免落盘:`export QT_PASSWORD=***`。 ```bash # 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 模板预检 | ## 联调(唯一剩余的验证) 第一次真实写入建议这样跑,只影响一个项目: ```bash 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 个用例,无需网络 ``` ## 测试 ```bash 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` 中。