#!/usr/bin/env python3
"""Build the normalized capability catalog, competitor matrix, and evidence ledger."""

from __future__ import annotations

import csv
import re
from dataclasses import dataclass
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
EXTRACT = ROOT / "source-extract"

COMPETITORS = [
    "康比特",
    "满客宝",
    "南京小牛",
    "银豹收银",
    "天财商龙",
    "收钱吧",
    "禧云国际",
    "利楚商服（扫呗）",
    "米果智能",
    "孚谷智慧食堂",
    "点单星",
    "壹当伍",
]

DOMAIN_CODES = {
    "前厅": "FH",
    "后厨": "BOH",
    "食安": "FS",
    "营养健康": "NH",
    "经营管理": "OM",
    "平台与集成": "PI",
    "交付运维": "DO",
    "智能硬件": "HW",
}


@dataclass(frozen=True)
class ModuleSpec:
    domain: str
    level1: str
    level2: str
    role: str
    touchpoint: str
    input_data: str
    business_rule: str
    output: str
    acceptance: str
    value: str
    priority: str
    nature: str
    source_ids: str
    features: tuple[str, ...]


def lines(text: str) -> tuple[str, ...]:
    return tuple(line.strip() for line in text.strip().splitlines() if line.strip())


MODULES = [
    ModuleSpec("前厅", "基础档案与身份", "人员与组织", "后勤管理员/人事/食客", "PC管理端/移动端/API", "人员、部门、岗位、项目、证件、手机号", "身份唯一、组织可追溯、离职及时停用", "可用就餐身份与组织关系", "新增/同步/停用后可在终端按权限识别，日志可追溯", "减少人工维护与冒用风险", "P0", "基础能力", "SRC-001;SRC-003", lines("""
人员管理
人员批量导入
组织架构同步
部门与项目分组
岗位与人员类型
实名信息校验
手机号与证件去重
身份生效与失效日期
离职人员自动停用
黑名单与白名单
临时人员档案
家庭成员关系
特殊人群标签
人员信息脱敏展示
人员变更审计
跨食堂人员共享
""")),
    ModuleSpec("前厅", "基础档案与身份", "介质与认证", "卡务员/食客/访客", "卡务台/消费终端/小程序", "实体卡、二维码、人脸、掌纹、工号", "一人多介质可控、挂失即时生效、认证失败可降级", "可认证的支付与核销介质", "绑介质、挂失、解挂、换卡、补卡均可回查", "提升通行与结算可靠性", "P0", "基础能力", "SRC-001", lines("""
卡务管理
实体卡开户
实体卡绑定
实体卡挂失
实体卡解挂
补卡与换卡
二维码身份码
动态付款码
人脸注册
人脸更新与注销
掌纹/掌静脉注册
工号密码认证
身份证认证
多介质优先级
认证失败降级
身份介质操作日志
""")),
    ModuleSpec("前厅", "账户与福利", "账户体系", "财务/后勤/卡务员/食客", "PC管理端/小程序/消费终端", "账户类型、余额、有效期、预算科目", "账户分账、余额不可透支或按策略透支、变更留痕", "可结算账户与完整账本", "余额、流水、调整原因和操作者可核对", "支撑复杂福利与资金管理", "P0", "基础能力", "SRC-001;SRC-003", lines("""
账户管理
现金账户
补贴账户
福利账户
招待账户
积分账户
礼品账户
多账户并存
账户支付优先级
账户有效期
账户冻结与解冻
账户透支控制
余额查询
账户流水查询
账户余额调整
账户调账审批
账户销户
跨项目账户隔离
""")),
    ModuleSpec("前厅", "账户与福利", "充值与补贴", "财务/后勤/员工", "PC管理端/小程序/自助终端", "充值金额、补贴规则、发放名单、资金渠道", "发放可撤销、重复发放拦截、资金账实一致", "充值/补贴到账与凭证", "批量发放、个人充值、撤销后账本一致", "提升福利发放效率", "P0", "基础能力", "SRC-001;SRC-003", lines("""
充值策略
线上充值
线下现金充值
银行卡充值
企业批量充值
按部门补贴发放
按人员类型补贴发放
按周期自动发放
按出勤补贴发放
按餐次补贴发放
补贴有效期
补贴清零与结转
补贴撤销
充值退款
充值限额
充值对账
福利管理
福利餐券
""")),
    ModuleSpec("前厅", "消费与定价", "消费策略", "后勤/财务/食客", "PC管理端/消费终端", "人员类型、餐次、档口、日期、额度、次数", "策略按场景命中且可解释，冲突有优先级", "可执行的消费规则", "同一用户在边界场景下扣费与拒绝结果符合配置", "控制预算与就餐秩序", "P0", "基础能力", "SRC-001;SRC-003", lines("""
消费策略
定额扣费
按次扣费
按金额扣费
按重量扣费
免扣费
折扣价
阶梯价
会员价
员工价与访客价
餐次限制
档口限制
品类限制
日期与节假日限制
日消费额度
月消费额度
单笔限额
消费次数限制
超额自费
补贴与现金组合支付
多人共享额度
策略命中说明
""")),
    ModuleSpec("前厅", "菜品与菜单", "菜单发布", "厨师长/档口/食客", "PC管理端/电子菜牌/小程序", "菜品、价格、图片、营养、库存、餐次", "发布有生效范围与版本，售罄及时同步", "可售菜单与展示内容", "不同食堂/档口/餐次看到正确菜单与价格", "减少错价与信息不一致", "P0", "基础能力", "SRC-001;SRC-003", lines("""
菜品管理
标准菜品库
菜品分类
菜品图片
菜品标签
菜品价格
菜品营养信息
菜品过敏原
菜品上下架
菜品售罄
餐次菜单
档口菜单
周期菜单
临时菜单
菜单审批
菜单版本管理
菜品重复率提醒
信息发布
""")),
    ModuleSpec("前厅", "订餐与报餐", "预订流程", "食客/部门管理员/档口", "小程序/H5/PC管理端/接单端", "菜单、人数、截止时间、用餐地点、配送信息", "库存/产能校验、超时规则、退订规则明确", "可履约订单与备餐量", "下单、支付、改单、退订、核销全状态可追踪", "以预定减少浪费并提升备餐准确性", "P0", "基础能力", "SRC-001;SRC-003", lines("""
预定管理
线上订餐
个人报餐
扫码预定
集体订餐
包厢预定
会议餐预定
加班餐预定
年节福利预定
周期订餐
套餐订餐
自选菜品订餐
预订截止时间
预订人数上限
改单
取消与退订
候补排队
订单备注与忌口
订餐消息提醒
订餐统计
""")),
    ModuleSpec("前厅", "现场结算", "自助结算场景", "食客/收银员/档口", "消费机/称重台/RFID餐台/AI结算台/闸机", "身份、菜品、重量、价格、消费策略", "识别与计价一致、失败可人工处理、离线可控", "支付结果与消费订单", "高峰连续交易、重复扣款防护、异常订单可追溯", "缩短排队并减少收银人力", "P0", "基础能力", "SRC-001;SRC-002;SRC-003;SRC-005", lines("""
美食档口-纯消费机
美食档口-称重结算
美食档口-点餐叫号
按克计费
小碗菜（RFID）
小碗菜（AI识别）
自助选餐
托盘绑盘
托盘解绑
RFID菜品识别
AI视觉菜品识别
菜品人工纠正
重量去皮
多菜品合并结算
无感支付
闸机结算
反向闸机
定额通行
结算小票
离线结算
订单防重复提交
异常订单挂账
""")),
    ModuleSpec("前厅", "支付与退款", "支付渠道", "食客/财务/收银员", "消费终端/小程序/支付网关", "订单、支付渠道、商户号、金额", "一单一支付状态、回调幂等、退款原路退", "支付凭证与清分账单", "支付、撤销、退款、补单与对账结果一致", "保障资金安全与拓展支付收益", "P0", "集成能力", "SRC-001;SRC-002;SRC-003", lines("""
余额支付
微信支付
支付宝支付
银联支付
银行卡支付
聚合码支付
实体卡支付
二维码支付
人脸支付
掌纹支付
混合支付
第三方代扣
支付预授权
支付撤销
整单退款
部分退款
退款审核
支付补单
支付回调幂等
支付渠道对账
自动清分
服务商返利统计
""")),
    ModuleSpec("前厅", "取餐与履约", "叫号自提配送", "食客/档口/配送员", "叫号屏/小程序/取餐柜/配送端", "订单、取餐码、柜格、配送地址", "一单一核销、超时可处理、错拿可追溯", "已交付餐品与履约记录", "叫号、入柜、通知、取餐、配送各状态闭环", "提升取餐秩序和多场景履约", "P1", "场景能力", "SRC-001;SRC-002", lines("""
排队叫号
订单接单
制作中状态
待取餐状态
取餐码
扫码核销
人脸核销
取餐柜入柜
取餐柜通知
取餐柜自提
超时未取提醒
错拿申诉
外卖点餐
配送地址管理
配送任务分配
配送轨迹
无人车配送接口
配送完成确认
""")),
    ModuleSpec("前厅", "访客与服务", "访客招待和互动", "接待人员/访客/员工/运营", "PC管理端/小程序/前台终端", "访客、接待部门、预算、时间、人数", "申请审批后可用、超额受控、消费归属明确", "招待额度、访客身份和服务反馈", "访客创建到消费对账全链可回查", "支撑政企招待与体验运营", "P1", "场景能力", "SRC-001", lines("""
访客招待
访客预约
访客临时码
访客人脸登记
招待申请
招待审批
招待额度
招待订单归属
陪餐管理
食堂评价
意见反馈
投诉工单
打卡互动
员工商城
券码核销
优惠券管理
积分兑换
消息通知
""")),
    ModuleSpec("前厅", "展示与零售", "大屏与智慧零售", "食客/运营/领导", "电子菜牌/大屏/售卖柜/零售POS", "菜单、库存、营收、客流、设备状态", "展示数据有更新时间，零售库存与订单一致", "信息看板与零售订单", "大屏数据可追溯，售卖柜出货与支付一致", "扩大服务触点并提高信息透明度", "P1", "场景能力", "SRC-001;SRC-003", lines("""
电子菜牌
就餐引导大屏（营养）
经营大屏
余量看板
菜品结构销售
热销菜品排行
客流看板
营养公示
零售管理
智慧零售
智能售卖柜
无人零售库存
售卖柜出货回调
商品条码管理
零售促销
""")),

    ModuleSpec("后厨", "菜品与配方", "标准菜品和BOM", "厨师长/营养师/成本会计", "PC管理端/移动端", "菜品、原料、单位、损耗率、营养数据", "配方版本可追溯、单位换算一致、损耗计入成本", "标准配方、成本和营养基线", "按人数放大配方后原料量与成本可复算", "形成标准化生产和成本基础", "P0", "基础能力", "SRC-001;SRC-003", lines("""
货品管理
原料分类
原料档案
单位与换算
菜品标准配方
菜品BOM
配方版本
主辅料配置
调料配置
净料率
损耗率
成品率
份量标准
菜品成本卡
菜品营养计算
过敏原继承
配方审批
配方复制
""")),
    ModuleSpec("后厨", "排菜与计划", "菜单计划和需求反算", "厨师长/采购/仓管", "PC管理端/移动端", "用餐人数、菜单、配方、库存、损耗", "需求按配方和人数反算，库存与在途要扣减", "生产计划、采购需求、领料计划", "变更人数或菜单后需求量、缺口和成本同步更新", "减少拍脑袋采购和浪费", "P0", "基础能力", "SRC-001;SRC-003", lines("""
排菜管理
智能排菜
周期排菜
餐次排菜
多食堂排菜
菜单复制
排菜审批
临时加餐
停餐与减餐
预计就餐人数
预订量汇总
历史销量预测
需求反算
原料需求计划
缺口计算
领料计划
生产任务单
菜单变更联动
""")),
    ModuleSpec("后厨", "供应商与采购", "供应商管理", "采购/食安/财务", "PC管理端/供应商端", "供应商、证照、合同、品类、价格", "准入有审批、证照到期预警、黑名单全局生效", "合格供应商库与履约档案", "准入、续证、考核、停用可回查", "降低供应与合规风险", "P0", "基础能力", "SRC-001", lines("""
供应商管理
供应商准入
供应商证照
证照到期预警
供应品类授权
供应商报价
供应商合同
供应商考核
供应商评级
供应商黑名单
供应商停用
供应商履约记录
供应商退货统计
供应商端账号
""")),
    ModuleSpec("后厨", "供应商与采购", "询价招标采购", "采购/审批人/供应商", "PC管理端/供应商端", "采购需求、预算、报价、合同、交期", "比价可追溯、审批分权、超预算拦截", "采购订单与审批凭证", "询价到下单全流程有时间、人员和价格证据", "降低采购成本并规范流程", "P0", "基础能力", "SRC-001", lines("""
货品招标/询价
采购申请
采购计划
采购预算
询价单
供应商在线报价
多供应商比价
历史价格对比
市场价格参考
定标审批
采购合同关联
采购订单
采购订单变更
紧急采购
协议价采购
集采分单
采购进度跟踪
采购到货提醒
采购统计
""")),
    ModuleSpec("后厨", "验收与入库", "收货验收", "仓管/食安员/采购", "收货端/电子秤/PC管理端", "采购单、实收重量、批次、温度、票证", "订单、实物、票证三单匹配；不合格可拒收", "验收单、入库单与不合格记录", "称重、拍照、批次、偏差、处置均可追溯", "控制源头质量和数量偏差", "P0", "基础能力", "SRC-001", lines("""
验货收货
采购单到货匹配
验收电子秤对接
毛重皮重净重
实收数量
到货批次
生产日期与保质期
到货温度
检验检疫票证
索证索票
到货拍照
数量偏差预警
价格偏差预警
质量验收
不合格拒收
部分收货
退货单
验收入库联动
验收人员签名
""")),
    ModuleSpec("后厨", "库存管理", "出入库与盘点", "仓管/厨师长/财务", "PC管理端/移动PDA/电子秤", "批次、库位、数量、成本、保质期", "先进先出、负库存受控、每次移动留痕", "库存台账、成本和预警", "账面库存与盘点一致，差异有审批与原因", "降低积压、缺货和损耗", "P0", "基础能力", "SRC-001", lines("""
出入库管理
采购入库
生产领料出库
销售出库
调拨出入库
退货出库
退料入库
报损出库
库存管理
多仓库管理
库区库位管理
批次管理
先进先出
保质期管理
临期预警
安全库存
缺货预警
呆滞库存
库存盘点
循环盘点
盘盈盘亏
负库存控制
库存成本核算
库存台账看板
""")),
    ModuleSpec("后厨", "加工与生产", "切配烹饪和出品", "厨师长/切配员/烹饪员", "后厨工位屏/移动端/智能烹饪设备", "生产任务、配方、原料、工艺、时间温度", "关键工序按标准执行，异常需记录", "完成的生产批次与出品记录", "领料、切配、烹饪、出品数量和时间可追溯", "提高出品一致性与人效", "P1", "生产能力", "SRC-001;SRC-003", lines("""
切配管理
切配任务分派
净菜加工
原料解冻记录
加工批次
加工损耗记录
标准工艺卡
烹饪任务
智能烹饪设备下发
烹饪时间记录
中心温度记录
出品数量
出品称重
出品质量确认
备餐台补菜提醒
余量回收
剩余菜品处置
""")),
    ModuleSpec("后厨", "中央厨房与配送", "分单生产配送", "中央厨房/门店/配送员", "PC管理端/生产端/配送端", "门店需求、路线、箱筐、温度、签收", "按门店分单、全程批次与温控可追溯", "配送任务、签收与差异记录", "从生产批次到门店签收可一键追溯", "支撑多点供餐与中央厨房", "P1", "场景能力", "SRC-001", lines("""
中央厨房管理
门店需求汇总
极速分单
生产批次分配
分拣任务
装箱装筐
周转箱管理
配送计划
配送路线
配送车辆
司机与配送员
装车复核
配送温度
配送轨迹
门店签收
签收差异
退筐回收
配送时效统计
""")),
    ModuleSpec("后厨", "成本与财务", "成本应付结算", "财务/采购/运营", "PC管理端/财务接口", "采购价、库存成本、菜品销量、供应商账单", "成本口径统一、应付可对账、差异可解释", "成本报表与应付单", "采购、库存、领料、销售和财务数据勾稽", "提升毛利与资金管理", "P1", "经营能力", "SRC-001", lines("""
财务管理
采购应付
供应商对账
发票关联
付款申请
菜品理论成本
菜品实际成本
食材成本率
餐次成本
食堂成本
预算执行
成本差异分析
自动调价建议
价格趋势
采购节约额
""")),
    ModuleSpec("后厨", "清洁与资源", "清洁废弃物能耗", "后厨主管/保洁/设备管理员", "移动端/物联平台/PC管理端", "清洁计划、废弃物重量、水电燃数据", "任务闭环、异常预警、计量数据可追溯", "清洁记录、废弃物台账和能耗数据", "计划、执行、拍照、复核完整", "降低安全风险和运营消耗", "P1", "运营能力", "SRC-001", lines("""
收餐清洁
清洁计划
清洁任务
餐具回收
餐具清洗
餐具消毒
清洁拍照
清洁复核
废弃物管理
厨余称重
厨余去向记录
废油回收
垃圾分类
水耗监测
电耗监测
燃气监测
能耗统计
异常能耗预警
""")),

    ModuleSpec("食安", "主体与人员", "食堂人员供应商档案", "食安管理员/监管人员", "PC管理端/移动端/监管端", "食堂、人员、供应商、证照、岗位", "主体唯一、证照有效、责任到人", "完整食安主体档案", "抽查任一主体可查看证照、人员和责任链", "夯实监管底账", "P0", "基础能力", "SRC-001", lines("""
食堂管理
食堂主体档案
食安责任人
从业人员档案
健康证管理
健康证到期预警
供应商食安档案
供应商证照审查
岗位责任清单
风险分级
主体二维码公示
""")),
    ModuleSpec("食安", "人员健康", "晨检培训考试", "食安员/员工/监管人员", "晨检仪/移动端/PC管理端", "体温、手部、伤口、健康问答、培训记录", "异常人员不得上岗，培训考试留痕", "晨检结果与上岗状态", "异常识别、处置、复核和统计闭环", "降低人员带病上岗风险", "P0", "基础能力", "SRC-001", lines("""
晨检管理
体温检测
手部图像检查
伤口检查
健康问答
晨检异常拦截
异常人员复检
上岗状态
学习培训管理
培训课程
培训签到
考试管理
题库管理
考试成绩
培训证书
""")),
    ModuleSpec("食安", "过程监管", "明厨亮灶与AI识别", "食安员/监管人员/公众", "视频平台/AI盒子/大屏/移动端", "摄像头、区域、算法、营业时段", "事件有截图、时间、区域、处置和复核", "可视视频与AI违规事件", "违规识别、告警、派单、整改可重放", "由被动查看转向主动预警", "P0", "差异能力", "SRC-001;SRC-003", lines("""
互联网+明厨亮灶
视频实时预览
视频回放
多画面轮巡
摄像头在线状态
AI分析盒子接入
未戴帽识别
未戴口罩识别
未穿工服识别
吸烟识别
玩手机识别
鼠患识别
陌生人闯入识别
垃圾桶未盖识别
动火离人识别
违规事件截图
违规事件视频片段
AI事件误报复核
""")),
    ModuleSpec("食安", "过程监管", "留样农残添加剂", "食安员/检测员/监管人员", "留样柜/检测仪/移动端", "菜品、样品重量、时间、检测结果、责任人", "留样按餐次和时长执行，检测异常立即处置", "留样与检测台账", "样品、标签、时间、温度、检测结果可关联订单/菜单", "满足关键食安制度要求", "P0", "基础能力", "SRC-001;SRC-003", lines("""
留样管理
留样菜品登记
留样重量
留样时间
留样温度
留样标签打印
留样柜格口绑定
留样到期提醒
留样销毁记录
农残检测
农残检测仪接入
检测批次
检测结果上传
不合格处置
食品添加剂管理
添加剂采购登记
添加剂领用登记
添加剂使用限量
""")),
    ModuleSpec("食安", "环境设备", "环境消毒三防能耗", "食安员/设备管理员", "传感器/移动端/PC管理端", "温湿度、烟雾、水电燃、清洁消毒、设备状态", "阈值分级、告警有效期和处置责任明确", "环境设备台账和异常事件", "传感器异常可定位设备、区域和处置结果", "降低环境和设备事故", "P0", "物联能力", "SRC-001", lines("""
环境管理
温度监测
湿度监测
烟雾监测
燃气泄漏监测
用水异常监测
用电异常监测
冷库温度监测
冰箱温度监测
消毒管理
消毒任务
消毒记录
紫外消毒灯管理
挡鼠板巡检
灭蝇灯巡检
三防设施台账
设备安全管理
设备维保到期预警
能耗管理
""")),
    ModuleSpec("食安", "巡检整改", "任务预警闭环", "食安员/负责人/监管人员", "移动端/PC管理端/监管端", "检查表、风险项、整改时限、证据", "问题分级、超时升级、复核后关闭", "检查记录、整改工单和证据包", "发现—派单—整改—复核—销项全链可追踪", "把食安制度落实为闭环", "P0", "基础能力", "SRC-001", lines("""
巡检管理
检查表模板
日常自检
专项检查
督导巡查
陪餐上报
巡检打分
问题拍照
问题分级
整改任务
整改责任人
整改时限
整改证据上传
复核通过
复核驳回
逾期提醒
逾期升级
预警中心
预警分级
预警订阅
预警处置
预警闭环率
""")),
    ModuleSpec("食安", "溯源应急", "溯源召回和应急", "食安负责人/监管人员/采购", "PC管理端/移动端/监管端", "供应商、批次、验收、库存、菜品、订单", "正向反向追溯可完成，召回对象可圈定", "追溯链、召回记录和应急报告", "从菜品到原料批次及供应商可追溯，反向亦可", "缩短事故定位和处置时间", "P1", "监管能力", "SRC-001", lines("""
食材溯源
批次正向追溯
批次反向追溯
菜品到原料追溯
订单到菜品追溯
供应商到批次追溯
问题批次召回
召回范围计算
召回通知
召回处置记录
应急管理
应急预案
应急事件上报
应急处置流程
事件复盘报告
""")),
    ModuleSpec("食安", "多端监管", "移动大屏监管公众", "执行人员/领导/监管机构/家长公众", "移动端/大屏/监管端/公众端", "食安台账、视频、预警、整改、公开信息", "不同角色只看授权范围，公开内容脱敏", "多角色监管视图", "多校/多食堂汇总下钻、移动处置和公开查询可用", "提升监管透明度和协同效率", "P1", "场景能力", "SRC-001", lines("""
执行端移动办公
食安管理汇总
移动预警提醒
移动巡检
移动整改
食安管理公示
环境监管公示
能耗公示
监管驾驶舱
数据总览
台账监管
学校独立监管
教委监管多校
政府独立监管
企业独立监管
连锁餐饮监管
中央厨房食安监管
家长/公众端监管
意见反馈
""")),

    ModuleSpec("营养健康", "健康档案", "个人基础与风险档案", "食客/营养师/健康管理员", "小程序/PC营养端/API", "基础信息、体检、慢病、过敏、目标、运动", "本人授权、敏感信息最小可见、数据有来源日期", "个人健康画像", "档案变更、授权、来源和有效期可追溯", "为个性化营养奠定数据基础", "P0", "差异能力", "SRC-001;SRC-003", lines("""
档案建立
头像
性别
年龄
身高
体重
BMI
腰臀围
劳动强度
过敏原
忌口
慢性病
怀孕状态
住院状态
饮食医嘱
体重控制目标
运动目标
健康档案授权
档案数据来源
""")),
    ModuleSpec("营养健康", "健康检测", "体检微体检设备连接", "食客/健康管理员/营养师", "健康小屋/体脂秤/手环/小程序", "体检报告、体成分、血压、运动与睡眠", "指标单位和时间统一，异常仅作建议不替代诊断", "长期+短期健康指标", "设备数据能绑定本人、去重、展示趋势并可撤回授权", "形成餐前餐中餐后健康闭环", "P1", "差异能力", "SRC-003", lines("""
体检报告导入
体检报告结构化
微体检
体成分接入
体脂率
肌肉量
基础代谢
血压接入
心率接入
血糖接入
血氧接入
尿酸接入
血脂接入
骨密度接入
运动手环接入
运动手表接入
步数
运动消耗
睡眠数据
指标趋势
异常指标提醒
""")),
    ModuleSpec("营养健康", "菜品营养", "菜品营养标签与计算", "营养师/厨师长/食客", "PC营养端/电子菜牌/结算端", "配方、重量、营养数据库、烹饪损失", "同口径计算、份量与实际重量区分、来源可追溯", "菜品和餐次营养值", "配方变化后营养值自动重算并显示来源", "让营养能力进入真实菜品与结算", "P0", "差异能力", "SRC-001;SRC-003", lines("""
菜品营养计算
营养数据库
食材营养成分
烹饪损失系数
份量营养
实际重量营养
能量
碳水化合物
蛋白质
脂肪
膳食纤维
钠
胆固醇
维生素
矿物质
过敏原提示
低盐低脂标签
高蛋白标签
菜品营养公示
""")),
    ModuleSpec("营养健康", "餐前指导", "推荐配餐与菜单", "食客/营养师/厨师长", "小程序/结算端/PC营养端", "健康档案、目标、菜单、预算、库存", "推荐可解释、禁忌硬拦截、用户可自主选择", "个性化推荐与群体配餐", "同一档案在相同菜单下推荐稳定，禁忌不被推荐", "把专业营养转成可执行选择", "P0", "差异能力", "SRC-001;SRC-003", lines("""
餐前推荐菜品
餐前菜品评星
个性化配餐
群体配餐
营养方案
慢病配餐
减脂配餐
增肌配餐
孕产配餐
老年配餐
运动员配餐
营养师配置规则
禁忌菜品拦截
营养目标分配
预算约束推荐
库存约束推荐
菜单营养均衡检查
""")),
    ModuleSpec("营养健康", "餐中指导", "取餐与结算营养反馈", "食客/营养师", "称重台/AI结算台/小程序/大屏", "已选菜品、重量、个人目标、历史摄入", "实时计算、推荐与实际分开、超量只提示或按策略拦截", "本餐摄入与即时建议", "加减菜后营养值和目标差距实时变化", "在选择时影响健康行为", "P0", "差异能力", "SRC-001;SRC-003", lines("""
本餐能量实时计算
本餐碳水实时计算
本餐蛋白实时计算
本餐脂肪实时计算
本餐钠实时计算
本餐纤维实时计算
本餐推荐值
营养目标进度
取餐超量预警
取餐不足提醒
菜品替换建议
营养搭配提示
未绑盘取餐预警
无盘取餐预警
营养结算小票
""")),
    ModuleSpec("营养健康", "餐后分析", "记录报告与干预", "食客/营养师/健康管理员", "小程序/PC营养端/报告", "消费订单、实际摄入、健康指标、运动数据", "订单自动记账、手工记录有标识、建议基于证据", "个人/群体营养报告和行动建议", "日周月口径可复算，建议可回看和评价", "促进持续健康管理", "P0", "差异能力", "SRC-001;SRC-003", lines("""
膳食记录
订单自动记餐
拍照记餐
手工记餐
三餐比
供能比
膳食结构
日营养报告
周营养报告
月营养报告
实际与推荐对比
营养达标率
饮食评价
餐后饮食建议
纸质营养报告
健康宣教
营养科普
干预任务
干预效果评估
""")),
    ModuleSpec("营养健康", "AI营养师", "问答与动态建议", "食客/营养师/运营", "小程序/H5/PC营养端", "授权健康档案、饮食记录、知识库、菜单", "回答有来源边界、不作医疗诊断、敏感建议可转人工", "可解释问答与个性化行动建议", "典型问题正确引用本人数据与当前菜单，越界问题安全拒答", "放大专业服务能力", "P1", "AI差异能力", "SRC-003", lines("""
AI互动问答
基于健康档案问答
基于当日菜单问答
基于饮食记录问答
基于运动消耗问答
菜品推荐
饮食计划生成
健康风险解释
营养知识检索
连续对话记忆
建议原因解释
引用来源展示
人工营养师转接
高风险问题安全提示
问答反馈评价
""")),

    ModuleSpec("经营管理", "组织与集团", "多租户集团管控", "集团后勤/区域经理/食堂经理", "集团PC端/移动端", "集团、区域、法人、项目、食堂、档口、商户", "数据按租户隔离、组织继承可覆盖、跨层级可汇总", "统一组织与经营视图", "新增组织后权限、配置和数据范围正确", "支撑集团多地复制", "P0", "平台能力", "SRC-001;SRC-003", lines("""
多租户管理
集团管理
区域管理
项目管理
多食堂管理
餐厅架构
档口管理
商户管理
组织配置继承
项目个性配置
集团标准模板
跨区域数据汇总
""")),
    ModuleSpec("经营管理", "订单营收", "订单报表对账", "运营/财务/领导", "PC管理端/移动端/大屏", "订单、退款、支付、账户、档口、餐次", "订单与支付一一对应、统计口径有定义、跨日可追溯", "经营报表与对账结果", "明细汇总一致，筛选维度和导出结果一致", "提高经营透明度", "P0", "经营能力", "SRC-001;SRC-003", lines("""
订单管理
消费订单
订单状态
订单明细
订单来源
订单退款状态
营收管理（账户）
餐厅营收统计
档口营收统计
商户营收统计
餐次营收统计
人员消费统计
支付方式统计
客流统计
客单价
退款统计
统计报表
自定义报表
报表订阅
报表导出
""")),
    ModuleSpec("经营管理", "经营分析", "驾驶舱与经营优化", "领导/运营/厨师长", "经营大屏/PC管理端", "订单、客流、菜品、库存、成本、预订、浪费", "指标定义透明、可下钻到明细、异常有解释", "经营指标、趋势和改进线索", "总览与明细勾稽，时间范围和更新时间清晰", "提升人效、控浪费、识别爆品", "P1", "经营能力", "SRC-001;SRC-003", lines("""
经营驾驶舱
今日经营总览
日周月趋势
热销菜品
滞销菜品
菜品结构分析
餐次客流分析
档口排行
支付方式占比
订单来源分析
预订准确率
备餐偏差
售罄率
剩余率
食材浪费率
人效分析
设备利用率
异常经营提醒
""")),
    ModuleSpec("经营管理", "财务票据", "结算开票清分", "财务/商户/后勤", "财务端/商户端/API", "订单、渠道账单、商户合同、税务信息", "账期规则统一、差异可定位、票据与收入关联", "结算单、发票和清分结果", "平台账、支付账、商户账三方可核对", "减少财务人工与资金差错", "P1", "经营能力", "SRC-001;SRC-003", lines("""
日结
月结
支付渠道结算
商户结算
档口分账
补贴资金结算
对账差异
差错处理
清分规则
结算单
开票管理
电子发票
发票抬头
开票申请
发票状态
财务凭证接口
""")),

    ModuleSpec("平台与集成", "部署架构", "SaaS私有化信创", "IT管理员/交付/运维", "部署平台/配置中心", "租户、域名、服务器、数据库、中间件、证书", "部署模式与环境隔离、配置可审计、升级可回滚", "可运行环境与部署清单", "公有云/私有化/政务云环境按基线部署并通过回验", "满足不同客户合规和成本需求", "P0", "交付能力", "SRC-003", lines("""
公有云SaaS
私有化部署
政务云部署
本地离线部署
国产操作系统适配
国产数据库适配
国产中间件适配
国产CPU适配
信创环境适配
多环境隔离
域名与证书管理
环境参数中心
租户初始化
版本基线登记
""")),
    ModuleSpec("平台与集成", "可靠性", "弱网离线容灾", "运维/店长/收银员", "终端/边缘服务/云平台", "本地缓存、同步队列、网络状态、订单", "离线可控、恢复幂等、冲突有规则、数据不丢不重", "连续交易与同步结果", "断网交易、恢复补传、重复回调和时钟偏差场景通过", "保障高峰不断餐", "P0", "差异能力", "SRC-003", lines("""
高峰弱网运行
局域网模式
离线记账
断点续传
数据补传
同步队列
同步失败重试
冲突检测
冲突解决策略
重复数据去重
终端时钟校准
网络状态监测
离线额度控制
恢复后对账
边缘节点
""")),
    ModuleSpec("平台与集成", "开放连接", "身份支付业务接口", "IT管理员/研发/交付", "API网关/消息总线/文件交换", "接口凭证、字段映射、回调、重试策略", "最小权限、幂等、限流、脱敏、失败可补偿", "稳定接口与同步台账", "接口成功率、延迟、失败重试和对账可监控", "降低项目定制和替换成本", "P0", "集成能力", "SRC-003", lines("""
开放API
API鉴权
API限流
API幂等
Webhook回调
消息队列集成
文件批量交换
字段映射配置
组织人员接口
一卡通接口
门禁接口
财务系统接口
ERP接口
OA审批接口
企业微信接口
钉钉接口
统一身份SSO
支付渠道接口
体检系统接口
健康设备接口
视频平台接口
第三方设备SDK
接口监控
接口对账
""")),
    ModuleSpec("平台与集成", "权限安全", "权限审计隐私", "系统管理员/审计/安全人员", "统一管理后台", "用户、角色、组织、资源、操作、敏感字段", "最小权限、职责分离、关键操作双人或审批", "受控访问与审计证据", "越权测试失败、审计日志完整、数据导出受控", "满足政企安全与隐私要求", "P0", "安全能力", "SRC-003", lines("""
用户管理
角色管理
菜单权限
数据权限
组织范围权限
字段权限
按钮权限
临时授权
授权到期
权限审批
管理员分权
操作审计
登录日志
敏感字段脱敏
数据导出审批
水印
密码策略
多因素认证
会话超时
异常登录告警
""")),
    ModuleSpec("平台与集成", "配置与智能", "低代码AI报表规则", "产品运营/实施/客户管理员", "配置中心/报表中心/AI助手", "字段、规则、页面、报表需求、自然语言", "配置变更有版本、发布前校验、生成内容可审阅", "可复用配置和报表", "无需改代码完成常见客户差异并可回滚", "缩短交付周期", "P1", "差异能力", "SRC-003", lines("""
业务参数配置
数据字典
流程配置
审批流配置
表单配置
页面字段配置
打印模板配置
消息模板配置
报表字段选择
自助报表
自然语言生成报表
报表口径说明
配置版本管理
配置发布审批
配置回滚
项目配置包
""")),
    ModuleSpec("平台与集成", "商业化平台", "续费渠道与代理", "运营/财务/渠道经理/代理商", "运营后台/代理商后台", "合同、订阅、租户、渠道、佣金、版本", "到期提醒、权限变更可控、渠道数据隔离", "订阅状态、渠道业绩与佣金", "到期、续费、停复用、代理开通和结算闭环", "提升SaaS和渠道收入可持续性", "P1", "商业能力", "SRC-003", lines("""
产品版本管理
功能套餐
订阅合同
订阅到期提醒
自动续费提醒
逾期宽限期
权限自动降级
租户停用与恢复
续费订单
代理商管理
代理商后台
渠道客户管理
渠道报价权限
渠道订单
渠道佣金
支付返利管理
""")),

    ModuleSpec("交付运维", "实施交付", "项目初始化上线验收", "项目经理/实施/客户管理员", "交付平台/项目文档", "合同范围、环境、组织、设备、接口、验收项", "范围冻结、变更受控、每一步有证据", "上线系统与验收证据包", "从环境预检到验收签字均有负责人、时间、结果", "降低定制化交付不确定性", "P0", "交付能力", "SRC-003", lines("""
项目范围清单
需求差异清单
部署模式冻结
服务器预检
网络预检
端口与白名单
基础数据模板
组织人员初始化
菜单菜品初始化
消费策略初始化
设备点位清单
设备安装登记
接口联调计划
数据迁移
试运行计划
培训计划
上线检查单
验收用例
验收证据包
项目变更管理
""")),
    ModuleSpec("交付运维", "运行监控", "应用设备任务监控", "运维/实施/客户IT", "监控平台/告警中心", "服务、接口、任务、设备、日志、指标", "告警分级、降噪、责任路由、恢复通知", "告警事件与健康状态", "故障可定位到项目、服务、接口或设备并可复盘", "缩短故障发现和恢复时间", "P0", "运维能力", "SRC-003", lines("""
应用健康监控
数据库健康监控
缓存健康监控
消息队列监控
定时任务监控
接口成功率监控
支付回调监控
数据同步监控
终端在线监控
设备心跳
设备故障码
日志检索
链路追踪
告警分级
告警抑制
恢复通知
值班升级
服务状态页
""")),
    ModuleSpec("交付运维", "版本与灾备", "升级备份恢复", "运维/研发/客户IT", "发布平台/备份系统", "版本包、变更记录、数据库、配置、文件", "发布可回滚、备份可恢复、客户版本可追溯", "受控版本与灾备结果", "备份恢复演练和版本回退通过，数据一致", "降低升级与数据丢失风险", "P0", "运维能力", "SRC-003", lines("""
版本发布记录
客户版本台账
灰度发布
终端OTA
数据库迁移脚本
配置备份
数据库备份
文件备份
备份保留策略
备份完整性校验
恢复演练
一键回滚
回滚后验证
灾备切换
RPO/RTO记录
""")),
    ModuleSpec("交付运维", "服务与工单", "客户支持知识闭环", "客服/实施/研发/客户管理员", "工单平台/知识库/移动端", "问题、影响、日志、截图、责任人、SLA", "问题分级、转派和关闭有证据，知识可复用", "已解决工单与知识条目", "SLA、解决方案、验证和复盘完整", "降低重复支持成本", "P1", "服务能力", "SRC-003", lines("""
客户工单
问题分级
影响范围
SLA计时
工单转派
远程协助
问题解决记录
客户确认
工单关闭
常见问题知识库
故障案例库
产品反馈
需求池关联
服务满意度
""")),

    ModuleSpec("智能硬件", "前厅设备", "身份结算取餐展示", "食客/收银员/设备管理员", "设备本体/物联平台", "身份、菜品、重量、价格、订单、网络", "设备型号、固件、协议和业务能力有明确兼容矩阵", "稳定终端与设备事件", "连续交易、断网、断电恢复和外设异常均通过", "支撑软硬一体场景", "P0", "硬件能力", "SRC-001;SRC-005", lines("""
门禁闸机
身份绑盘机
智能结算秤
智能结算台
图像结算台
智能备餐台
RFID识别设备
AI菜品识别终端
消费机
收银机
点餐机
接单宝
取餐柜
智能售卖柜
菜品发布大屏
叫号大屏
电子菜牌
扫码枪
小票打印机
人脸识别摄像头
掌纹识别终端
""")),
    ModuleSpec("智能硬件", "后厨设备", "验收食安环境烹饪", "后厨人员/食安员/设备管理员", "设备本体/物联平台", "称重、检测、视频、传感、温控、能耗", "设备唯一编号、在线状态、校准与维保可追溯", "设备数据与告警事件", "设备数据能关联项目、点位、任务与处置", "提高后厨自动化和安全性", "P0", "硬件能力", "SRC-001", lines("""
验收电子秤
留样柜
留样仪
农残检测仪
晨检仪
AI智能识别摄像头
动火离人摄像头
NVR
AI分析盒子
明厨亮灶大屏
消毒灯
挡鼠板传感器
温度传感器
湿度传感器
烟雾传感器
水表
电表
燃气传感器
洗消设备
蒸烤箱
智能烹饪设备
消毒柜
冷库温度设备
厨余秤
""")),
    ModuleSpec("智能硬件", "健康设备", "体检穿戴与营养终端", "食客/健康管理员/营养师", "健康小屋/穿戴设备/小程序", "生理指标、运动、睡眠、身份", "设备数据需本人授权、单位统一、异常有边界提示", "个人健康指标与设备状态", "设备绑定、采集、上传、去重、解绑与删除可验证", "构建康比特营养健康差异化", "P1", "差异能力", "SRC-003", lines("""
体脂秤
身高体重仪
血压计
血糖仪
血氧仪
尿酸检测仪
血脂检测仪
骨密度仪
心电设备
运动手环
运动手表
智能戒指
健康小屋一体机
营养结算终端
""")),
    ModuleSpec("智能硬件", "设备平台", "设备全生命周期", "设备管理员/运维/实施", "设备管理平台", "型号、序列号、固件、点位、网络、故障", "一机一档、变更留痕、远程操作有权限和审计", "设备台账、在线状态和维护记录", "从到货、安装、运行、维修到报废全链可追溯", "降低设备运维成本", "P0", "物联能力", "SRC-001;SRC-003", lines("""
设备管理
设备型号
设备序列号
设备点位
设备绑定与解绑
设备在线状态
设备网络配置
设备参数下发
设备远程重启
设备日志上传
固件版本
固件OTA
设备校准
设备维保
备件管理
故障工单
设备更换
设备报废
设备兼容矩阵
""")),
]


