from pathlib import Path
import json,csv
p=Path(__file__).parent
scenes=[('online_dine','线上点餐','堂食订单打印'),('online_pickup','线上点餐','自提订单打印'),('online_locker','线上点餐','取餐柜订单打印'),('offline_menu','线下消费','菜单消费打印'),('offline_fixed','线下消费','定额消费打印'),('offline_custom','线下消费','自定义消费打印'),('manual','补打策略','人工补打'),('auto','补打策略','失败自动补打')]
ucs=[
 dict(id='UC-STALL-PRINT-SWITCH-001',name='维护档口的分场景打印策略',actor='具备目标档口打印策略修改权限的管理员/商家',trigger='需要保留线上打印，关闭线下菜单消费打印，或调整其他独立开关',pre=['已登录，账号有效，拥有目标档口查看及打印策略修改权限。','已选择有效授权档口并读取档口策略版本；绑定打印机为只读执行上下文，不是策略主键。未关联可维护策略，但不得实际投递；多关联沿用原系统阻断。','已有配置及版本成功读取；只展示该档口已启用且能可靠分类的消费场景。','原业务模板、张数及订单规则保留；演练中的初始开关仅为样例，不充当正式迁移默认值。'],success=['仅本次修改的场景/补打开关持久化，新版本和最后修改人/时间可读。','配置和成功操作审计一致提交；另一端重新读取/刷新后值一致。','尚未提交的打印按有效策略重新判定；已受理任务不声称可撤回。'],failure=['旧有效配置仍生效；未保存草稿不影响实际打印。','错误留在表单内，保留草稿并给出重试或刷新入口；无成功提示。','越权、失效档口不修改数据；审计失败不提交配置。'],main=[
 ('M1','餐厅管理→餐厅设置→档口设置，按所属餐厅和档口名称搜索','P01','保留现有档口名称、所属餐厅、四餐营业时间、负责人、位置、状态、编辑操作','loaded','AC01'),
 ('M2','点目标档口“编辑”，进入原有编辑档口整页，在原表单后查看新增“打印设置”分组','P02','以档口ID加载策略/版本/修改人；已关联打印机只读展示，设备绑定/模板/张数仍在设备管理维护','loaded','AC02'),
 ('M3','将“线下消费→菜单消费打印”关闭，其余开关保持','P02','蓝色开关变灰且文字“已关闭”；“有未保存修改”出现，底部保存可用','dirty','AC03'),
 ('M4','点击“保存”，表单进入保存中','P02','保存按钮显示“保存中…”，开关/取消临时禁用，阻止重复请求','saving','AC04'),
 ('M5','保存成功','P02','绿色就地提示“已保存”，显示新版本；追加含前后值的成功操作日志','saved','AC05'),
 ('M6','从商家移动端 我的→档口设置→目标授权档口→打印设置，点击刷新','M02','移动端显示同一档口的菜单消费已关闭，线上各项值保持；日志可追溯','synced','AC06')],alt=[
 ('A1@M3','需要恢复打印','开启对应场景并保存；只恢复后续合法打印，不集中补打历史抑制订单','P02/M02','saved','AC07'),
 ('A2@M3','编辑后点取消/关闭/返回','弹出“存在未保存修改，是否放弃？”；继续编辑返回草稿，放弃丢弃草稿不落库','P02/M02','discard-confirm','AC08'),
 ('A3@M2','只读角色查看','展示当前值、明确“无修改权限”；所有开关和保存禁用，日志仍按权限可查','P02/M02','readonly','AC09')],errors=[
 ('E1@M2/R1','配置读取失败','显示加载失败及“重新加载”，隐藏可编辑表单；恢复后重新读取','P02/M02','load-error','AC10'),
 ('E2@M4/R2','保存失败或超时','保留草稿和旧有效状态；就地红色错误、“重试保存”；超时先回读结果，不盲目重复','P02/M02','save-error','AC11'),
 ('E3@M4/R3','其他人已修改，版本冲突','提示“配置已被他人修改，请重新加载后再修改”；不覆盖新版本；确认重新加载会丢草稿','P02/M02','conflict','AC12'),
 ('E4@M4/R4','配置审计写入失败/档口权限撤销/档口失效','审计失败不保存；档口权限/状态失效拒绝并保留原值，打印机解绑不删除档口策略','P02/M02','audit-error/forbidden','AC13')]),
 dict(id='UC-STALL-PRINT-SWITCH-002',name='人工补打和失败自动补打，追踪每次请求结果',actor='有补打权限的管理员/商家；自动任务以系统身份执行',trigger='某个已支付订单需要补打，或系统处理明确失败的打印任务',pre=['订单属于账号授权档口，订单满足原有可打印状态，无重复支付或重建订单。','按订单所属档口自动匹配唯一有效打印机；不允许随意切换到其他档口或新增多机关系。','原业务场景允许打印且人工/自动对应开关开启；人工还需补打权限。','目标可用；上次结果未知或已受理未结时先查询，不盲目再次投递。'],success=['产生可追溯的补打请求及逐目标尝试，记录原因、份数、次数、操作者/系统、配置版本。','云受理号与状态可查；明确区分已受理、打印成功、失败、结果待确认。','订单金额、支付、模板和既有打印张数配置不被本需求改变。'],failure=['被禁止或校验不通过时不发送打印，不增加实际投递次数；记录拒绝/跳过原因。','相同请求重试复用原结果；无法判定受理结果时停在待确认。','未保存补打请求日志前不对外投递，失败可定位且不得静默继续。'],main=[
 ('M1','订单管理→消费订单→查看目标订单','P03/M03','订单详情显示所属档口、场景、唯一匹配打印机、现有份数和上次打印结果','detail','AC14'),
 ('M2','点击“补打小票”','P04/M04','弹出补打确认框/移动底部面板，打印机及份数只读；选择补打原因','confirm','AC15'),
 ('M3','选择“缺纸恢复/票据遗失/其他”，其他时填写说明','P04/M04','必填原因；缺失时表单内红字，不能提交','valid/invalid','AC16'),
 ('M4','点击“确认补打”','P04/M04','请求处理中按钮及关闭动作禁用；生成幂等请求和待提交日志，校验最新策略','submitting','AC17'),
 ('M5','云服务接受请求','P05/M05','提示“已受理，等待打印结果”；补打记录新增行，显示人工、原因、份数、尝试次数','accepted','AC18'),
 ('M6','回查服务结果或刷新记录','P05/M05','有明确结果才变为打印成功/打印失败；不能回查则显示结果待确认','success/failed/unknown','AC19')],alt=[
 ('A1@M2','用户取消','关闭确认框回订单详情，不产生补打请求','P04/M04','cancelled','AC20'),
 ('A2@M1','发生明确失败且自动补打开启','系统按原合法重试规则触发；同场景开关仍生效，日志身份显示系统','P05/M05','auto-attempt','AC21'),
 ('A3@M5','某目标部分成功','逐目标展示结果，仅重试明确未成功目标；当前标品唯一目标，不借此扩展多机绑定','P05/M05','partial','AC22'),
 ('A4@M1','场景或人工补打开关关闭','按钮禁用并显示具体关闭项；提供有配置权限者前往打印策略的入口，不绕过','P03/M03','blocked','AC23')],errors=[
 ('E1@M3/R1','原因未填/其他未说明','就地红字关联字段，保留输入并聚焦错误位置','P04/M04','invalid','AC16'),
 ('E2@M1/R2','未关联、多关联、离线、已解绑','显示具体原因且禁止提交；管理员修复后刷新重新匹配','P03/M03','device-error','AC24'),
 ('E3@M4/R3','重复点击、网络重试、人工和自动并发','同一请求不新增投递；处理中返回既有请求并提示查询记录','P04/P05','duplicate','AC25'),
 ('E4@M4/R4','日志持久化失败/服务明确拒绝','日志失败不发请求；服务拒绝记录失败及原因，保留可追溯请求','P04/P05','failed','AC26'),
 ('E5@M5/R5','受理超时/结果未知/超过自动重试上限','结果待确认，停止盲重投；先查询云状态，超限转人工核实，不承诺物理恰好一次','P05/M05','unknown','AC27')]),
 dict(id='UC-STALL-PRINT-SWITCH-003',name='查看开关操作日志与补打记录',actor='具备日志查看权限的管理员/商家/运营只读人员',trigger='查询谁更改了开关，或追查订单补打原因、次数和结果',pre=['已登录并有日志查看权限，查询范围限定在授权商家/食堂/档口。','从档口编辑“打印日志”或订单“补打记录”进入；保留来源上下文。','记录已经产生；没有记录时进入真实空状态而非造数据。'],success=['按筛选条件分页返回完整记录，能看变更前后值或补打各次结果及原因。','订单入口只返回该订单、档口入口继承该档口上下文；打印机ID只用于执行记录筛选；返回路径保留原上下文。','日志只读，敏感字段脱敏，不提供关闭审计/编辑/删除按钮。'],failure=['无权限不显示其他租户/档口记录；查询失败不显示伪造结果。','筛选条件保留，重试能恢复；空结果与读取失败明确区分。'],main=[
 ('M1','从档口编辑点“打印日志”或订单详情点“补打记录”','P06/P05/M06/M05','进入日志页，档口/订单自动限定；开关操作和补打记录分两个页签','loaded','AC28'),
 ('M2','设置时间、档口/打印机、操作人、结果；补打支持订单号','P06/M06','标签清楚；查询/重置按钮，日期合法性校验，不扩大权限范围','filter','AC29'),
 ('M3','点击查询并翻页','P06/M06','加载中骨架/遮罩，结果分页10条，显示总条数','loading/loaded','AC30'),
 ('M4','打开记录详情','P06/M06','开关逐项前后值及版本；补打逐次状态、原因、云受理号/时间；关闭返回筛选列表','record-detail','AC31')],alt=[
 ('A1@M2','筛选无结果','显示“暂无符合条件的记录”，提供“重置筛选”','P06/M06','empty','AC32'),
 ('A2@M1','从订单进入补打记录','只读订单号条件，查询该订单所有人工/自动尝试，按时间倒序','P05/M05','scoped','AC33'),
 ('A3@M4','点击关联订单','有订单查看权限则打开详情；无权限则说明原因且不越权跳转','P03/M03','permission','AC34')],errors=[
 ('E1@M3/R1','查询失败/网络中断','就地错误及“重试查询”；保留筛选，不将失败当空数据','P06/M06','query-error','AC35'),
 ('E2@M2/R2','结束日期早于开始日期','日期字段红字，禁止请求；修正后查询','P06/M06','invalid','AC36'),
 ('E3@M1/R3','日志查看权限撤销/越权参数','无权限页，不暴露记录数量或详情；返回授权入口','P06/M06','forbidden','AC37')]),
 dict(id='UC-STALL-PRINT-SWITCH-004',name='系统按订单来源与有效打印策略执行首次打印',actor='支付成功后既有打印触发链路及打印服务',trigger='线上点餐或线下消费产生符合原规则的首次打印事件',pre=['订单已达到现有合法打印时点，支付/订单状态以现有业务为准。','订单来源、就餐方式/消费类型能确定唯一叶子场景，不能把联网在线/离线或支付方式当业务来源。','目标档口和打印机绑定合法；有效配置可读；迁移映射已经评审。'],success=['命中允许场景且原规则可打印时按既有模板/份数投递；关闭时跳过并记策略原因。','系统运行记录可解释“是否投递及原因”；云服务受理与物理出纸明确分开。','不会影响同机其他档口、其他场景；不重复支付或创建重复订单。'],failure=['未知场景/无效绑定/配置不可用不盲投，保留原订单及可追溯异常。','失败任务进入可解释状态，只在策略允许且结果明确时重试。'],main=[
 ('M1','收到原业务合法打印事件','P03/P07','订单详情可看到来源、所属档口及首次打印状态；原型仅模拟该事件','event','AC38'),
 ('M2','确定场景和打印目标','P07','事件模拟面板展示分类结果及唯一目标；真实系统无额外弹窗','classified','AC39'),
 ('M3','提交前读取当前有效策略','P07','展示“允许/跳过”及命中开关版本；草稿不参与判断','decision','AC40'),
 ('M4','允许则调用既有打印，关闭则跳过','P05/P07','订单打印记录分别显示已受理或已跳过及原因','accepted/skipped','AC41'),
 ('M5','受理结果回查','P05/P07','明确成功/失败/待确认；失败进入UC002自动补打判定','result','AC42')],alt=[
 ('A1@M3','目标场景关闭','不投递、不伪报打印失败、不在恢复开启后追打历史；其他场景继续','P05/P07','skipped','AC43'),
 ('A2@M1','原规则本就不打印','仍不打印；开启新开关不能把不符合原规则的订单强制打印','P05/P07','skipped','AC44'),
 ('A3@M3','保存时已有排队任务','尚未受理的按最新有效配置重新判断；已经受理提示无法保证撤回','P05/P07','in-flight','AC45')],errors=[
 ('E1@M2/R1','来源未知或分类重叠','进入分类待核实；不随意归到线上/线下；保留订单及原因','P05/P07','unknown-scene','AC46'),
 ('E2@M3/R2','配置读取失败/绑定无效','不盲投；记录失败，恢复后重新读取最新配置再决定是否发送','P05/P07','config-error','AC47'),
 ('E3@M4/R3','云服务拒绝或受理结果不明','明确失败可按UC002处理；未知先回查，不能用成功请求码代替出纸证明','P05/P07','failed/unknown','AC48')])]
