{stringifyGovernanceMetadata(detail.asset.metadata)}
diff --git a/project-docs/AI合同起草与审查助手-当前工作进度保存-2026-05-11.md b/project-docs/AI合同起草与审查助手-当前工作进度保存-2026-05-11.md
index 93551a69..0c2517fb 100644
--- a/project-docs/AI合同起草与审查助手-当前工作进度保存-2026-05-11.md
+++ b/project-docs/AI合同起草与审查助手-当前工作进度保存-2026-05-11.md
@@ -969,3 +969,52 @@
- `review_rules` 仍缺 publish / unpublish / rollback
- `knowledge_bases` 仍缺 publish / unpublish / rollback
- 正式生产放行层面仍然继续阻塞在真实人工复核和最终批准签署
+
+## 2026-05-13 GOV-INT-003 正式生产资料包与文件中心基础打通
+
+### 本轮完成项
+- 已补强文件中心底座:
+ - `FileRepository` 新增按 `file_hash + file_name + file_type` 复用文件资产的能力
+ - `FileService` 新增 `ensure_file_record_from_path(...)`,可把仓库内现有资料文件按路径纳入文件中心,而不是每次导入都重复创建
+ - `uploaded_by` 现在在系统导入场景下支持从 `id / username / display_name / system` 回退,避免 formal-launch 脚本导入时出现 `files.uploaded_by` 为空
+- 已把 formal-launch 的生产资料导入链打通到文件中心:
+ - `production_material` 资产导入时会为 `file_path` 创建/复用文件资产,并把 `source_file_id` 写入治理资产 `metadata`
+ - `material_signoff` 的本地 evidence 文件会尝试写入文件中心,并把 `file_id` 写入治理证据
+- 已补强 reviewer 分配脚本:
+ - `scripts/apply-formal-launch-reviewer-assignment-matrix.ps1 -Apply` 现在会自动补齐 7 份 `project-docs/formal-launch-ops/signoff/evidence/*.md` 生产资料签收证据模板
+ - 这些模板会预填 `signer_name` 和 `planned_due_date`,但继续保持 `EVIDENCE_STATUS: TEMPLATE`
+- 已补强治理资产详情页:
+ - `production_material` 资产现在可直接跳到文件中心查看 `source_file_id`
+ - 证据记录如已绑定 `file_id`,也可直接从治理资产页跳到 `/files?highlight=...`
+
+### 本轮验证
+- 后端测试:
+ - `python -m unittest tests.test_governance_service tests.test_governance_api tests.test_files_api`
+ - 结果:`22 tests OK`
+- 后端编译:
+ - `python -m py_compile backend/app/services/governance_service.py backend/app/services/file_service.py backend/app/repositories/file_repository.py`
+ - 结果:通过
+- 前端检查:
+ - `npm --prefix frontend run lint`
+ - 结果:通过
+ - `npm --prefix frontend run build`
+ - 结果:通过
+- 远程真相源验收(通过 SSH 隧道连接远程 PostgreSQL + MinIO):
+ - `FORMAL_LAUNCH_GOVERNANCE_DB_IMPORT_PASS`
+ - 产物:`project-docs/qa-artifacts/formal-launch-governance-db-import/2026-05-13T22-20-20-293/`
+ - 验证结果:
+ - `production_material / MAT-USER-AGREEMENT-001` 已存在 `source_file_id`
+ - 其最新本地 signoff evidence 已存在带 `file_id` 的 `template` 证据记录
+
+### 当前状态更新
+- `GOV-INT-003` 已从 `PLANNED` 推进到 `IN_PROGRESS`
+- 正式生产资料不再只是 docs 文件路径:
+ - 至少生产资料主文件已可进入文件中心并绑定治理资产
+ - 生产资料签收证据模板也已具备文件中心绑定能力
+- 当前本机仍无法直连 `192.168.3.90:9000`,但通过 SSH 隧道可以完成远程验收;这更像本机到对象存储端口的网络可达性问题,不是生产 MinIO 不可用
+
+### 当前阻塞项
+- `GOV-INT-003` 仍未完全结束:
+ - 文件中心里的“版本视角”仍较基础,后续还可继续增强生产资料版本链可见性
+ - 真实 reviewer 仍需把这些生产资料证据从 `TEMPLATE` 改成 `READY`
+- 正式生产放行继续阻塞在真人复核和最终批准签署
diff --git a/project-docs/formal-launch-ops/governance-productization-backlog.csv b/project-docs/formal-launch-ops/governance-productization-backlog.csv
index f9afef34..a55a9e1e 100644
--- a/project-docs/formal-launch-ops/governance-productization-backlog.csv
+++ b/project-docs/formal-launch-ops/governance-productization-backlog.csv
@@ -20,5 +20,5 @@ batch,priority,area,task_id,title,current_gap,target_deliverable,depends_on,acce
5C,P1,frontend,GOV-WEB-005,新增台账导入与同步页,当前 CSV 导入完全依赖脚本,新增导入中心页面支持预览导入差异和执行导入,GOV-API-006,管理员可在Web端完成台账同步,COMPLETED
5D,P0,integration,GOV-INT-001,实现现有 formal-launch 台账到数据库的首轮回填,当前历史台账和协作包仍是仓库真相源,编写一次性 backfill 脚本和映射规则,GOV-API-006,数据库中出现与当前 CSV 对齐的治理资产和复核任务,IMPLEMENTED_VALIDATED
5D,P0,integration,GOV-INT-002,门禁脚本增加数据库读模式,当前上线门禁只读 project-docs CSV 和 md,为 run-formal-launch-approval-gate.ps1 增加优先读数据库的模式,GOV-DB-005;GOV-API-005;GOV-INT-001,门禁可同时校验系统内治理状态和历史兼容输入,IMPLEMENTED_VALIDATED
-5D,P1,integration,GOV-INT-003,正式上线资料包与文件中心打通,当前用户协议隐私政策免责声明等资料只存在 docs,将生产资料包纳入系统文件中心并绑定治理资产,GOV-DB-001;GOV-API-004,资料包可在系统内查看版本和签署状态,PLANNED
+5D,P1,integration,GOV-INT-003,正式上线资料包与文件中心打通,当前用户协议隐私政策免责声明等资料只存在 docs,将生产资料包纳入系统文件中心并绑定治理资产,GOV-DB-001;GOV-API-004,资料包可在系统内查看版本和签署状态,IN_PROGRESS
5D,P1,integration,GOV-INT-004,权限与审计补齐,当前治理动作权限模型不完整,为治理域增加权限点和审计事件定义,GOV-API-002;GOV-API-003;GOV-API-004;GOV-API-005,所有治理关键动作都可审计可追责,PLANNED
diff --git a/project-docs/formal-launch-ops/signoff/evidence/MAT-ADMIN-RUNBOOK-001.md b/project-docs/formal-launch-ops/signoff/evidence/MAT-ADMIN-RUNBOOK-001.md
new file mode 100644
index 00000000..ad68f866
--- /dev/null
+++ b/project-docs/formal-launch-ops/signoff/evidence/MAT-ADMIN-RUNBOOK-001.md
@@ -0,0 +1,22 @@
+# Formal Launch Material Signoff Evidence
+
+EVIDENCE_STATUS: TEMPLATE
+
+- material_id: MAT-ADMIN-RUNBOOK-001
+- material_name: 管理员上线操作手册
+- signer_name: 诗融
+- planned_due_date: 20271221
+- signoff_date: TODO_YYYY-MM-DD
+- signoff_result: TODO_APPROVED_OR_REJECTED
+
+## Review Basis
+
+TODO_REAL_REVIEWER: record the business, legal, or operations basis for signing off this material.
+
+## Required Changes
+
+TODO_REAL_REVIEWER: list required changes, or write none.
+
+## Final Decision
+
+TODO_REAL_REVIEWER: replace EVIDENCE_STATUS with READY only after the real signer confirms this material.
\ No newline at end of file
diff --git a/project-docs/formal-launch-ops/signoff/evidence/MAT-AI-DISCLAIMER-001.md b/project-docs/formal-launch-ops/signoff/evidence/MAT-AI-DISCLAIMER-001.md
new file mode 100644
index 00000000..2f2cdcd5
--- /dev/null
+++ b/project-docs/formal-launch-ops/signoff/evidence/MAT-AI-DISCLAIMER-001.md
@@ -0,0 +1,22 @@
+# Formal Launch Material Signoff Evidence
+
+EVIDENCE_STATUS: TEMPLATE
+
+- material_id: MAT-AI-DISCLAIMER-001
+- material_name: AI输出免责声明
+- signer_name: 陈钰麒
+- planned_due_date: 20271221
+- signoff_date: TODO_YYYY-MM-DD
+- signoff_result: TODO_APPROVED_OR_REJECTED
+
+## Review Basis
+
+TODO_REAL_REVIEWER: record the business, legal, or operations basis for signing off this material.
+
+## Required Changes
+
+TODO_REAL_REVIEWER: list required changes, or write none.
+
+## Final Decision
+
+TODO_REAL_REVIEWER: replace EVIDENCE_STATUS with READY only after the real signer confirms this material.
\ No newline at end of file
diff --git a/project-docs/formal-launch-ops/signoff/evidence/MAT-CUSTOMER-ONBOARDING-001.md b/project-docs/formal-launch-ops/signoff/evidence/MAT-CUSTOMER-ONBOARDING-001.md
new file mode 100644
index 00000000..ded1182e
--- /dev/null
+++ b/project-docs/formal-launch-ops/signoff/evidence/MAT-CUSTOMER-ONBOARDING-001.md
@@ -0,0 +1,22 @@
+# Formal Launch Material Signoff Evidence
+
+EVIDENCE_STATUS: TEMPLATE
+
+- material_id: MAT-CUSTOMER-ONBOARDING-001
+- material_name: 客户上线核对表
+- signer_name: 诗融
+- planned_due_date: 20271221
+- signoff_date: TODO_YYYY-MM-DD
+- signoff_result: TODO_APPROVED_OR_REJECTED
+
+## Review Basis
+
+TODO_REAL_REVIEWER: record the business, legal, or operations basis for signing off this material.
+
+## Required Changes
+
+TODO_REAL_REVIEWER: list required changes, or write none.
+
+## Final Decision
+
+TODO_REAL_REVIEWER: replace EVIDENCE_STATUS with READY only after the real signer confirms this material.
\ No newline at end of file
diff --git a/project-docs/formal-launch-ops/signoff/evidence/MAT-LEGAL-REVIEW-SOP-001.md b/project-docs/formal-launch-ops/signoff/evidence/MAT-LEGAL-REVIEW-SOP-001.md
new file mode 100644
index 00000000..4cfa9ff4
--- /dev/null
+++ b/project-docs/formal-launch-ops/signoff/evidence/MAT-LEGAL-REVIEW-SOP-001.md
@@ -0,0 +1,22 @@
+# Formal Launch Material Signoff Evidence
+
+EVIDENCE_STATUS: TEMPLATE
+
+- material_id: MAT-LEGAL-REVIEW-SOP-001
+- material_name: 法务复核SOP
+- signer_name: 陈钰麒
+- planned_due_date: 20271221
+- signoff_date: TODO_YYYY-MM-DD
+- signoff_result: TODO_APPROVED_OR_REJECTED
+
+## Review Basis
+
+TODO_REAL_REVIEWER: record the business, legal, or operations basis for signing off this material.
+
+## Required Changes
+
+TODO_REAL_REVIEWER: list required changes, or write none.
+
+## Final Decision
+
+TODO_REAL_REVIEWER: replace EVIDENCE_STATUS with READY only after the real signer confirms this material.
\ No newline at end of file
diff --git a/project-docs/formal-launch-ops/signoff/evidence/MAT-PRIVACY-POLICY-001.md b/project-docs/formal-launch-ops/signoff/evidence/MAT-PRIVACY-POLICY-001.md
new file mode 100644
index 00000000..c03a8075
--- /dev/null
+++ b/project-docs/formal-launch-ops/signoff/evidence/MAT-PRIVACY-POLICY-001.md
@@ -0,0 +1,22 @@
+# Formal Launch Material Signoff Evidence
+
+EVIDENCE_STATUS: TEMPLATE
+
+- material_id: MAT-PRIVACY-POLICY-001
+- material_name: 隐私政策草案
+- signer_name: 陈钰麒
+- planned_due_date: 20271221
+- signoff_date: TODO_YYYY-MM-DD
+- signoff_result: TODO_APPROVED_OR_REJECTED
+
+## Review Basis
+
+TODO_REAL_REVIEWER: record the business, legal, or operations basis for signing off this material.
+
+## Required Changes
+
+TODO_REAL_REVIEWER: list required changes, or write none.
+
+## Final Decision
+
+TODO_REAL_REVIEWER: replace EVIDENCE_STATUS with READY only after the real signer confirms this material.
\ No newline at end of file
diff --git a/project-docs/formal-launch-ops/signoff/evidence/MAT-SUPPORT-SOP-001.md b/project-docs/formal-launch-ops/signoff/evidence/MAT-SUPPORT-SOP-001.md
new file mode 100644
index 00000000..7c1c8791
--- /dev/null
+++ b/project-docs/formal-launch-ops/signoff/evidence/MAT-SUPPORT-SOP-001.md
@@ -0,0 +1,22 @@
+# Formal Launch Material Signoff Evidence
+
+EVIDENCE_STATUS: TEMPLATE
+
+- material_id: MAT-SUPPORT-SOP-001
+- material_name: 客服与故障响应SOP
+- signer_name: 诗融
+- planned_due_date: 20271221
+- signoff_date: TODO_YYYY-MM-DD
+- signoff_result: TODO_APPROVED_OR_REJECTED
+
+## Review Basis
+
+TODO_REAL_REVIEWER: record the business, legal, or operations basis for signing off this material.
+
+## Required Changes
+
+TODO_REAL_REVIEWER: list required changes, or write none.
+
+## Final Decision
+
+TODO_REAL_REVIEWER: replace EVIDENCE_STATUS with READY only after the real signer confirms this material.
\ No newline at end of file
diff --git a/project-docs/formal-launch-ops/signoff/evidence/MAT-USER-AGREEMENT-001.md b/project-docs/formal-launch-ops/signoff/evidence/MAT-USER-AGREEMENT-001.md
new file mode 100644
index 00000000..c10ada45
--- /dev/null
+++ b/project-docs/formal-launch-ops/signoff/evidence/MAT-USER-AGREEMENT-001.md
@@ -0,0 +1,22 @@
+# Formal Launch Material Signoff Evidence
+
+EVIDENCE_STATUS: TEMPLATE
+
+- material_id: MAT-USER-AGREEMENT-001
+- material_name: 用户协议草案
+- signer_name: 陈钰麒
+- planned_due_date: 20271221
+- signoff_date: TODO_YYYY-MM-DD
+- signoff_result: TODO_APPROVED_OR_REJECTED
+
+## Review Basis
+
+TODO_REAL_REVIEWER: record the business, legal, or operations basis for signing off this material.
+
+## Required Changes
+
+TODO_REAL_REVIEWER: list required changes, or write none.
+
+## Final Decision
+
+TODO_REAL_REVIEWER: replace EVIDENCE_STATUS with READY only after the real signer confirms this material.
\ No newline at end of file
diff --git a/scripts/apply-formal-launch-reviewer-assignment-matrix.ps1 b/scripts/apply-formal-launch-reviewer-assignment-matrix.ps1
index c538c470..225b9a69 100644
--- a/scripts/apply-formal-launch-reviewer-assignment-matrix.ps1
+++ b/scripts/apply-formal-launch-reviewer-assignment-matrix.ps1
@@ -209,6 +209,54 @@ function Set-IfColumnExists {
}
}
+function Ensure-SignoffEvidenceTemplate {
+ param(
+ [Parameter(Mandatory = $true)][string]$EvidencePath,
+ [Parameter(Mandatory = $true)][string]$MaterialId,
+ [Parameter(Mandatory = $true)][string]$MaterialName,
+ [Parameter(Mandatory = $true)][string]$SignerName,
+ [string]$DueDate = ""
+ )
+
+ if ([string]::IsNullOrWhiteSpace($EvidencePath)) {
+ return $false
+ }
+ $fullPath = if ([System.IO.Path]::IsPathRooted($EvidencePath)) { $EvidencePath } else { Join-Path $repoRoot $EvidencePath }
+ if (Test-Path -LiteralPath $fullPath) {
+ return $false
+ }
+
+ $dir = Split-Path -Parent $fullPath
+ New-Item -ItemType Directory -Force -Path $dir | Out-Null
+ $lines = New-MarkdownLines
+ $lines.Add("# Formal Launch Material Signoff Evidence") | Out-Null
+ $lines.Add("") | Out-Null
+ $lines.Add("EVIDENCE_STATUS: TEMPLATE") | Out-Null
+ $lines.Add("") | Out-Null
+ $lines.Add("- material_id: $MaterialId") | Out-Null
+ $lines.Add("- material_name: $MaterialName") | Out-Null
+ $lines.Add("- signer_name: $SignerName") | Out-Null
+ if (-not [string]::IsNullOrWhiteSpace($DueDate)) {
+ $lines.Add("- planned_due_date: $DueDate") | Out-Null
+ }
+ $lines.Add("- signoff_date: TODO_YYYY-MM-DD") | Out-Null
+ $lines.Add("- signoff_result: TODO_APPROVED_OR_REJECTED") | Out-Null
+ $lines.Add("") | Out-Null
+ $lines.Add("## Review Basis") | Out-Null
+ $lines.Add("") | Out-Null
+ $lines.Add("TODO_REAL_REVIEWER: record the business, legal, or operations basis for signing off this material.") | Out-Null
+ $lines.Add("") | Out-Null
+ $lines.Add("## Required Changes") | Out-Null
+ $lines.Add("") | Out-Null
+ $lines.Add("TODO_REAL_REVIEWER: list required changes, or write none.") | Out-Null
+ $lines.Add("") | Out-Null
+ $lines.Add("## Final Decision") | Out-Null
+ $lines.Add("") | Out-Null
+ $lines.Add("TODO_REAL_REVIEWER: replace EVIDENCE_STATUS with READY only after the real signer confirms this material.") | Out-Null
+ Write-Utf8File -Path $fullPath -Text ($lines -join [Environment]::NewLine)
+ return $true
+}
+
function Update-EvidenceTemplate {
param(
[Parameter(Mandatory = $true)][string]$EvidencePath,
@@ -482,10 +530,16 @@ if ($Apply -and $errors.Count -eq 0 -and $missingGroups.Count -eq 0) {
$materialId = ([string]$row.material_id).Trim()
$roleText = (([string]$row.signoff_role) + " " + ([string]$row.owner) + " " + $materialId)
$name = $opsName
+ $materialDueDate = Get-DueDate -Matrix $matrix -Group "OPS_MATERIAL"
if ($roleText.Contains($script:CnLegal) -or @("MAT-USER-AGREEMENT-001", "MAT-PRIVACY-POLICY-001", "MAT-AI-DISCLAIMER-001", "MAT-LEGAL-REVIEW-SOP-001") -contains $materialId) {
$name = $legalName
+ $materialDueDate = Get-DueDate -Matrix $matrix -Group "LEGAL_CORE"
}
Set-IfColumnExists -Row $row -Column "signer_name" -Value $name
+ $evidencePath = ([string]$row.evidence_path).Trim()
+ if (Ensure-SignoffEvidenceTemplate -EvidencePath $evidencePath -MaterialId $materialId -MaterialName ([string]$row.material_name).Trim() -SignerName $name -DueDate $materialDueDate) {
+ $changedFiles.Add($evidencePath) | Out-Null
+ }
}
Export-CsvRows -Rows $materialSignoffs -Path $materialSignoffPath
$changedFiles.Add($materialSignoffPath) | Out-Null