CATALOG_FIELDS = [
    "capability_id", "业务域", "一级模块", "二级模块", "功能点", "功能定义",
    "主要用户角色", "使用端/触点", "关键输入", "核心业务规则", "输出结果",
    "验收观察点", "客户价值", "标准版优先级", "能力性质", "候选来源", "证据等级", "证据状态", "备注",
]


def build_catalog() -> list[dict[str, str]]:
    counters = {domain: 0 for domain in DOMAIN_CODES}
    records: list[dict[str, str]] = []
    seen: set[tuple[str, str, str, str]] = set()
    for module in MODULES:
        for feature in module.features:
            key = (module.domain, module.level1, module.level2, feature)
            if key in seen:
                raise ValueError(f"Duplicate capability key: {key}")
            seen.add(key)
            counters[module.domain] += 1
            capability_id = f"{DOMAIN_CODES[module.domain]}-{counters[module.domain]:03d}"
            records.append({
                "capability_id": capability_id,
                "业务域": module.domain,
                "一级模块": module.level1,
                "二级模块": module.level2,
                "功能点": feature,
                "功能定义": f"在{module.level2}范围内支持“{feature}”的配置、执行、查询与留痕。",
                "主要用户角色": module.role,
                "使用端/触点": module.touchpoint,
                "关键输入": module.input_data,
                "核心业务规则": module.business_rule,
                "输出结果": module.output,
                "验收观察点": module.acceptance,
                "客户价值": module.value,
                "标准版优先级": module.priority,
                "能力性质": module.nature,
                "候选来源": module.source_ids,
                "证据等级": "B/D",
                "证据状态": "功能候选已定义；厂商状态逐项补证",
                "备注": "P0/P1/P2 为标准产品建议优先级，不代表当前已完成状态。",
            })
    return records


def normalized(value: str) -> str:
    value = value.replace("RFID ", "RFID").replace("AI识别 ", "AI识别")
    return re.sub(r"[^0-9A-Za-z\u4e00-\u9fff]+", "", value).lower()


ALIASES = {
    ("前厅", "预定线上订餐"): "线上订餐",
    ("前厅", "预定个人报餐"): "个人报餐",
    ("前厅", "预定扫码预定"): "扫码预定",
    ("前厅", "预定集体订餐"): "集体订餐",
    ("前厅", "预定包厢预定"): "包厢预定",
    ("前厅", "预定年节福利"): "年节福利预定",
    ("前厅", "支付余额支付"): "余额支付",
    ("前厅", "支付第三方支付"): "聚合码支付",
    ("前厅", "小碗菜rfid"): "小碗菜（RFID）",
    ("前厅", "小碗菜ai识别"): "小碗菜（AI识别）",
    ("营养健康", "餐后报告"): "日营养报告",
    ("营养健康", "方案建议"): "营养方案",
    ("营养健康", "营养分析"): "营养达标率",
    ("营养健康", "报告"): "体检报告导入",
    ("后厨", "货品报价"): "供应商报价",
    ("后厨", "订单管理"): "采购订单",
    ("后厨", "配送管理"): "配送计划",
    ("食安", "人员管理"): "从业人员档案",
    ("食安", "供应商管理"): "供应商食安档案",
    ("食安", "预警管理"): "预警中心",
    ("食安", "台账监管"): "食安管理汇总",
    ("食安", "明厨亮灶"): "互联网+明厨亮灶",
    ("食安", "日常运营管理"): "日常自检",
    ("前厅", "智能排菜"): ("后厨", "智能排菜"),
    ("前厅", "餐厅架构"): ("经营管理", "餐厅架构"),
    ("前厅", "营收管理账户"): ("经营管理", "营收管理（账户）"),
    ("前厅", "订单管理"): ("经营管理", "订单管理"),
    ("前厅", "统计报表"): ("经营管理", "统计报表"),
    ("前厅", "设备管理"): ("智能硬件", "设备管理"),
    ("前厅", "营养报告"): ("营养健康", "日营养报告"),
    ("前厅", "结算充值"): "线上充值",
    ("前厅", "消费订单"): ("经营管理", "消费订单"),
    ("前厅", "国产化适配"): ("平台与集成", "信创环境适配"),
    ("前厅", "离线管理"): ("平台与集成", "离线记账"),
    ("前厅", "方案建议"): ("营养健康", "营养方案"),
    ("前厅", "档案建立"): ("营养健康", "档案建立"),
    ("前厅", "膳食分析"): ("营养健康", "营养达标率"),
    ("前厅", "自助体检"): ("营养健康", "微体检"),
    ("前厅", "烹饪系统"): ("后厨", "智能烹饪设备下发"),
    ("前厅", "收餐清洁"): ("后厨", "收餐清洁"),
    ("食安", "废弃物管理"): ("后厨", "废弃物管理"),
    ("食安", "出入库管理"): ("后厨", "出入库管理"),
    ("食安", "预警提醒"): "移动预警提醒",
    ("食安", "陪餐管理"): ("前厅", "陪餐管理"),
    ("食安", "食材清洗管理"): ("后厨", "清洁任务"),
    ("食安", "餐具管理"): ("后厨", "餐具清洗"),
    ("后厨", "采购管理"): "采购申请",
}


def find_capability(catalog: list[dict[str, str]], domain: str, feature: str) -> str:
    target = normalized(feature)
    alias = ALIASES.get((domain, target))
    if alias:
        if isinstance(alias, tuple):
            domain, alias = alias
        target = normalized(alias)
    candidates = [
        row for row in catalog
        if row["业务域"] == domain and normalized(row["功能点"]) == target
    ]
    return candidates[0]["capability_id"] if len(candidates) == 1 else ""


def normalize_status(raw: str) -> str:
    raw = raw.strip()
    if "√" in raw or "✓" in raw:
        return "有"
    try:
        number = float(raw)
    except ValueError:
        return ""
    if 0 < number < 1:
        return "部分具备"
    return ""


def read_csv(path: Path) -> list[list[str]]:
    with path.open(encoding="utf-8-sig", newline="") as handle:
        return list(csv.reader(handle))


def legacy_evidence(catalog: list[dict[str, str]]) -> list[dict[str, str]]:
    specs = [
        ("SRC-001", "前厅", EXTRACT / "03-202511更新-竞对公司软件功能比对总览-前厅.csv", 1),
        ("SRC-001", "食安", EXTRACT / "04-202512更新-竞对公司软件功能比对总览-食安.csv", 1),
        ("SRC-001", "后厨", EXTRACT / "05-202512更新-竞对公司软件功能比对总览-进销存.csv", 1),
        ("SRC-001", "营养健康", EXTRACT / "11-营养功能矩阵.csv", 1),
    ]
    result: list[dict[str, str]] = []
    seq = 0
    for source_id, domain, path, feature_col in specs:
        rows = read_csv(path)
        header_index = next(
            (i for i, row in enumerate(rows[:8]) if "康比特" in row and ("功能模块" in row or "软件功能矩阵分析" in row)),
            None,
        )
        if header_index is None:
            continue
        header = rows[header_index]
        vendor_columns = {
            idx: vendor for idx, vendor in enumerate(header)
            if vendor in {"康比特", "满客宝", "南京小牛"}
        }
        for excel_row, row in enumerate(rows[header_index + 1 :], start=header_index + 2):
            if feature_col >= len(row):
                continue
            feature = row[feature_col].strip()
            if not feature or feature.startswith("竞对分析结论") or feature in {"重点功能点", "已完成功能点", "待完成功能点"}:
                continue
            if feature.isdigit() or (row and row[0].strip() == "场景"):
                continue
            capability_id = find_capability(catalog, domain, feature)
            for col, vendor in vendor_columns.items():
                raw = row[col].strip() if col < len(row) else ""
                status = normalize_status(raw)
                if not status:
                    continue
                seq += 1
                result.append({
                    "evidence_id": f"EV-{seq:05d}",
                    "capability_id": capability_id,
                    "业务域": domain,
                    "源功能点": feature,
                    "厂商": vendor,
                    "状态": status,
                    "原始值": raw,
                    "证据等级": "B",
                    "source_id": source_id,
                    "source_file": path.name,
                    "source_row": str(excel_row),
                    "source_date": "2025-11/12",
                    "freshness": "stale",
                    "not_proven": "仅证明原表在该日期记录了此状态；不自动证明2026当前版本、生产验收或空白为暂无。",
                })
    return result


PUBLIC_FEATURES = {
    "满客宝": ["AI视觉菜品识别", "按克计费", "食材溯源", "个性化配餐", "供应商管理", "经营驾驶舱"],
    "南京小牛": ["小碗菜（RFID）", "AI视觉菜品识别", "按克计费", "线上订餐", "食安管理汇总", "采购管理"],
    "银豹收银": ["AI视觉菜品识别", "人脸支付", "补贴账户", "食材溯源", "库存管理", "多食堂管理"],
    "收钱吧": ["实体卡支付", "二维码支付", "掌纹支付", "人脸支付", "排队叫号", "取餐柜自提", "无人车配送接口", "补贴账户"],
    "禧云国际": ["校园缴费", "家校共建K12", "补贴账户", "余额支付"],
    "利楚商服（扫呗）": ["微信支付", "支付宝支付", "银联支付", "聚合码支付"],
    "米果智能": ["小碗菜（RFID）", "AI视觉菜品识别", "按克计费"],
    "孚谷智慧食堂": ["线上订餐", "自助选餐", "个性化配餐", "供应商管理", "库存管理", "食材溯源"],
    "点单星": ["多食堂管理", "优惠券管理", "员工商城", "补贴账户", "零售管理"],
    "壹当伍": ["学校独立监管", "教委监管多校", "食安管理汇总"],
}


def public_evidence(catalog: list[dict[str, str]], start: int) -> list[dict[str, str]]:
    result: list[dict[str, str]] = []
    seq = start
    for vendor, features in PUBLIC_FEATURES.items():
        for feature in features:
            matches = [row for row in catalog if normalized(row["功能点"]) == normalized(feature)]
            if not matches:
                continue
            seq += 1
            row = matches[0]
            result.append({
                "evidence_id": f"EV-{seq:05d}",
                "capability_id": row["capability_id"],
                "业务域": row["业务域"],
                "源功能点": feature,
                "厂商": vendor,
                "状态": "有",
                "原始值": "公开汇编明确列出",
                "证据等级": "C",
                "source_id": "SRC-002",
                "source_file": "团餐收银解决方案竞品分析报告.docx",
                "source_row": "n/a",
                "source_date": "2026-08-10",
                "freshness": "unknown",
                "not_proven": "公开汇编未逐项交叉验证；仅作为待复核线索，不证明当前版本、深度或验收质量。",
            })
    return result


def write_csv(path: Path, rows: list[dict[str, str]], fields: list[str]) -> None:
    with path.open("w", encoding="utf-8-sig", newline="") as handle:
        writer = csv.DictWriter(handle, fieldnames=fields)
        writer.writeheader()
        writer.writerows(rows)