ucs[0]['alt'] += [
 ('A4@M2','同一档口更换打印机','档口策略及版本保持；仅执行设备改变，日志保留历史目标','P02/P03','binding-replaced','AC49'),
 ('A5@M2','打印机改绑另一档口','读取新档口策略，不携带原档口开关；订单按自身档口重新匹配','P01/P02/P03','binding-moved','AC50'),
 ('A6@M2','档口未关联打印机','可预配置并保存档口开关；提示实际打印/补打仍因无设备而阻断','P02/M02/P03','unbound-configurable','AC51'),
 ('A7@M1','从设备管理查看绑定档口','设备页没有独立策略编辑，仅定位当前绑定档口的编辑页','P08/P02','stall-context','AC52')]

# stable machine-readable source for documentation and traceability
(p/'use-cases.json').write_text(json.dumps({'version':'0.5','scenes':scenes,'useCases':ucs},ensure_ascii=False,indent=2))
head='''# VWCG-1414｜按业务场景控制小票打印：完整 Use Case 与原型规格 V0.5

2026-10-09｜负责人赖清涛｜Review / DoR: BLOCKED（原系统识别与上线契约待评审，见末章）

## 目标与范围

解决线上点餐、线下菜单消费共用打印机混打的问题。标品 PC 和商家移动端以档口ID作为策略归属，按“档口＋业务场景”独立开关；打印机仅是已绑定执行设备；人工/自动补打独立控制；配置操作及每次补打可查询日志。默认保持旧业务行为，不改订单、金额、支付、模板或既有打印份数，不增加打印机绑定关系。

最新用户明确要求完整 Use Case 直接可见，图片不能替代前置/后置/主/分支/异常流程。V0.5正文为当前权威版本；V0.1—V0.4附件仅历史参考。

## 现有系统依据与证据边界

PC档口入口：`store/public/static/src/view/restaurantManagement/restaurantSetting/stallSet.vue`及`addStallRules.vue`。设备绑定：`store/public/static/src/view/system/device/deviceManagement.vue`、`device/index.vue`、`router/system/index.js`、`assets/common.css`、`view/home.vue`、订单列表/详情源码与归档真实截图。实体仓库HEAD `301815bd695483a5709d2fac4d6b7f29e1334b49`；本地旧分支，不能冒充最新生产发布。复用已安装Vue 2.7.16、Element UI 2.15.14及图标字体、官方Logo；不改业务仓库。

商家移动端：沿用已评审商户移动管理端的“我的→设备、订单→详情→补打”、蓝色、卡片、字体和44px触控规范。该来源是已有原型，不是本次新获取的生产截图。本机业务8080/CDP9222无监听；当前真实部署样式和来源枚举需清涛对照发布版本评审。原型使用合成数据，仅浏览器本地演练，不调用业务/支付/云打印接口。

## 角色与权限

| 角色 | 查配置 | 改策略 | 人工补打 | 查日志 | 数据范围 |
| --- | --- | --- | --- | --- | --- |
| 食堂管理员 | 授权 | 单独授权 | 单独授权 | 单独授权 | 授权食堂/档口 |
| 档口商家 | 本档口 | 单独授权 | 单独授权 | 单独授权 | 本人授权档口 |
| 运营只读 | 授权 | 无 | 无 | 单独授权 | 授权范围 |
| 系统重试 | 读策略 | 无 | 仅自动规则 | 写自身结果 | 原任务目标 |

前后端同样校验。原型角色切换仅演练，不是正式权限认证；正式角色ID映射待评审。

## 独立开关

| Key（拟议字段，非既有接口事实） | 分组 | 控件文案 |
| --- | --- | --- |
'''
head+='\n'.join(f'| `{key}` | {group} | {label} |' for key,group,label in scenes)
head+='''

仅显示已启用且可以可靠识别的业务。称重、AI识别、点餐机等其他场景须证明分类互斥后按同一结构扩展，不凭设备名称或支付方式猜来源。人工补打需同时满足场景开关、人工补打开关、权限；自动补打需同时满足场景开关、自动开关和原合法重试规则。日志不能被关闭。

## 页面地图与原系统落位

| ID | 原系统入口/新增位置 | 关键控件与业务体现 |
| --- | --- | --- |
| P01 | 餐厅管理→餐厅设置→档口设置 | 保留所属餐厅/档口名称筛选、营业时间/负责人/位置/状态及编辑操作 |
| P02 | 现有编辑档口整页addStallRules | 保留原表单、顶部保存；在原业务规则后新增“打印设置”分组、档口策略版本与日志入口，打印机只读 |
| P08 | 系统管理→设备管理（只读上下文） | 不另存策略；查看绑定档口跳转P02，执行日志可按设备查询 |
| P03 | 订单管理→消费订单→查看 | 保留订单/菜品/支付信息；增加打印概况、补打小票、补打记录 |
| P04 | 订单详情内补打确认弹窗520px | 订单/匹配打印机/份数只读，原因必填，取消/确认补打，表单内错误 |
| P05 | 订单详情补打记录 | 逐请求/尝试时间、人工/自动、原因、份数/次数、状态；可回查结果 |
| P06 | 档口设置内打印设置→操作/打印日志 | 开关操作/补打记录分签，范围/时间/操作人/结果/订单号筛选，查询/重置，分页10条，详情只读 |
| P07 | 仅原型审查工具：事件模拟 | 对应UC004系统事件；生产不新增业务弹窗。可演练有效策略、已受理/跳过/失败/待确认 |
| M01 | 商家移动端 我的→档口设置 | 新增档口设置入口；按授权档口进入编辑与打印设置，原设备状态入口保留 |
| M02 | 档口→打印设置整页 | 分组开关，每行≥56px、点击热区≥44px，底部安全区取消/保存，返回时未保存确认 |
| M03/M04 | 订单→详情→补打 | 详情卡片、底部补打确认面板，唯一打印机只读，原因与错误在同面板 |
| M05/M06 | 订单补打记录/打印机日志 | 卡片化日志，筛选底部面板/原生控件；无PC宽表挤到手机，支持返回来源 |

原型 `prototype.html` 可切PC/移动端、角色、故障条件；每个按钮的演练和状态都能按下述Use Case定位，审查工具明确与生产页面分开。

'''
text=head
trace=[]
for u in ucs:
 text+=f'## {u["id"]}｜{u["name"]}\n\n- Use Case ID：{u["id"]}\n- 版本/状态：V0.5 / Review；DoR: BLOCKED（末章明确负责人）。\n- 主要角色：{u["actor"]}。\n- 触发：{u["trigger"]}。\n\n### 前置条件\n\n'
 text+='\n'.join(f'{i}. {v}' for i,v in enumerate(u['pre'],1))+'\n\n### 后置条件\n\n成功：\n\n'
 text+='\n'.join(f'- {v}' for v in u['success'])+'\n\n失败/取消：\n\n'
 text+='\n'.join(f'- {v}' for v in u['failure'])+'\n\n### 主流程\n\n| 步骤 | 用户/系统动作 | 页面/控件 | 系统响应与数据变化 | 状态 | 验收 |\n| --- | --- | --- | --- | --- | --- |\n'
 for step,action,page,result,state,ac in u['main']:
  text+=f'| {step} | {action} | {page} | {result} | {state} | {ac} |\n';trace.append([u['id'],step,page,state,ac,'原型/自动化+上线系统回归',result])
 text+='\n### 分支流程\n\n| 分支/分支点 | 条件 | 行为与返回/结果 | 页面 | 状态 | 验收 |\n| --- | --- | --- | --- | --- | --- |\n'
 for step,condition,result,page,state,ac in u['alt']:
  text+=f'| {step} | {condition} | {result} | {page} | {state} | {ac} |\n';trace.append([u['id'],step,page,state,ac,'原型/自动化+上线系统回归',result])
 text+='\n### 异常/恢复流程\n\n| 异常/恢复 | 条件 | 用户提示、数据处理及恢复 | 页面 | 状态 | 验收 |\n| --- | --- | --- | --- | --- | --- |\n'
 for step,condition,result,page,state,ac in u['errors']:
  text+=f'| {step} | {condition} | {result} | {page} | {state} | {ac} |\n';trace.append([u['id'],step,page,state,ac,'原型模拟/自动化+上线故障注入',result])
 text+='\n'
