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

操作流程:

  1. 填「TFS 连接」区:URL、项目、工作项类型、认证方式与域账号;内网保持「使用系统代理」不勾选。
  2. 点「加载迭代列表」,选择目标迭代。
  3. 点「生成Excel模板」拿到表头,或「浏览...」选择已有 .xlsx;点「预览数据」核对。
  4. 需要挂父项时填「父 PBI Id」(默认全部挂同一个;Excel 里加 PBI Id 列可逐行指定不同父项)。
  5. 点「开始导入 TFS」,日志区逐行输出 OK/SKIP/FAIL 与工作项 ID。
  6. 历史数据 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(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 版后至少跑:

python -m py_compile tfs_import.py
python tfs_import.py --dry-run

桌面版改完跑一次 --limit 级别的小批量导入(先删服务器已有同标题项或开启跳过),并在积压工作页展开父项确认子任务可见。

S
Description
tfs导入工具
Readme
505 KiB
Languages
C# 90.5%
Python 9.5%