chenshoulin 8a6f9d55bc refactor: 移除不再使用的配置文件与代码,优化用户界面
- 删除 config.json 和 requirements.txt 文件,移除 Python 版相关代码,简化项目结构。
- 更新 README.md,调整工具说明,强调仅保留 C# WinForms 版本。
- 在 MainForm.cs 中添加输入框的占位提示,提升用户体验。
- 更新 .gitignore,添加 debug.log 文件以记录运行时日志。
- 移除与 Python 版相关的示例 CSV 文件,确保数据源一致性。
2026-11-01 21:44:31 +08:00

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 桌面版),数据源为 .xlsx(默认模板随程序发布),图形界面预览后导入。

通信走 TFS Client OM(VS/Team Explorer 同款内部 SOAP):该 TFS 2013 实例只有 /_apis/projects 通,wit/fields、wit/classificationnodes、wit/wiql 全部 404,REST 路线走不通。原先另有一套 Python CLI(REST /_apis/wit/*)实现,因上述限制无法在本环境工作,已移除。

目录结构

TFSImport/
├── data/
│   └── work_items.csv             # 本地工时台账(含人员/项目,不入库)
├── templates/
│   └── TFS导入模板.xlsx            # 默认模板:构建时嵌入 exe,「导出Excel模板」原样导出
├── tools/
│   ├── make_icon.py               # 生成 app.ico(9 个尺寸帧,纯标准库 SDF 栅格化 + 手写 PNG/ICO)
│   ├── pe_icon.py                 # 解析 exe 的 PE 图标资源,导出某帧 PNG(换图标后核对用)
│   └── win_screenshot.py          # 冒烟截图:拉起 exe、PrintWindow 抓窗、导出标题栏图标(ctypes)
└── TfsExcelImporter/              # C# WinForms 桌面版
    ├── TfsExcelImporter.csproj    # net48 + ClosedXML + TFS ExtendedClient 14.*
    ├── FodyWeavers.xml            # Costura 单文件打包配置(便携版)
    ├── NativeLibraryLoader.cs     # 释放并预加载内嵌的 TFS native DLL
    ├── AppIcon.cs                 # 从内嵌 app.ico 取 Form.Icon(窗口/任务栏)
    ├── app.ico                    # 应用图标(16~256 共 9 档,tools/make_icon.py 生成)
    ├── MainForm.cs                # 导入 / 修 Area / 补挂父项流程
    ├── MainFormUi.cs              # 界面外壳:步骤卡片布局、手风琴折叠、状态呈现
    ├── Theme.cs                   # 设计 token(颜色/字号/间距/缩放)与自绘控件
    ├── app.manifest               # System DPI 感知,像素尺寸按缩放换算
    ├── TfsClient.cs               # TFS Client OM 封装
    ├── ExcelService.cs            # 读 Excel、导出默认模板、时间/数值归一
    ├── Config.cs                  # exe 同目录 config.json 读写
    └── config.json                # 桌面版配置

快速开始

桌面版

dotnet build TfsExcelImporter.sln -c Release
# 或用 Visual Studio 打开 TfsExcelImporter.sln(目标框架 net48,无需装 VS 也能 dotnet build)
TfsExcelImporter\bin\Release\net48\TfsExcelImporter.exe

「导出Excel模板」优先用 exe 同目录的 TFS导入模板.xlsx(现场可替换),没有则从 exe 内嵌资源导出——分发时只带一个 exe 就够,不用再附带模板文件。

应用图标是 TfsExcelImporter/app.ico(蓝色渐变圆角底 + 白色上升箭头),csproj 里 ApplicationIcon 写进 exe 的 Win32 图标资源,同时以托管资源再嵌一份给 Form.Icon 用(多尺寸帧,高 DPI 下不糊)。要改设计就改 tools/make_icon.py 后重跑,覆盖 app.ico 再编译:

python tools/make_icon.py          # 重新生成 app.ico,并出预览图 tools/icon_preview.png
python tools/win_screenshot.py TfsExcelImporter\bin\Release\net48\TfsExcelImporter.exe tools\_win.png

界面按 4 步排:顶部状态条常驻显示服务器/项目/类型与连接状态,四张卡片一次只展开一张(展开一张会把其它收成一行摘要),预览表格永远占满剩余高度。

  1. 步骤 1 连接:填 URL、项目、工作项类型与账号密码(认证固定 NTLM,用户名可写 user 或 DOMAIN\user),点「测试连接」——右上状态灯变绿即连通。
  2. 步骤 2 选数据:点「加载迭代列表」选目标迭代(也可手填完整路径);点「导出 Excel 模板」拿默认模板,或「浏览...」选已有 .xlsx。需要挂父项时填「父 PBI Id」(默认全部挂同一个;Excel 里加 PBI Id 列可逐行指定不同父项);勾「跳过已存在标题」按「项目/类型/迭代 + 标题」去重。字段校验不可绕过,不合法时日志会直接告诉你改哪一列。
  3. 步骤 3 预览核对:选完文件自动读表,表格里逐行核对;缺迭代/文件路径无效时红字直接标在对应输入框下方,不再弹框。
  4. 步骤 4 导入:底部操作条点「开始导入 TFS」,进度条 + n/m + 成功/跳过/失败三个计数实时更新,日志抽屉逐行输出 OK/SKIP/FAIL 与工作项 ID;跑完点「打开报告 CSV」看逐行结果。
  5. 历史数据 Area 不对、或漏挂父项时,展开底部「高级工具」用「按迭代修 Area」「补挂父项」批量纠正;Area 路径也在这一栏里填(以前只能手改 config.json)。

桌面版按 System DPI 感知实现,界面尺寸随 Windows 显示缩放换算;改了显示缩放比例要重启程序才生效。

便携版(单 exe,免安装)

构建产物只有一个文件:TfsExcelImporter\bin\Release\net48\TfsExcelImporter.exe(约 9.3 MB)。依赖和资源全部内嵌:

内容 打包方式
58 个托管 DLL(ClosedXML / OpenXml / TFS Client OM …) Costura.Fody 内嵌,运行时经 AssemblyResolve 从内存加载,不落盘
Microsoft.WITDataStore32/64.dll(纯 win32,TFS OM 用) 独立内嵌,启动时按进程位数释放到 %TEMP%\TfsExcelImporter\native\{内容hash} 并 LoadLibrary 预加载
默认模板 TFS导入模板.xlsx 内嵌资源,「导出Excel模板」时释放;exe 同目录放同名文件则优先用它
config.json 首次保存时写到 exe 同目录(便携);目录只读时回退 %AppData%\TfsExcelImporter\config.json

分发:

mkdir dist
copy TfsExcelImporter\bin\Release\net48\TfsExcelImporter.exe dist\

把 dist\TfsExcelImporter.exe 拷到任意目录双击即用。想连配置一起带走,就把 exe 同目录的 config.json 一起拷。

前提:目标机需有 .NET Framework 4.8(Win10 1903+ / Win11 系统自带,一般无需额外安装)。FodyWeavers.xml 是 Costura 的打包配置,删掉它或用 -p:FodyDisable=true 构建即回到「exe + 一堆 DLL」的普通输出。

配置说明

桌面版的配置落在 exe 同目录的 config.json(界面改动后自动写回);exe 目录不可写时回退到 %AppData%\TfsExcelImporter\config.json。

配置的语义、留空字段如何解析见 Config.cs,关键字段:

键 说明
TfsUrl / Project / WorkItemType 目标集合、项目、工作项类型(Task 或 Product Backlog Item)
IterationPath 可只写 2026年Sprint\2026年Sprint10,程序自动补项目名前缀
AreaPath 区域路径;界面「高级工具」栏也可改
Username / Password 认证固定 NTLM,用户名可写 user 或 DOMAIN\user
FinancialProjectRef / CompletedRef / LogDateRef / OriginalEstimateRef 自定义字段引用名;留空则按显示名在服务器字段表里查(支持前缀模糊匹配,如 Completed → Completed Work)
ParentPbiId 所有 Task 默认挂接的父 PBI Id;Excel 每行 PBI Id 列可覆盖
SkipExistingTitles 同项目/同类型/同迭代下按标题去重
DefaultState Excel State 列留空时的默认值
LastExcelPath 上次选中的数据文件,界面回填用

配置里含明文密码,请勿提交到共享仓库;config.json 保持空值,凭据本地填写。

Excel 列

桌面版 Excel 模板 = 仓库里的 templates\TFS导入模板.xlsx,界面「导出Excel模板」直接复制它:

Title、State、Assigned To、FinancialProject、Completed、LogDate、PBI Id、Description

写入 TFS 的对应关系:

模板列 目标字段
Description System.Description
Completed 显示名 Completed(模糊匹配到 Completed Work)解析出的字段
—(无列) Original Estimate 取该行 Completed 的值;引用名留空则按显示名 Original Estimate 解析,可写在 OriginalEstimateRef 里固定

系统日期列(Created Date / Closed Date)已从模板移除:System.CreatedDate 由服务器按导入时间填写,Microsoft.VSTS.Common.ClosedDate 不再写入、保持为空。

真实台账含人员与项目信息,不入库,放在 data\work_items.csv(已在 .gitignore 里)。

  • 表头按名匹配,顺序不限,AssignedTo / Assigned To 等价;缺列按模板顺序兜底,但该列已被别的表头占用时不套用位置(旧版模板文件不会串列)。
  • 空 Title 的行直接跳过;State 为空时用 DefaultState;未识别的列(如 Group)忽略。
  • 日期支持 yyyy-MM-dd[ / ][ HH:mm[:ss]] 等格式,Excel 日期序列号(如 46300)会被还原成日期。

环境坑点

这些是实测结论,改代码前先看:

  • 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 置空强制直连,界面不提供代理开关(本机若只能经代理上网,程序会直连失败)。
  • TF400276 / 关闭状态只读:改已关闭工作项的 Area 会被拒;FixAreaAsync 走「重新打开 → 改 Area → 还原状态」的状态循环,中途失败回滚状态。
  • System.CreatedDate 写不进去:OM 14.x 没有 SaveFlags.BypassRules(15.x 才加),该字段服务器托管;Microsoft.VSTS.Common.ClosedDate 在 State=Done 时按普通字段可尝试写入。模板已去掉日期列,所以导入不再走这条路——若某模板要求 Done 必填 Closed Date,需另加列或改回写日期。
  • 字段校验不可绕过:OM 14.x 没有 SaveFlags.BypassRules,旧界面上那个 bypassRules 勾选从未真正生效,已移除。校验失败时日志与 report CSV 直接给出「字段(原因) → 怎么改」,列表字段还会列出允许值,按提示改 Excel 对应列后重跑即可。
  • 孤立 Task 在积压工作页不可见:TFS 2013 的 backlog/任务板按「父项 → 子任务」组织,没挂父链接的 Task 只能在查询里看到,所以导入必须带父项(界面填父 PBI Id,或 Excel 里加 PBI Id 列)。
  • DNS 解析失败 / 超时:多为 VPN 或网络断开,不是代码问题;桌面版会对这类异常附加排查提示。

验证

桌面版改完,先用只含几行的 Excel 跑一次小批量导入(先删服务器已有同标题项或勾「跳过已存在标题」),再在积压工作页展开父项确认子任务可见;改动涉及界面布局或图标时,跑 python tools/win_screenshot.py 抓图核对。确认无误后再跑全量。

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