def main() -> None:
    catalog = build_catalog()
    legacy = legacy_evidence(catalog)
    evidence = legacy + public_evidence(catalog, len(legacy))

    strength = {"A": 4, "B": 3, "C": 2, "D": 1}
    status_by_key: dict[tuple[str, str], dict[str, str]] = {}
    evidence_ids: dict[str, list[str]] = {}
    for item in evidence:
        if not item["capability_id"] or item["厂商"] not in COMPETITORS:
            continue
        key = (item["capability_id"], item["厂商"])
        existing = status_by_key.get(key)
        if existing is None or strength[item["证据等级"]] > strength[existing["证据等级"]]:
            status_by_key[key] = item
        evidence_ids.setdefault(item["capability_id"], []).append(item["evidence_id"])

    matrix: list[dict[str, str]] = []
    for row in catalog:
        record = {
            "capability_id": row["capability_id"],
            "业务域": row["业务域"],
            "一级模块": row["一级模块"],
            "二级模块": row["二级模块"],
            "功能点": row["功能点"],
            "标准版优先级": row["标准版优先级"],
            "能力性质": row["能力性质"],
        }
        for vendor in COMPETITORS:
            item = status_by_key.get((row["capability_id"], vendor))
            record[vendor] = item["状态"] if item else "待核实"
        record["证据ID"] = ";".join(evidence_ids.get(row["capability_id"], []))
        matrix.append(record)

    backlog: list[dict[str, str]] = []
    for row in matrix:
        for vendor in COMPETITORS:
            if row[vendor] != "待核实":
                continue
            backlog.append({
                "capability_id": row["capability_id"],
                "业务域": row["业务域"],
                "一级模块": row["一级模块"],
                "二级模块": row["二级模块"],
                "功能点": row["功能点"],
                "厂商": vendor,
                "当前状态": "待核实",
                "建议证据": "官网/产品手册/演示截图/招标参数/真实系统/合同验收中至少一项",
                "通过条件": "能证明功能存在、适用版本、使用端、关键业务规则和可观察结果",
                "优先级": row["标准版优先级"],
            })

    write_csv(ROOT / "capability-catalog.csv", catalog, CATALOG_FIELDS)
    matrix_fields = ["capability_id", "业务域", "一级模块", "二级模块", "功能点", "标准版优先级", "能力性质", *COMPETITORS, "证据ID"]
    write_csv(ROOT / "competitor-matrix.csv", matrix, matrix_fields)
    evidence_fields = [
        "evidence_id", "capability_id", "业务域", "源功能点", "厂商", "状态", "原始值", "证据等级",
        "source_id", "source_file", "source_row", "source_date", "freshness", "not_proven",
    ]
    write_csv(ROOT / "evidence-ledger.csv", evidence, evidence_fields)
    backlog_fields = ["capability_id", "业务域", "一级模块", "二级模块", "功能点", "厂商", "当前状态", "建议证据", "通过条件", "优先级"]
    write_csv(ROOT / "validation-backlog.csv", backlog, backlog_fields)

    mapped = sum(1 for item in evidence if item["capability_id"])
    print(f"catalog_rows={len(catalog)}")
    print(f"matrix_rows={len(matrix)}")
    print(f"evidence_rows={len(evidence)} mapped={mapped} unmapped={len(evidence)-mapped}")
    print(f"validation_backlog_rows={len(backlog)}")


if __name__ == "__main__":
    main()
