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:
@@ -122,6 +122,36 @@ async def set_field(s: AsyncSession, ctx: TenantContext, project_id: UUID, key:
|
||||
)
|
||||
|
||||
|
||||
async def resolve_pending(s: AsyncSession, ctx: TenantContext, project_id: UUID, key: str, accept: bool) -> None:
|
||||
"""补遗带来的新值(FR-M2-10):采用 → 变成 AI 值、出处换成补遗那一句、清掉人工值(以补遗为准,AD3);
|
||||
不采用 → 丢掉待确认值,保持原值。都写审计。"""
|
||||
require(ctx, Permission.PROJECT_EDIT)
|
||||
p = await service.get(s, ctx, project_id)
|
||||
service.ensure_editable(p)
|
||||
f = (
|
||||
await s.execute(select(ProjectField).where(ProjectField.project_id == p.id, ProjectField.field_key == key))
|
||||
).scalar_one_or_none()
|
||||
if f is None or f.pending_value is None:
|
||||
raise AppError("no_pending_value")
|
||||
before = {"value": f.human_value if f.human_value is not None else f.ai_value, "pending": f.pending_value}
|
||||
if accept:
|
||||
f.ai_value, f.ai_citation_id = f.pending_value, f.pending_citation_id
|
||||
f.human_value, f.human_set_by, f.human_set_at = None, None, None
|
||||
_sync(p, key, f.ai_value)
|
||||
f.pending_value, f.pending_citation_id = None, None
|
||||
await s.flush()
|
||||
await audit.record(
|
||||
s,
|
||||
ctx,
|
||||
"project_field.resolve_pending",
|
||||
"project",
|
||||
p.id,
|
||||
project_id=p.id,
|
||||
before={key: before},
|
||||
after={key: {"accept": accept, "value": f.ai_value if accept else before["value"]}},
|
||||
)
|
||||
|
||||
|
||||
def _source(f: ProjectField) -> str:
|
||||
return "human" if f.human_value is not None else "ai" if f.ai_value is not None else "none"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user