feat(addendum): 补遗追加(FR-M2-10)
- 招标文件解析后可以追加补遗 / 澄清:单独解析、扣 1 次;新条目标「补遗 N」进正式清单,清单回到草稿(AD1)
- 同一种日期、时间不同的旧条目标「已被补遗 N 更新」,逐条确认更新或保留两条(AD2、AD3)
- 项目字段的新值在概览「采用 / 不采用」:采用时以补遗为准、清掉人工值;采用截止时间时日期条目一起确认;
没确认前查标用旧值,检查预览提示(AD4)
- 补遗不改项目名称、编号、采购人、代理机构、法律体系(更正公告标题会被抽成项目名称);时间比较按北京时间补时区
- 接口:fields/{key}/pending、checklist-items/{id}/supersession;清单项 origin_document、superseded_by;
关键日期 replaces;解析结果 dates_superseded、fields_pending;检查预览 pending_updates
- 前端:补遗模式的上传页、解析页结果、概览确认、读标页来源筛选和日期确认、检查预览提示
- 决策 AD1–AD6;方案和落地记录 docs/architecture/addendum-plan.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
98
docs/architecture/addendum-plan.md
Normal file
98
docs/architecture/addendum-plan.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# 补遗追加实现方案 v0.1(2026-11-05)
|
||||
|
||||
> 范围:FR-M2-10 补遗追加(简化版):追加的补遗 / 澄清文件单独解析、扣 1 次解析,新条目标注来源并入清单;日期类字段以最新文件为准,在旧值上标「已被补遗 N 更新」,由用户确认;不自动判断条目「新增 / 变更 / 作废」(P1)。
|
||||
> 依据:`03-functional-requirements.md`、`02-user-journeys.md` J2 第 1 步、`01-scope-roles.md`(「追加解析 + 来源标注」,影响分析是 P1)、`data-model.md` §5.2「补遗」、`openapi.yaml`(`/projects/{id}/fields/{key}/pending`、`/checklist-items/{id}/supersession`)、原型 `project-new.html?addendum=1` `parsing.html?addendum=1` `checklist.html` `project.html` 的关键日期卡。
|
||||
> 状态:AD1–AD6 已全部按建议确认(2026-11-05,`docs/decisions.md`)。
|
||||
|
||||
## 0. 结论速览
|
||||
|
||||
- **一半已经有了**:后端上传时可以选补遗 / 澄清(编号「补遗 N」「澄清 N」),能单独解析、扣 1 次解析、失败退回;新条目记着来自哪份文件;项目字段的新值和旧值不同时先放进待确认(`pending_value`),不覆盖。
|
||||
- **缺的**:日期类条目的「已被补遗 N 更新」关联;两个确认接口(字段新值、日期条目);并入后清单回到草稿;前端的追加入口、解析结果、确认界面、来源筛选。
|
||||
- **按 FR 只做日期类的自动关联**。原型里「★ 参数原为 480G、补遗改成 960G」那种逐条对照属于「变更判断」,是 P1,这次不做(AD2)。
|
||||
- **补遗的新条目直接进正式清单**,和招标文件的新旧条目都查:宁可多报,不漏报(AD1)。
|
||||
- **没有补遗样本**:10 份样本里一份都没有。这次用虚构的更正公告测链路;国动项目如果有更正公告,请一起给我,这是第一份真实补遗。
|
||||
|
||||
## 1. 现状
|
||||
|
||||
| 环节 | 现在 | 要补的 |
|
||||
|---|---|---|
|
||||
| 上传、编号 | `role = addendum / clarification`,各自从 1 编号 | 前端入口 |
|
||||
| 解析、扣点 | 和招标文件同一条流水线,扣 1 次解析,失败、超时、取消退回 | 解析结果页按补遗写(新增几条、哪些日期有新值) |
|
||||
| 清单项来源 | `origin_document_id` 指向补遗;但接口的 `origin_label` 只有「AI」「人工添加」 | `origin_label` 对补遗来的条目写「补遗 1」;读标页按来源筛选;并入后清单回到草稿 |
|
||||
| 项目字段 | 新值和旧值不同 → `pending_value`,等确认 | 确认接口和界面;确认后同步冗余列 |
|
||||
| 日期类条目 | 没有关联(`superseded_by_id` 列已建,没写) | 并入时关联、确认接口、界面 |
|
||||
| 分包 | 补遗不改已有的分包 | 不变 |
|
||||
|
||||
## 2. 并入时做什么(后端)
|
||||
|
||||
补遗解析完、并入清单时,在现有的 `checklist.merge` 之后多做三件事:
|
||||
|
||||
1. **新条目照常进清单**:和招标文件一样按校验结果进正式清单或待确认区,标「补遗 N」。
|
||||
2. **关联日期类条目**:补遗里每条有具体时间的「日期与递交」条目(投标截止、开标、提疑截止、保证金截止、投标有效期),在更早的文件(招标文件、编号更小的补遗)里找同一种日期、还在正式清单里的条目,时间不同的把旧条目的 `superseded_by_id` 指向新条目,等用户确认。时间相同的不关联(补遗只是复述)。
|
||||
3. **清单回到草稿**:已确认的清单改为草稿,历史版本保留,要求重新确认(原型:「补遗 1 并入后待重新确认」)。
|
||||
|
||||
解析任务的结果里加:`items_added`(新增几条)、`dates_superseded`(几处日期有新值)、`fields_pending`(几个项目字段有新值),解析页按这三个数写。
|
||||
|
||||
## 3. 确认(后端)
|
||||
|
||||
按契约实现两个接口:
|
||||
|
||||
- **`POST /projects/{id}/fields/{key}/pending`**(`accept: true / false`):
|
||||
- 采用:补遗的值变成 AI 值(出处换成补遗那一句),**清掉人工值**(以补遗为准;用户之后还可以再改),冗余列同步;
|
||||
- 不采用:丢掉待确认值,保持原值;
|
||||
- 采用投标截止时间时,清单里「投标截止」日期条目的更新一起确认(AD3),不用在读标页再点一次;
|
||||
- 都写审计(`project_field.resolve_pending`)。
|
||||
- **`POST /checklist-items/{id}/supersession`**(`accept: true / false`,对旧条目调用):
|
||||
- 采用:旧条目软删除(可以恢复),新条目保留;
|
||||
- 保留两条:解除关联,两条都在,由用户自己处理;
|
||||
- 都写审计(`checklist_item.resolve_supersession`)。
|
||||
|
||||
接口返回里补上已有字段:清单项的 `superseded_by`(新条目编号、来自哪份补遗)、清单概况的 `superseded_pending`(还有几处没确认)、关键日期的 `superseded_pending`。
|
||||
|
||||
**没确认之前查标用旧值**(AD4):项目字段的契约就是「确认后才生效」(`data-model.md` §5.2)。检查预览提示「补遗 1 带来的新值还有 N 处没确认」,不阻止发起检查(红线 4)。确认后截止时间变了,已有的检查结果按 PI5 提示重新检查。
|
||||
|
||||
## 4. 前端
|
||||
|
||||
- **追加入口**:概览页右上角和读标页清单栏各放一个「追加补遗 / 澄清」,进新建项目页的补遗模式(原型 `project-new.html?addendum=1`)。补遗模式下不填项目名称,文件类型选补遗或澄清答复,说明写「补遗单独解析,新条目标注『补遗 N』并入清单。标博士不判断哪些旧条目已作废,并入后由你核对」,按钮旁写扣 1 次解析。招标文件还没解析完时不显示入口(AD5)。
|
||||
- **解析页**:标题「解析补遗 1」;完成后写「新增 N 条 · M 处日期有新值 · K 个项目字段有新值 · 清单回到草稿,核对后重新确认」,下一步「核对补遗」进读标页、只看这份补遗的条目。
|
||||
- **概览页**:
|
||||
- 项目要点里有待确认值的字段写「补遗 1:新值 → 原值」,带出处,给「采用 / 不采用」;
|
||||
- 关键日期卡:被更新、还没确认的日期显示「已被补遗 1 更新,待确认」;确认后显示新值并注「已按补遗 1 更新 · 原为 11月18日 09:30」(原型 `project.html`)。
|
||||
- **读标页**:
|
||||
- 来源筛选:全部 / 招标文件 / 补遗 1 / ……,每项带条数;
|
||||
- 被更新的日期条目写「已被补遗 1 更新:新值 ……」,给「确认更新(删除旧条目)」和「保留两条」;
|
||||
- 并入后顶部提示「补遗 1 新增 N 条。标博士不判断哪些旧条目被它改了或作废了,请核对:改了的旧条目可以修改或删除」,带「只看补遗 1」。
|
||||
- **查标页**:检查预览里有没确认的新值时出上面那条提示。
|
||||
|
||||
## 5. 测试
|
||||
|
||||
- 虚构一份更正公告样例(`server/tests/fixtures/addendum-mini.pdf`,内容全虚构):改投标截止时间、开标时间,新增一条 ★ 参数,复述一条没改的日期。
|
||||
- 集成测试:追加 → 解析 → 扣 1 次 → 新条目标「补遗 1」、清单回到草稿、截止时间进待确认、日期条目关联(复述的不关联);字段采用 / 不采用;采用截止时间时日期条目一起确认;日期条目采用(旧条目软删除、可以恢复)/ 保留两条;检查预览的提示;只读项目 409。
|
||||
- 假模型能不能从样例里抽出日期要看它的规则;抽不出时测试里用接口建条目。浏览器里走一遍追加到确认的全程。
|
||||
- 用真实模型对这份虚构样例跑一次解析看看抽取结果,花费几分钱(AD6)。
|
||||
|
||||
## 6. 工作量和顺序
|
||||
|
||||
1. 后端:并入时关联日期、清单回到草稿、两个确认接口、接口字段补齐、检查预览提示,以及测试样例和测试。约半天。
|
||||
2. 前端:补遗模式的上传页、解析页的补遗结果、概览页的确认、读标页的来源筛选和日期确认。约半天。
|
||||
|
||||
## 7. 待确认事项
|
||||
|
||||
| 编号 | 事项 | 建议 |
|
||||
|---|---|---|
|
||||
| AD1 | 补遗的新条目进正式清单还是待确认区 | **按招标文件同一套规则进正式清单**(没出处、校验没过的照样进待确认区),标「补遗 N」,读标页给「只看补遗 N」和核对提示,清单回到草稿要求重新确认。新旧条目都在、都查,宁可多报,不漏报;全放待确认区的话,用户不逐条采纳就查不到补遗的新要求 |
|
||||
| AD2 | 哪些条目自动标「已被补遗更新」 | **只做日期类**(同一种日期、时间不同),按 FR。原型里参数类「原为 480G → 现为 960G」的逐条对照是变更判断,P1;先等真实补遗样本看写法 |
|
||||
| AD3 | 怎么确认 | **字段在概览页逐个「采用 / 不采用」,日期条目在读标页逐条确认**;采用投标截止时间时清单里的「投标截止」条目一起确认。采用时清掉人工值,以补遗为准 |
|
||||
| AD4 | 没确认之前查标用哪个值 | **旧值**,确认后生效(数据模型原定);检查预览提示还有几处没确认,不阻止检查 |
|
||||
| AD5 | 招标文件没解析完能不能追加补遗 | **不能**,界面上招标文件解析成功后才显示入口。没有招标文件时补遗的日期没有可关联的旧值 |
|
||||
| AD6 | 用真实模型对虚构样例跑一次解析 | **跑一次**(DeepSeek,几分钱),看抽取对补遗写法(「原为……现更正为……」)的表现;结果只记录,这次不按它调提示词 |
|
||||
|
||||
## 8. 落地记录(2026-11-05)
|
||||
|
||||
- 后端:`checklist/addendum.py`(并入时关联日期条目、确认 / 保留两条、采用截止时间时一起确认「投标截止」条目);`project/edit.resolve_pending`(采用时补遗的值变成 AI 值、出处换成补遗那一句、清掉人工值);清单项接口补 `origin_document`、`superseded_by`,`origin_label` 对补遗来的条目写「补遗 N」;清单概况 `superseded_pending`;关键日期 `replaces`(「已按补遗 1 更新 · 原为……」);解析结果 `dates_superseded`、`fields_pending`;检查预览 `pending_updates`。
|
||||
- **补遗不改项目是谁**:解析补遗时项目名称、编号、采购人、代理机构、法律体系不进待确认。测试里发现,更正公告的标题会被抽成「项目名称」,和招标文件的不同,每份补遗都会冒出一条没意义的待确认。
|
||||
- **时间比较按北京时间补时区**:模型给的时间有时带时区、有时不带,补遗复述原时间时不能误判成「有新值」。
|
||||
- ORM 给 JSONB 列写 `None` 存的是 JSON null,不是 SQL NULL,SQL 里数待确认值时要排除 JSON null。
|
||||
- 前端:补遗模式的上传页(选补遗 / 澄清答复)、解析页的补遗结果和「核对补遗」、概览页「追加补遗 / 澄清」和字段「采用 / 不采用」、关键日期的待确认和「已按补遗 1 更新」、读标页的来源筛选、核对提示、「只看日期更新」和逐条确认、条目上的「补遗 1」标签、检查预览的提示。
|
||||
- **AD6 结果**:用 DeepSeek Flash 对虚构的更正公告(推后递交截止时间、新增一条 ★ 参数)跑了一次解析(6 次调用):两处改动都抽对了,★ 参数归为技术参数 + 无效条款、新截止时间 12 月 8 日,出处都是「补遗 1 · 第 1 页」,没有多抽。只是一份很规整的虚构样例,「原为……现更正为……」那种写法这份样例里没有,真实补遗到了再看。
|
||||
- 浏览器里走过:追加补遗 → 解析结果 → 概览采用新截止时间(项目头、要点、关键日期一起变,旧的「投标截止」条目自动删除)→ 读标页只看补遗 1。
|
||||
@@ -188,9 +188,9 @@
|
||||
|---|---|---|
|
||||
| 登录与账号 | 发验证码、注册、登录(含挤下线确认)、退出、`GET /me` | 修改显示名、在线会话列表和下线、注销账号 |
|
||||
| 额度与套餐 | `GET /plans`、`GET /quota` | 额度流水、开通申请 |
|
||||
| 项目 | 列表(不含筛选和分页)、新建、详情(含解析出的基本信息字段和关键日期)、状态变更、基本信息字段修改、分包的增删改、「不分包」和选包 | 补遗确认、删除 |
|
||||
| 项目 | 列表(不含筛选和分页)、新建、详情(含解析出的基本信息字段和关键日期)、状态变更、基本信息字段修改、补遗新值的采用 / 不采用、分包的增删改、「不分包」和选包 | 删除 |
|
||||
| 上传与文档 | 分片上传全套、挂到项目、文档列表和详情、逐页摘要、一页的句子、签名下载、发起解析、解析进度、取消、人工建出处 | 删除文档 |
|
||||
| 清单 | 清单概况(含确认人、删除数、按类别计数)、清单项列表(按区域、类别、所投的包筛选,可含已删除)、清单项详情(ETag)、修改(If-Match 选填)、采纳、删除恢复、人工新增、替换出处、修改记录、确认清单、版本 | 补遗更新确认(等补遗解析) |
|
||||
| 清单 | 清单概况(含确认人、删除数、按类别计数)、清单项列表(按区域、类别、所投的包筛选,可含已删除)、清单项详情(ETag)、修改(If-Match 选填)、采纳、删除恢复、人工新增、替换出处、修改记录、确认清单、版本、「已被补遗 N 更新」的确认 | — |
|
||||
| 检查 | 检查预览、发起、进度、取消、检查记录、问题列表(含本次已解决)、每条清单项的检查结论(C9,2026-11-04 新增)、处理、撤销、批量处理(只限提示级);投标分册类型和版本、电子投标平台加密格式识别 | 需人工核对(第 2 步) |
|
||||
| 报告、资质 | 报告:导出、生成进度、历次报告、签名下载(第 5 步);资质库:增删改、按类型 / 关键词筛选、附件(上传后挂上、签名下载、删除)、`late_for` 到期提醒(第 4 步) | — |
|
||||
| 通知与事件 | 事件流(含检查进度)、通知列表、反馈 | 标为已读 |
|
||||
|
||||
@@ -2260,6 +2260,13 @@ components:
|
||||
kind: { type: string, enum: [question_deadline, bid_deadline, opening, deposit_deadline, validity, other] }
|
||||
at: { $ref: '#/components/schemas/Timestamp' }
|
||||
edited: { type: boolean, description: 人工改过(投标截止时间按项目字段的人工值显示) }
|
||||
replaces:
|
||||
type: [object, 'null']
|
||||
description: 已按补遗确认更新:原来的时间和带来新值的文件(「已按补遗 1 更新 · 原为……」)
|
||||
required: [at, document_label]
|
||||
properties:
|
||||
at: { type: string }
|
||||
document_label: { type: string }
|
||||
superseded_pending: { type: boolean, description: 补遗带来了新值、等用户确认 }
|
||||
latest_check_run_id:
|
||||
oneOf:
|
||||
@@ -2514,6 +2521,8 @@ components:
|
||||
items_pending: { type: integer }
|
||||
items_absent: { type: integer, description: 重新解析时没再出现的旧条目 }
|
||||
lots_found: { type: integer }
|
||||
dates_superseded: { type: integer, description: 补遗:更早文件里有几条日期条目被标「已被补遗 N 更新」(FR-M2-10) }
|
||||
fields_pending: { type: integer, description: 补遗:项目字段里有几个新值等确认 }
|
||||
verify_failures:
|
||||
type: object
|
||||
description: 没通过校验的原因码和条数(如 quote_too_short、value_unverified),评测和排查用
|
||||
@@ -2691,6 +2700,13 @@ components:
|
||||
lot_no: { type: [string, 'null'], description: 空表示各包通用 }
|
||||
origin: { $ref: '#/components/schemas/Origin' }
|
||||
origin_label: { type: string, description: '如「AI」「人工添加」「补遗 1」' }
|
||||
origin_document:
|
||||
type: [object, 'null']
|
||||
description: 来自哪份招标侧文件(招标文件、补遗 N、澄清 N);人工添加的为 null
|
||||
required: [id, label]
|
||||
properties:
|
||||
id: { $ref: '#/components/schemas/Id' }
|
||||
label: { type: string }
|
||||
zone: { $ref: '#/components/schemas/Zone' }
|
||||
pending_reason:
|
||||
oneOf:
|
||||
@@ -2772,6 +2788,7 @@ components:
|
||||
description: 24 小时免扣窗口的结束时间(从上一次扣点的检查算起,P1)
|
||||
checklist_confirmed: { type: boolean, description: 为 false 时提示「清单未确认,检查结果以当前清单为准」 }
|
||||
pending_item_count: { type: integer, description: 待确认区条目不参与检查 }
|
||||
pending_updates: { type: integer, description: 补遗带来的新值、日期更新还没确认的处数;查标用旧值(AD4),只提示、不阻止 }
|
||||
bid_documents:
|
||||
type: array
|
||||
description: 默认用每个分册的最新版本
|
||||
|
||||
@@ -4,6 +4,19 @@
|
||||
|
||||
---
|
||||
|
||||
## AD1–AD6 · 2026-11-05 · 补遗追加:全部按建议
|
||||
|
||||
依据 `docs/architecture/addendum-plan.md` §7。
|
||||
|
||||
| # | 决策 | 理由 |
|
||||
|---|---|---|
|
||||
| AD1 | 补遗的新条目按招标文件同一套规则进正式清单(没出处、校验没过的进待确认区),标「补遗 N」;并入后清单回到草稿,要求重新确认 | 新旧条目都查,宁可多报,不漏报;全放待确认区的话,不逐条采纳就查不到补遗的新要求 |
|
||||
| AD2 | 只对日期类条目自动标「已被补遗 N 更新」(同一种日期、时间不同);参数等条目的逐条对照是变更判断,P1 | 按 FR-M2-10;没有补遗样本,先看真实写法 |
|
||||
| AD3 | 字段的新值在概览页逐个「采用 / 不采用」,日期条目在读标页逐条确认;采用投标截止时间时清单里的「投标截止」条目一起确认;采用时清掉人工值,以补遗为准 | 截止时间两处一次确认;补遗是更新的正式文件 |
|
||||
| AD4 | 没确认之前查标用旧值,确认后生效;检查预览提示还有几处没确认,不阻止检查 | 数据模型原定「确认后生效」;不阻断用户(红线 4) |
|
||||
| AD5 | 招标文件解析成功后才显示追加补遗的入口 | 没有招标文件时补遗的日期没有可关联的旧值 |
|
||||
| AD6 | 用真实模型对虚构的更正公告样例跑一次解析,结果只记录,这次不调提示词 | 看抽取对「原为……现更正为……」写法的表现,花费几分钱 |
|
||||
|
||||
## PI1–PI7 · 2026-11-04 · 项目基本信息修改和分包选择:全部按建议
|
||||
|
||||
依据 `docs/architecture/project-info-plan.md` §8。
|
||||
|
||||
Reference in New Issue
Block a user