text+='''## 归属纠正与绑定变化（本次V0.5核心）

- 打印策略及版本以商家/租户＋档口ID归属，不以打印机ID或SN归属。编辑入口在现有addStallRules整页的“打印设置”；设备管理不再提供另一份可编辑策略。
- 执行链：订单.restaurant_id → 档口有效打印策略 → 按equipment.restaurant_id查当前关联打印机 → 按允许场景执行。
- 同一档口换打印机，策略/版本保持；同一打印机换绑另一档口，读取新档口策略，不携带旧档口开关。绑定变化仍走原设备管理，不在打印设置中直接改绑。
- 无打印机时允许预配置档口开关并明确“当前未绑定，不会实际打印”；实际订单/补打仍阻断未关联、多关联及无效设备。打印机异常不改变策略主键。
- 配置日志以档口记录修改对象，可附当前绑定打印机快照；补打日志记录订单所属档口与实际执行打印机。历史日志不因换绑改写。
- 新验收AC49：换机不丢策略；AC50：换绑只读新档口策略；AC51：无机可配但不能投递；AC52：设备页没有独立策略编辑入口。额外测试与映射见ownership-tests.json。

## 核心规则、状态和数据

- BR01：一个订单按来源+业务类型命中唯一场景；原规则不打印时，新开关不能强行打印。联网在线/离线和支付方式不能代替业务来源。
- BR02：开关只影响指定档口/合法打印目标/命中场景；关闭线下菜单不能误关线上点餐或其他档口。
- BR03：配置版本+审计同一事务保存。读取成功后编辑，保存前只是草稿；并发版本冲突拒绝覆盖。
- BR04：保存成功后对尚未向云服务提交的任务按新策略判定；已受理/正在打印不能承诺撤回。再次开启不集中追打历史抑制任务。
- BR05：补打不改支付，不重建订单；唯一目标由原系统绑定匹配，不开放跨档口任选打印机。被关闭场景不能被自动重试绕过。
- BR06：先持久化补打请求再投递；同请求重试幂等，人工/自动并发同订单目标防重；供应商无去重/结果查询能力时不能承诺物理打印恰好一次。
- BR07：首次打印、人工补打、自动补打分开标识；状态为待提交→处理中→已受理→明确成功/明确失败/结果待确认，关闭策略为已跳过，权限/校验不通过为已拒绝。
- BR08：配置与执行日志不允许业务用户关停/编辑/删除。日志不可用时新人工补打不得投递；失败状态保留可追踪请求，不删除历史。

### 日志字段

| 日志 | 必填字段 | 页面显示 |
| --- | --- | --- |
| 开关操作 | 事件/请求ID、操作人ID/姓名、时间、端、商家/食堂/档口/打印机、改动项、前后值、前后版本、结果/原因 | 列表显示人/时间/对象/项/前后值/结果，详情展开版本和请求号 |
| 补打 | 请求/尝试ID、订单、来源/场景、目标、人工/自动、人或系统、时间、原因、份数、真实投递次数、服务受理号、策略版本、状态/结果时间/错误 | 订单关联卡片/表格，逐次明细；拒绝/跳过不计入成功出票 |

日志不保存密码、token、打印机云密钥、小票全文或未脱敏联系方式。默认倒序、分页10条。时间展示Asia/Shanghai，日志保留/归档期由清涛评审。

### API与数据契约（能力定义，具体路径/字段待技术评审）

| 能力 | 输入/前置 | 输出/错误/一致性 |
| --- | --- | --- |
| 读策略 | 已鉴权、档口ID | 场景支持列表、当前值、版本、最后修改人/时间；无权限/无绑定/读取失败区分 |
| 存策略 | 档口ID、变更项、expectedVersion、requestId | 新版本+审计成功才成功；冲突409语义、权限403语义、持久化失败不改旧值 |
| 人工补打 | 订单、唯一匹配目标、原因、requestId、版本 | 记录ID、受理/待确认状态；幂等复用，关闭/无权限/设备异常/未填原因明确拒绝 |
| 查询日志/状态 | 授权范围、时间/场景/操作者/结果/订单、page/pageSize | records/total；结果未知不能映射成功；越权不泄露数量 |

上述状态码是拟议语义，不宣称当前接口已支持。数据库对象为按档口ID归属的策略及配置审计、补打请求、逐目标尝试；保留现有业务订单和支付状态。跨云打印调用不是可回滚数据库事务。

## 验收标准与自动化测试矩阵

每个上表AC的Given=对应前置/分支/异常条件，When=对应动作，Then=同一行可观察响应/数据结果；`traceability.csv`逐条保留映射，不用一张总截图冒充全部通过。

| 套件 | 目标 | 覆盖 | 证据 |
| --- | --- | --- | --- |
| UI-CONFIG | PC与移动端读/改/保存/取消/刷新/角色 | AC01—13 | 原型自动化、关键状态截图；正式接口与DB回读待开发 |
| UI-REPRINT | 原因必填、受理、明确结果、禁止/离线/多绑定/重复 | AC14—27 | 原型自动化+日志回读；供应商/实机验收待开发 |
| UI-LOG | 双日志、筛选/分页/上下文、空/失败/无权限 | AC28—37 | 原型自动化、详情截图；真实审计一致性待开发 |
| POLICY | 八开关组合、首次打印/人工/自动、来源分类/结果未知 | AC38—48及AC21/23 | 原型模拟决策测试；真实代码与自动任务待开发 |
| VISUAL | 桌面1440×1000、移动390×844；默认/弹窗/错误/日志 | 设计规格中的组件和状态 | 实际浏览器截图、无根水平溢出、可达控件/键盘/控制台 |

后端权限、事务、幂等、供应商状态和物理打印不能由静态原型证明。现阶段测试只证明文档/原型路径与模拟行为，发布必须补正式API/并发/权限/设备回归。

## 性能、安全、发布与待确认

- 安全：服务端鉴权和租户/档口隔离，前端隐藏不是权限控制；请求防重，日志字段最小化，无跨域生产调用。
- 性能：配置读取沿用缓存版本策略，不新增逐单跨端轮询；日志分页和组合索引；响应时间/业务峰值/日志容量的量化阈值需结合当前发布版本评审，本次不捏造已测性能结果。
- 发布：历史行为对照→迁移预演→测试档口实机→小范围灰度→两端/日志/自动任务冒烟。配置/功能回滚不抹掉已受理打印或历史审计。

| P0待确认 | 必须形成的结论 | 负责人/时点 |
| --- | --- | --- |
| 实际发布基线 | 当前PC/移动端真实版本及页面、支持场景、唯一分类条件、订单详情补打入口 | 赖清涛，开发前 |
| 迁移与生效 | 新设备默认值、旧行为映射、配置读取失败策略、已排队/已受理边界 | 赖清涛，开发前 |
| 补打授权与服务能力 | 关闭场景能否人工例外（当前方案禁止），次数/间隔、去重/回查能力 | 赖清涛组织产品/技术，开发前 |
| 正式权限/日志/性能 | 权限ID映射、日志保留/归档、容量/并发/响应阈值 | 赖清涛协调相关负责人，开发前 |

文档和原型是完整评审包，不是生产可用证明。以上P0未关闭时DoR保持BLOCKED，不开始业务实现。
'''
(p/'use-cases-v05.md').write_text(text)
with (p/'traceability.csv').open('w') as f:
 w=csv.writer(f);w.writerow(['UseCase','Step','Screen','State','AC','ValidationScope','Expected']);w.writerows(trace)
print('UseCases',len(ucs),'mapped flow rows',len(trace),'document chars',len(text))
