Files
TFSImport/README.md
T
chenshoulin 8d1af0d11c chore: 初始化 TFS 2013 工作项批量导入工具集
- TfsExcelImporter: WinForms 桌面版,走 TFS Client OM(本环境 wit REST 路由 404,OM 是唯一可行通道)
- tfs_import.py: REST 版命令行导入器,供 wit 接口可用的 TFS 实例使用
- templates: Excel 导入模板及其 OOXML 源部件
- README: 两套工具定位、配置、环境坑点与验证方式
- 真实台账与本机工作笔记不入库,仅提交 work_items.example.csv 说明列结构
2026-11-01 11:21:20 +08:00

133 lines
7.6 KiB
Markdown
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.
# TFSImport
TFS 2013 工作项批量导入工具集,用于把 Excel/CSV 里的工时台账(标题、状态、指派人、完成量、日期等)批量写成团队项目里的工作项,并挂到指定的父项(Product Backlog Item / 占位 Backlog Item)下,使其在「积压工作」页和任务板可见。
目标环境:
| 项 | 值 |
| --- | --- |
| 服务器 | `http://pdtfs01.dev.goldenstand.cn:8080/tfs/DefaultCollection` |
| 团队项目 | `Tools_Dev` |
| 团队 | `DataTechnologies` |
| 迭代 | `2026年Sprint\2026年Sprint10` |
| 认证 | 域账号 NTLM(内网直连,不走代理) |
## 两套工具,先用哪个
| | [TfsExcelImporter](TfsExcelImporter)(C# WinForms) | [tfs_import.py](tfs_import.py)(Python CLI) |
| --- | --- | --- |
| 通信协议 | TFS Client OM(VS/Team Explorer 同款内部 SOAP) | REST `/_apis/wit/*` |
| 适用 | **本环境可用**。该 TFS 2013 实例只有 `/_apis/projects` 通,`wit/fields`、`wit/classificationnodes`、`wit/wiql` 全部 404 | 服务器开放了 wit REST 路由时使用 |
| 数据源 | `.xlsx`(内置模板) | `data/work_items.csv`(GBK/UTF-8 均可,需自备) |
| 界面 | 图形界面,预览后导入 | 命令行,`--dry-run` 先校验 |
Python 版在本环境会因 REST 路由 404 而失败,保留用于其它 TFS 实例或接口可用时的批量脚本化导入。
## 目录结构
```
TFSImport/
├── tfs_import.py # Python 命令行导入器(REST)
├── config.json # Python 版配置
├── requirements.txt # requests / requests-ntlm
├── data/
│ └── work_items.example.csv # 列结构与示例行(UTF-8 BOM);真实台账不入库
├── templates/
│ ├── TFS导入模板.xlsx # Excel 模板成品
│ ├── build_template.ps1 # 由 _parts 里的 XML 打包生成 xlsx
│ └── _parts/ # xlsx 的 OOXML 源文件
└── TfsExcelImporter/ # C# WinForms 桌面版(主用)
├── TfsExcelImporter.csproj # net48 + ClosedXML + TFS ExtendedClient 14.*
├── MainForm.cs # 界面与导入/修 Area 流程
├── TfsClient.cs # TFS Client OM 封装
├── ExcelService.cs # 读 Excel、生成模板、时间/数值归一
├── Config.cs # exe 同目录 config.json 读写
└── config.json # 桌面版配置
```
## 快速开始
### C# 桌面版(主用)
```powershell
dotnet build TfsExcelImporter.sln -c Release
# 或用 Visual Studio 打开 TfsExcelImporter.sln(目标框架 net48,无需装 VS 也能 dotnet build)
TfsExcelImporter\bin\Release\net48\TfsExcelImporter.exe
```
操作流程:
1. 填「TFS 连接」区:URL、项目、工作项类型、认证方式与域账号;内网保持「使用系统代理」**不勾选**。
2. 点「加载迭代列表」,选择目标迭代。
3. 点「生成Excel模板」拿到表头,或「浏览...」选择已有 `.xlsx`;点「预览数据」核对。
4. 需要挂父项时填「父 PBI Id」(默认全部挂同一个;Excel 里加 `PBI Id` 列可逐行指定不同父项)。
5. 点「开始导入 TFS」,日志区逐行输出 `OK/SKIP/FAIL` 与工作项 ID。
6. 历史数据 Area 不对时用「按迭代修Area」批量纠正。
### Python 版
```powershell
pip install -r requirements.txt
python tfs_import.py --dry-run # 只校验数据与字段映射,不写入
python tfs_import.py --list-fields # 列出服务器全部字段(显示名/引用名)
python tfs_import.py # 正式导入
python tfs_import.py --link-existing # 已导入的项补挂父链接(不新建)
python tfs_import.py --skip-existing-titles # 跳过服务器已有的同标题项
python tfs_import.py --input other.csv --limit 5
```
每次运行在 `data/import_report_<时间戳>.csv` 生成逐行结果报告(行号 / Title / 结果 / WorkItemId / 错误)。
## 配置说明
配置的语义、留空字段如何解析见 [tfs_import.py:49](tfs_import.py#L49) 与 [Config.cs](TfsExcelImporter/Config.cs),关键字段:
| 键 | 说明 |
| --- | --- |
| `tfs_url` / `project` / `work_item_type` | 目标集合、项目、工作项类型(`Task` 或 `Product Backlog Item`) |
| `iteration_path` | 可只写 `2026年Sprint\2026年Sprint10`,程序自动补项目名前缀 |
| `auth.mode` | `ntlm`(域账号,典型)/ `basic` / `default`(当前 Windows 凭据,仅桌面版) |
| `field_refs` | 语义名 → 字段引用名;自定义字段留空时按 `resolve_fields_by_display_name` 的显示名在服务器字段表里查(支持前缀模糊匹配,如 `Completed` → `Completed Work`) |
| `parent_title` / `parent_id` | 父工作项,二选一;按标题匹配不到会直接报错并提示改用 `parent_id` |
| `bypass_rules` | 是否尝试写入 `CreatedDate`/`ClosedDate` 等系统日期 |
| `skip_existing_titles` | 同项目/同类型/同迭代下按标题去重 |
> 配置里含明文密码,请勿提交到共享仓库;`config.json` 保持空值,凭据本地填写。
## Excel / CSV 列
`Title`、`State`、`Assigned To`、`FinancialProject`、`Completed`、`LogDate`、`Created Date`、`Closed Date`、`PBI Id`、`Notes`
真实台账含人员与项目信息,不入库;本地准备数据文件:
```powershell
copy data\work_items.example.csv data\work_items.csv # Python 版默认读这个路径,已在 .gitignore 里
```
- 表头按名匹配,顺序不限,`AssignedTo` / `Assigned To` 等价;缺列自动按模板顺序兜底。
- 空 `Title` 的行直接跳过;`State` 为空时用 `default_state`;未识别的列(如 `Group`)忽略。
- 日期支持 `yyyy-MM-dd[ / ][ HH:mm[:ss]]` 等格式,Excel 日期序列号(如 `46300`)会被还原成日期。
- CSV 从 Excel 另存时注意编码,脚本按 `utf-8-sig` → `gbk` 依次尝试。
## 环境坑点
这些是实测结论,改代码前先看:
- **wit REST 路由全 404**:TFS 2013 RTM/低 Update 不带 `/_apis/wit/*`,`Classification.asmx?WSDL` 也被禁 → 桌面版整体走 Client OM(NuGet `Microsoft.TeamFoundationServer.ExtendedClient` 钉 `14.*`,对应 VS2015 客户端代际;15.x 不兼容 TFS 2013)。
- **502 Bad Gateway**:TFS OM 内部用 `HttpWebRequest`,默认跟随 IE 系统代理,代理会拦内网 TFS。桌面版默认把 `WebRequest.DefaultWebProxy` 置空直连;确实要走代理时勾「使用系统代理」并带上 Windows 凭据过代理认证。
- **TF400276 / 关闭状态只读**:改已关闭工作项的 Area 会被拒;`FixAreaAsync` 走「重新打开 → 改 Area → 还原状态」的状态循环,中途失败回滚状态。
- **`System.CreatedDate` 写不进去**:OM 14.x 没有 `SaveFlags.BypassRules`(15.x 才加),该字段服务器托管;`Microsoft.VSTS.Common.ClosedDate` 在 `State=Done` 时尝试写入,校验或保存失败自动降级为普通保存,保证行不丢。
- **孤立 Task 在积压工作页不可见**:TFS 2013 的 backlog/任务板按「父项 → 子任务」组织,没挂父链接的 Task 只能在查询里看到,所以导入必须带 `parent_title`/`parent_id`。
- **DNS 解析失败 / 超时**:多为 VPN 或网络断开,不是代码问题;桌面版会对这类异常附加排查提示。
## 验证
改动 Python 版后至少跑:
```powershell
python -m py_compile tfs_import.py
python tfs_import.py --dry-run
```
桌面版改完跑一次 `--limit` 级别的小批量导入(先删服务器已有同标题项或开启跳过),并在积压工作页展开父项确认子任务可见。