fix: preserve collection timestamps during catalog edits

This commit is contained in:
2026-10-17 00:13:27 +08:00
parent ab0e5e3a2f
commit d714c87d31
11 changed files with 156 additions and 10 deletions
+59
View File
@@ -0,0 +1,59 @@
# 2026-10-17 导出与采集时间浏览器验收
本轮基于已合入的检索导出、导出工作台和资料生命周期页面,在隔离 PostgreSQL/pgvector 与默认生命周期 FastAPI、Vite、真实浏览器之间联调。全部账号、资料和查询均为合成测试;没有加载 Embedding、调用生成模型或产生领域实验结论。
## 基线与准备
- 旧 AGit [PR #33](https://gitea.kronecker.cc/saint/border-red-post-nalati/pulls/33) 关闭未合并,其普通分支通过真正 merged 的 [PR #34](https://gitea.kronecker.cc/saint/border-red-post-nalati/pulls/34) 合入 dev,保留原实现并增加下载响应的显式 `Cache-Control: no-store`。原 head、替代 head 和 merge 均已核对为最新 dev 祖先。
- [PR #36](https://gitea.kronecker.cc/saint/border-red-post-nalati/pulls/36) 的导出页面和 [PR #38](https://gitea.kronecker.cc/saint/border-red-post-nalati/pulls/38) 的来源生命周期页面也已合入。从最新 dev 创建本阶段分支,没有叠加待审功能或合入 main。
- 独立 HTTP 初始化合成来源、带微秒采集时间的关联文档、已审核知识和文本发布,并实际执行 22 条条目/chunk 关键词检索,准备其中一条的兼容请求上下文。这些是测试准备,不能称为浏览器录入或向量推理。
## 浏览器已实际验证的路径
| 路径 | 结果 |
|---|---|
| 匿名访问导出页 | 跳转登录并保留回跳路径;管理员登录后回到导出页 |
| 历史分页 | 显示 22 条,第一页 20 条、第二页 2 条;两端翻页按钮正确禁用 |
| 跨页选择 | 第一页选择一条 chunk 记录,第二页选择一条条目记录,仍保留两条选择 |
| 可选上下文 | 勾选关联 RAG 准备上下文后触发真实浏览器文件下载 |
| 下载文件核对 | 文件实际落盘;manifest 为 2 条检索和 1 条上下文;记录区 SHA-256 与页面显示一致,每个 payload 与原保存快照一致 |
| 文档时区缺陷复现 | 原采集时间为 `2026-10-16T12:34:56.789123+08:00`,仅修改备注却变成 `2026-10-16T04:34:00+08:00` |
| 修正后时间回填 | 编辑页显示当地时钟的 `12:34:56.789`,而非 UTC 时钟的 `04:34`;新建表单采集时间留空,要求明确填写 |
| 修正后仅修改备注 | 页面保存成功;独立 API 读取确认原带微秒的时间字符串完全保留,文档版本递增 |
| 文档历史 | 页面展开最新版本快照,可看到版本 4 及原 `12:34:56.789123+08:00` 采集时间 |
浏览器实际下载:
![跨页选择并下载](assets/export-acceptance/export-download.jpg)
备注修改后的历史快照:
![采集时间保留原微秒值](assets/export-acceptance/document-collected-at.jpg)
## 修正内容
原表单把 `toISOString()` 的 UTC 时钟直接填入 `datetime-local`,提交时又按本地时区解析,造成时区偏移;截取到分钟还会丢失秒和更高精度。
新增日期转换函数:回填使用本地时钟,支持秒/毫秒输入;若管理员没有修改时间,直接保留服务端原时间字符串,避免丢失微秒或在夏令时重叠时刻改变实际瞬间。明确修改时间时才转换为带时区的 ISO 时间。新建文档不再预填当前时间作为采集事实,仍由必填校验要求输入。 最新 dev 新合入的知识编辑页使用了相同的 UTC 截取方式,本轮同步复用此转换函数,保留其无采集时间时的空值语义;该知识页面的修改通过共享函数回归和构建验证,没有冒充新的浏览器操作。
为复核修正效果,缺陷复现后只在隔离合成数据上通过独立 HTTP 恢复原采集时间,再重新加载页面执行仅改备注的浏览器验证。该恢复属于测试基线准备,没有冒充浏览器操作;不自动修复或猜测既有业务资料的真实采集时间。
## 独立 HTTP 交叉核对
以下和浏览器操作分开记录:
- 文档修改后关联知识回到 pending,旧文本发布的检索返回 409;没有继续使用旧审核依据。
- 被活动知识/文档引用的文档、来源删除返回 409;过期版本修改返回 409。
- 单独可丢弃来源/文档经过修改和软删除后,正文详情隐藏,删除历史快照保留。
- 来源与文档修订后再次通过 HTTP 导出原选择,记录区原始字节与首次浏览器下载完全一致;manifest 的导出时间/编号允许变化,生成用量/费用仍未采集。
- 下载响应 `Cache-Control: no-store`;普通用户直接请求导出返回 403。
## 验证命令和边界
- `npm run test:datetime`:**6 passed**,覆盖中国时区、UTC、负偏移跨日、夏令时重叠、显式修改、微秒与空值;这些是 Node 时区单元测试,不等同于多浏览器验收。
- `npm run test:session`:**3 passed**;`npm run build`(含类型检查):通过。
- `pytest -q backend/tests/test_exports.py backend/tests/test_catalog.py`:**49 passed**;`ruff check backend`、`ruff format --check backend`:通过。本轮未把此前全量 254 项结果当作本阶段重新执行的结果。
- 原生删除确认框使本次浏览器控制无法继续完成确认;该浏览器删除路径未验收。相关删除保护、软删除与历史保留仅由上述独立 HTTP 和后端专项覆盖,仍需人工复核确认交互。没有据此放宽删除保护或修改确认机制。
- Docker、生产 HTTPS、多浏览器、浏览器改密、领域资料/问题集及生成模型实际调用仍不在本轮已完成范围。
复现时使用单独数据库或隔离 Schema、合成账号和明确的采集时间,按上述步骤比较下载原始字节及修改前后时间;环境、密码、原始 JSONL、数据库、日志和本机路径不入库。验证结束后关闭测试服务并删除本轮隔离 Schema。截图只保留合成数据,导出下载文件和原始检查记录保存在 Git 元数据目录。
+1 -1
View File
@@ -88,7 +88,7 @@ rows = [json.loads(line) for line in records.rstrip(b"\n").split(b"\n")]
assert len(rows) == sum(manifest["counts"].values())
```
前端调用层 `services/exports.ts` 的 `exportRetrievalHistory` 返回 Blob、受限文件名和记录摘要,沿用同一 Cookie/CSRF、401 撤销及错误处理,不放入 localStorage。此阶段没有新增下载页面,页面入口由后续前端集成;调用层测试是模拟网络的契约测试,不冒充浏览器下载验收。
前端调用层 `services/exports.ts` 的 `exportRetrievalHistory` 返回 Blob、受限文件名和记录摘要,沿用同一 Cookie/CSRF、401 撤销及错误处理,不放入 localStorage。下载页面已通过 PR #36 合入,可从管理员工作台进入。真实浏览器跨页选择、可选上下文、落盘文件与摘要核对见 [导出浏览器验收](EXPORT_BROWSER_ACCEPTANCE.md);调用层模拟网络测试与真实浏览器路径分别记录。
## 验证与撤回
+9
View File
@@ -107,3 +107,12 @@
- 一个 SQL 语句读取历史快照,保留旧版本而不重新检索;导出前完整校验,最多100条检索/100条上下文/8MiB,不静默截断,不返回部分文件。不新增数据库迁移或环境配置。
- 前端仅增加下载类型与调用层,复用 Cookie/CSRF/错误处理,不新增页面;真实浏览器下载与实验展示留给后续集成。完整字段、摘要核对、复现与回滚见 [RETRIEVAL_EXPORT_API.md](RETRIEVAL_EXPORT_API.md)。
- 专项测试 **30 passed**,包括真实 PostgreSQL 并发快照和 HTTP 重启导出;完整回归 **254 passed, 1 skipped**(skip 为须显式启用的真实 Embedding 专项),`ruff check` 与 `ruff format --check` 通过。前端会话下载契约 **3 passed**,类型与生产构建通过。测试使用合成资料与坐标 fixture,未重跑模型推理或调用生成模型。
## 2026-10-17 T05/T10:导出与采集时间浏览器复核
- 导出原实现及显式禁用缓存修正已通过替代 PR #34 真正合入 dev,原 #33 关闭未合并;已核对原 head、当前普通分支 head 与 merge 均包含在最新 dev。导出工作台 #36 与来源生命周期 #38 已合入,再创建唯一阶段分支 `test/backend-export-acceptance`。
- 真实浏览器完成匿名回跳/管理员登录、22条历史两页跨页选择、关联准备上下文与实际 JSONL 下载;落盘文件为2条检索/1条上下文,记录区摘要与页面一致,payload 与保存的原始快照一致。本轮没有模型推理。
- 发现并修正仅改文档备注时采集时间偏移8小时且截断到分钟的问题;回填使用本地时钟,未改时间保留服务端原微秒值,新建不猜测采集时间。最新合入的知识编辑页复用同一修正。文档浏览器保存与历史快照、独立API均复核通过;知识页仅共享函数测试和构建验证。
- 前端时区6项、会话3项测试与构建通过;后端导出/目录专项49项和ruff检查/格式检查通过。来源/文档版本、引用删除保护、软删除历史及修订后原导出字节不变均经独立HTTP核对。详见 [EXPORT_BROWSER_ACCEPTANCE.md](EXPORT_BROWSER_ACCEPTANCE.md)。
- 浏览器原生删除确认交互受工具限制仍待人工复核,不把独立HTTP删除验证当作浏览器结果;Docker/HTTPS/多浏览器/浏览器改密、领域资料与生成调用继续保留待办。
Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB