From b334f580094dc1ce73ba7d4cd96c1438be6640ed Mon Sep 17 00:00:00 2001 From: ThearchyHelios Date: Fri, 16 Oct 2026 10:54:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=BC=E5=85=A5=E6=B8=85=E5=8D=95=E7=9A=84?= =?UTF-8?q?=E3=80=8C=E6=98=AF=E5=90=A6=E8=AE=A4=E8=AF=81=E3=80=8D=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=E7=85=A7=E8=AE=B0=E4=B8=8B=E7=9A=84=E8=AF=B4=EF=BC=9A?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E8=BA=AB=E4=BB=BD=20/=20=E6=9C=AA=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E7=BD=B2=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为什么:服务端开始如实记「是否认证」之后(导入清单那个提交),界面上的清单、导入记录、接受理由还一律写「署名(未认证)」, 开了登录、署名就是登录的人时说错了(05-已知遗留 (c))。 做了什么:导入记录接口带上 signed_by_verified(按这一期导入清单里记下的;旧清单记的是 false,照记下的说,不改写;简单导入 没有清单按未认证)。ManifestBody 的文件署名、接受理由的署名,VersionsDialog 的导入记录、快照里各期、接受理由的署名,都按记下 的值写「登录身份:x」或「署名(未认证):x」,和确认框里那一行(signerLine)同一套说法。接受理由由提交这一期的人写:署名和 清单(导入记录)的一样时才算认证。统计期、修改记录的署名是暂存时记的,没有记是否认证,照旧写「署名(未认证)」。 测试:test_signer_verified 加一句:开登录导入后导入记录的 signed_by_verified 为真。 --- backend/app/data/source_versions.py | 12 ++++++++++ backend/tests/test_signer_verified.py | 2 ++ frontend/src/pages/import/ManifestBody.tsx | 20 +++++++++++++--- frontend/src/pages/import/VersionsDialog.tsx | 24 +++++++++++++------- frontend/src/types.ts | 2 ++ 5 files changed, 49 insertions(+), 11 deletions(-) diff --git a/backend/app/data/source_versions.py b/backend/app/data/source_versions.py index 4e389fb0..2e0dedfe 100644 --- a/backend/app/data/source_versions.py +++ b/backend/app/data/source_versions.py @@ -110,6 +110,16 @@ def load_manifest(artifact_id: str | None) -> dict[str, Any] | None: return doc if isinstance(doc, dict) else None +def _signer_verified(artifact_id: str | None) -> bool: + """导入清单里记下的「署名是否认证」;没有清单(简单导入)、读不出来的照实算未认证。""" + try: + doc = load_manifest(artifact_id) + except ValueError: + return False + signer = (doc or {}).get("signed_by") + return bool(signer.get("verified")) if isinstance(signer, dict) else False + + def _rows_of(report: dict[str, Any] | None) -> dict[str, int]: """构建回执里各表的行数(执行器回执、期 1 的解析回执都是 tables: [{name, rows}])。""" out: dict[str, int] = {} @@ -1055,6 +1065,8 @@ async def import_records(session: AsyncSession, source: DataSource, imps: list[T # 按配方导入才有(简单导入为空):配方、统计期、署名,接受的条数 "recipe_id": imp.recipe_id, "period_start": imp.period_start, "period_end": imp.period_end, "signed_by": imp.signed_by, "overrides": len(imp.overrides or []), "waivers": len(imp.waivers or []), + # 署名是不是认证过的身份:按这一期导入清单里记下的(05-已知遗留 (c);旧清单写的是 False,照记下的说,不改写) + "signed_by_verified": _signer_verified(imp.manifest_artifact), # 期 3(7.6) "acceptances": _acceptance_rows(imp), "revoked": imp.revoked, "manifest_artifact": imp.manifest_artifact, "recipe_seq": seqs.get(imp.recipe_id) if imp.recipe_id else None, "in_current": in_current, diff --git a/backend/tests/test_signer_verified.py b/backend/tests/test_signer_verified.py index 6d4df7f7..13e98ee0 100644 --- a/backend/tests/test_signer_verified.py +++ b/backend/tests/test_signer_verified.py @@ -56,6 +56,8 @@ async def test_login_on_import_manifest_and_purge_record_say_verified(admin): assert imp.signed_by == "root" source_id = out["source"]["id"] + records = (await admin.get(f"/api/datasources/{source_id}/imports")).json() + assert [(r["signed_by"], r["signed_by_verified"]) for r in records] == [("root", True)], "导入记录按清单里记下的说" resp = await admin.post(f"/api/datasources/{source_id}/imports/{imp.id}/purge-raw", json={"reason": "含有不应保留的内容", "signed_by": "随手填的名字"}) assert resp.status_code == 200, resp.text diff --git a/frontend/src/pages/import/ManifestBody.tsx b/frontend/src/pages/import/ManifestBody.tsx index c19a028e..b2665b84 100644 --- a/frontend/src/pages/import/ManifestBody.tsx +++ b/frontend/src/pages/import/ManifestBody.tsx @@ -3,7 +3,7 @@ import clsx from 'clsx' import type { CheckResult, ImportAcceptance, ManifestOut } from '../../types' import { CopyButton, Notice } from '../../components/ui' import { formatBytes, formatCost, formatDateTime, formatTokens } from '../../lib/format' -import { RAW_STATE_LABEL, RECIPE_ORIGIN_LABEL, RECIPE_TEXT, VERSIONS_TEXT as VT } from '../../lib/terms' +import { RAW_STATE_LABEL, RECIPE_ORIGIN_LABEL, RECIPE_TEXT, SIGNER_TEXT, VERSIONS_TEXT as VT } from '../../lib/terms' import { CheckStatusChip } from './ImportReceipt' import { ReceiptSummary } from './ReceiptBlocks' @@ -79,7 +79,11 @@ export function ManifestBody({ m, fileName, rawState, recipeSeq }: { {short(c.file?.raw_sha256) && {VT.sha(short(c.file?.raw_sha256))}} {rawState != null && {RAW_STATE_LABEL[rawState] ?? rawState}} - {c.signed_by?.name &&
{VT.signedBy(c.signed_by.name)}
} + {c.signed_by?.name && ( +
+ {signerText(c.signed_by.name, !!c.signed_by.verified)} +
+ )} @@ -146,7 +150,9 @@ export function ManifestBody({ m, fileName, rawState, recipeSeq }: {
- {VT.signedBy(a.signed_by || VT.unsigned)} + {/* 接受理由由提交这一期的人写,署名同清单的 signed_by:是不是登录身份按清单里记下的说 */} + {a.signed_by ? signerText(a.signed_by, !!c.signed_by?.verified && a.signed_by === c.signed_by.name) + : VT.signedBy(VT.unsigned)} {a.at && {formatDateTime(a.at)}}
@@ -235,3 +241,11 @@ function RawJson({ summary, value, attr }: { summary: string; value: unknown; at ) } + +/** + * 留痕里的署名怎么说:按记下的「是否认证」写「登录身份:x」或「署名(未认证):x」(05-已知遗留 (c)),和确认框里那一行 + * (VersionsDialog 的 signerLine)同一套说法。旧清单记的是未认证,照记下的说 + */ +export function signerText(name: string, verified: boolean): string { + return verified ? SIGNER_TEXT.signedIn(name) : VT.signedBy(name) +} diff --git a/frontend/src/pages/import/VersionsDialog.tsx b/frontend/src/pages/import/VersionsDialog.tsx index 59c3389d..b7b16d9d 100644 --- a/frontend/src/pages/import/VersionsDialog.tsx +++ b/frontend/src/pages/import/VersionsDialog.tsx @@ -14,7 +14,7 @@ import { signerNow } from '../../store/auth' import { humanizeError } from '../../lib/errors' import { formatBytes, formatDateTime, formatTime, parseServerTime } from '../../lib/format' import { RAW_STATE_LABEL, SIGNER_TEXT, VERSIONS_TEXT as VT } from '../../lib/terms' -import { ManifestBody } from './ManifestBody' +import { ManifestBody, signerText } from './ManifestBody' // =========================================================================== // 版本页(数据源卡片上的「版本」,P3-SPEC 7.8):当前版本的各期、历史版本、全部导入记录三个页签, @@ -447,10 +447,10 @@ function CurrentTab({ cur, recOf, busy, onManifest, onRemove, onRevoke, onPurge {acceptN > 0 && (
{VT.acceptCount(acceptN)} - +
)} - {rec?.signed_by &&
{VT.signedBy(rec.signed_by)}
} + {rec?.signed_by &&
{signerText(rec.signed_by, !!rec.signed_by_verified)}
}
- {VT.signedBy(a.signed_by || VT.unsigned)} + {a.signed_by + ? signerText(a.signed_by, !!signer?.signed_by_verified && a.signed_by === signer.signed_by) + : VT.signedBy(VT.unsigned)} {a.at && {formatDateTime(a.at)}}
@@ -654,10 +660,12 @@ function ImportsTab({ imports, busy, onManifest, onPurge }: {
{r.recipe_seq != null ? VT.recipeSeq(r.recipe_seq) : r.recipe_id ? '' : VT.simpleImport} {r.created_at && {formatDateTime(r.created_at)}} - {r.signed_by && {VT.signedBy(r.signed_by)}} + {r.signed_by && + {signerText(r.signed_by, !!r.signed_by_verified)} + }
- +