Files
mini-openclaw-v2/backend/services/assistant_data.py
T
qiaoqichaoandClaude Code d56c699ce6 chore(v2): 从兴邦预审v2白名单抽取独立可运行基座
按 docs/v2/05-implementation-plan.md §1 候选迁移表与 claude_use/T00 白名单,
从 55e2cdb 抽取 224 个文件(2.48 MB)到独立目录 F:/mini-OpenClaw-v2。

- 排除:凭据(.env/server-deploy.conf/旧DELIVERY.md)、运行数据(xingbang_data 等
  4 个 SQLite)、依赖(venv/node_modules 符号链接)、归档(_backup*/test-evidence/
  ori_file)、Office 临时文件
- 目标目录独立 git 仓库,初始恢复点;Gitea 远程与分支待 OQ-06 Q2 定案
- 未部署、未启动服务、未调用模型

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-29 12:58:26 +08:00

79 lines
4.6 KiB
Python

"""Same read-only query/alignment contract for mock data and approved SQL sources."""
from copy import deepcopy
from decimal import Decimal
from api import risk_agent_config as catalog
from services.assistant_store import uid, digest
from services.assistant_materials import NUMERIC, scalar
from services.skill_executor import number
from api.risk_agent_alignment import MONEY_FIELDS, SCALE
def load_catalog():
with catalog._db() as db:
return {kind: catalog._all(db, kind) for kind in catalog.SEED}
def read_cards(project, components):
values, sources = deepcopy(project["values"]), {}
for field, refs in project["sources"].items():
sources[field] = [r for r in refs if r["id"] == project["selected_sources"].get(field)]
datasets, errors = [], []
if project.get("pending_candidates"):
errors.append({"card_id": "materials", "message": "存在尚未人工确认的材料候选,本次仍使用已确认事实"})
samples = {}
for (table, rows), mapping in zip(catalog.SAMPLES.items(), catalog.MAPPINGS):
row = {key: None for key in rows[0]}
row["project_id"] = project["id"]
for field, column in mapping.items():
row[column] = values.get(field)
row.update(project_id=project["id"])
if table == "project":
row["customer_name"] = project["customer"]
if table == "finance":
row.update(period=project.get("period", "2025"), scope=project.get("scope", "合并"))
samples[table] = [row]
for card in components.get("legacy_cards", []):
connection = next(c for c in components["legacy_connections"] if c["id"] == card["connection"])
if connection.get("data_domain", "demo" if connection.get("type") == "demo" else "production") != project["data_domain"]:
raise ValueError("演示数据与正式数据源不可混用")
try:
rows = catalog.query_card(card, connection, project["id"], demo_samples=samples, exact_decimals=True)
records, selected = catalog.align(card, rows, project["id"], project.get("period", "2025"))
datasets.append({"card_id": card["id"], "name": card["name"], "version": card["version"], "records": records, "row_hash": digest(rows)})
if selected is None:
errors.append({"card_id": card["id"], "message": "多期或多口径记录未选择,未合并计算"})
continue
for field, column in card.get("mapping", {}).items():
raw = selected["values"].get(field)
if field in NUMERIC:
raw = selected["raw"].get(column)
unit = card.get("field_units", {}).get(field, card.get("unit") if field in MONEY_FIELDS else None)
if raw is not None and unit:
raw = number(raw) * SCALE[unit]
raw = scalar(field, raw)
# Mock SQL uses the actual parsed fixture values; retain their file locators.
if connection.get("type") == "demo" and field in sources:
same = values.get(field) == raw
if field in NUMERIC and values.get(field) is not None and raw is not None:
same = number(values[field]) == number(raw)
if not same:
values[field] = None
errors.append({"card_id": card["id"], "message": field + "映射结果与原始材料冲突"})
continue
ref = {"id": uid("ev"), "project_id": project["id"], "field": field, "value": raw,
"source": card["name"], "locator": "数据卡字段 " + column, "card_id": card["id"], "version": card["version"],
"period": selected["period"], "scope": selected["scope"], "unit": selected["units"].get(field), "currency": card.get("currency", "CNY")}
same = values.get(field) == raw
if field in NUMERIC and values.get(field) is not None and raw is not None:
same = number(values[field]) == number(raw)
if field in values and not same:
values[field] = None
errors.append({"card_id": card["id"], "message": field + "多来源冲突,请人工确认"})
else:
values[field] = raw
sources.setdefault(field, []).append(ref)
except Exception as exc:
# No connection strings/credentials in project-facing diagnostics.
errors.append({"card_id": card["id"], "message": "数据卡读取或口径校验失败", "error_type": type(exc).__name__})
return values, sources, datasets, errors