cf875b55fbc860ad58d3449ffdfdb73b35d892ee
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(C# WinForms) | 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# 桌面版(主用)
dotnet build TfsExcelImporter.sln -c Release
# 或用 Visual Studio 打开 TfsExcelImporter.sln(目标框架 net48,无需装 VS 也能 dotnet build)
TfsExcelImporter\bin\Release\net48\TfsExcelImporter.exe
操作流程:
- 填「TFS 连接」区:URL、项目、工作项类型、认证方式与域账号;内网保持「使用系统代理」不勾选。
- 点「加载迭代列表」,选择目标迭代。
- 点「生成Excel模板」拿到表头,或「浏览...」选择已有
.xlsx;点「预览数据」核对。 - 需要挂父项时填「父 PBI Id」(默认全部挂同一个;Excel 里加
PBI Id列可逐行指定不同父项)。 - 点「开始导入 TFS」,日志区逐行输出
OK/SKIP/FAIL与工作项 ID。 - 历史数据 Area 不对时用「按迭代修Area」批量纠正。
Python 版
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 与 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
真实台账含人员与项目信息,不入库;本地准备数据文件:
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(NuGetMicrosoft.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 版后至少跑:
python -m py_compile tfs_import.py
python tfs_import.py --dry-run
桌面版改完跑一次 --limit 级别的小批量导入(先删服务器已有同标题项或开启跳过),并在积压工作页展开父项确认子任务可见。
Languages
C#
90.5%
Python
9.5%