diff --git a/pipeline_bidding/bid_analysis_capability.py b/pipeline_bidding/bid_analysis_capability.py index c4d8912..7942abc 100644 --- a/pipeline_bidding/bid_analysis_capability.py +++ b/pipeline_bidding/bid_analysis_capability.py @@ -436,6 +436,64 @@ async def add_doc_requirement(project_id, requirement, req_type="structure", return True, "投标文件要求已录入: %s %s" % (req_type, chapter_title or "") +async def update_doc_requirement(project_id, record_id="", chapter_no="", req_type="", + requirement="", source_ref="", page_limit="", + delete=False, who=None, agent_id=None): + """更新/删除既有投标文件要求条目(2026-09-03,根治 add 只增不改)。 + + 定位:record_id 优先;否则 chapter_no+req_type 唯一定位。 + - delete=True → 删除该条(编造/无出处项的处置); + - requirement/source_ref/page_limit 非空 → 回写原条目(纠正必须回写,不得只追加新条目)。 + """ + try: + project_id = await resolve_project_id(project_id) + except ValueError as e: + return False, str(e) + db, dbname = get_db() + async with db.sqlorContext(dbname) as sor: + if record_id: + recs = await sor.sqlExe( + "SELECT id FROM bid_doc_requirements WHERE project_id=${pid}$ AND id=${r}$", + {"pid": project_id, "r": record_id}) + elif chapter_no: + recs = await sor.sqlExe( + "SELECT id FROM bid_doc_requirements WHERE project_id=${pid}$ " + "AND chapter_no=${c}$ AND req_type=${rt}$", + {"pid": project_id, "c": str(chapter_no)[:30], + "rt": (req_type or "structure")[:30]}) + else: + return False, "需要 record_id 或 chapter_no 定位条目" + await sor.sqlExe("COMMIT", {}) + if not recs: + return False, "未找到条目(record_id=%s chapter_no=%s req_type=%s)" % ( + record_id, chapter_no, req_type) + if len(recs) > 1 and not record_id: + return False, "chapter_no+req_type 命中 %d 条,请改用 record_id 精确定位" % len(recs) + rid = getattr(recs[0], "id", "") + if delete: + await sor.sqlExe("DELETE FROM bid_doc_requirements WHERE id=${r}$", {"r": rid}) + await sor.sqlExe("COMMIT", {}) + await record(sor, project_id, "bid_doc_requirements", rid, "delete", + who=who, agent_id=agent_id, detail="删除条目") + return True, "已删除条目 %s" % rid + sets, p = [], {"r": rid} + if requirement: + sets.append("requirement=${rq}$"); p["rq"] = requirement + if source_ref: + sets.append("source_ref=${sr}$"); p["sr"] = str(source_ref)[:480] + if page_limit: + sets.append("page_limit=${pl}$"); p["pl"] = to_int(page_limit, 0) or None + if not sets: + return False, "无可更新字段(requirement/source_ref/page_limit 至少传一个)" + sets.append("updated_at=NOW()") + await sor.sqlExe("UPDATE bid_doc_requirements SET " + ", ".join(sets) + + " WHERE id=${r}$", p) + await sor.sqlExe("COMMIT", {}) + await record(sor, project_id, "bid_doc_requirements", rid, "update", + who=who, agent_id=agent_id, detail="回写原条目") + return True, "已更新条目 %s" % rid + + async def _upsert_tender_facts(project_id, facts, who=None, agent_id=None): """把投标要素写入 bid_tenders(source=tender_doc,复用原招标信息表承载商务要素)。 diff --git a/pipeline_bidding/role_tool_schemas.py b/pipeline_bidding/role_tool_schemas.py index 9ffb92b..0e57a13 100644 --- a/pipeline_bidding/role_tool_schemas.py +++ b/pipeline_bidding/role_tool_schemas.py @@ -110,6 +110,15 @@ BID_ROLE_TOOL_SCHEMAS = { "patches": "JSON数组[{chapter_no,title?,outline?,section?,scoring_item_ids?,scoring_items_by_name?,max_score?}]"}, "required": ["patches"], }, + "update_doc_requirement": { + "module": f"{_M}.bid_analysis_capability", + "description": "更新/删除既有投标文件要求条目(纠正必须回写原条目,不得只追加新条目形成矛盾对;编造/无出处项用 delete 删除)。add_doc_requirement 只增不改", + "params": {"project_id": "项目ID(可选,默认当前)", "record_id": "条目ID(优先)", + "chapter_no": "章节号(与req_type组合定位)", "req_type": "类型(structure/format/seal/copies/deadline/other)", + "requirement": "回写的新正文(可选)", "source_ref": "回写原文出处(可选)", + "page_limit": "页数(可选)", "delete": "true=删除该条(可选)"}, + "required": [], + }, "list_scoring_items": { "module": f"{_M}.bid_analysis_capability", "description": "列出评分项与得分规则",