# Generate an Excel testcase workbook from existing e2e/tests automation flow.
$ErrorActionPreference = 'Stop'

$Root = (Resolve-Path '.').Path
$Output = Join-Path $Root 'SaaS_E2E测试用例_基于自动化脚本重构版.xlsx'

function Steps {
    param([string[]]$Items)
    $result = @()
    for ($i = 0; $i -lt $Items.Count; $i++) {
        $result += ('{0}. {1}' -f ($i + 1), $Items[$i])
    }
    return ($result -join "`n")
}

function Add-Sheet {
    param($Workbook, [string]$Name, [object[]]$Headers, [object[]]$Rows)
    $ws = $Workbook.Worksheets.Add([Type]::Missing, $Workbook.Worksheets.Item($Workbook.Worksheets.Count))
    $ws.Name = $Name
    for ($c = 0; $c -lt $Headers.Count; $c++) {
        $ws.Cells.Item(1, $c + 1).Value2 = [string]$Headers[$c]
    }
    for ($r = 0; $r -lt $Rows.Count; $r++) {
        $row = $Rows[$r]
        for ($c = 0; $c -lt $row.Count; $c++) {
            $ws.Cells.Item($r + 2, $c + 1).Value2 = [string]$row[$c]
        }
    }
    $ws.Rows.Item(1).Font.Bold = $true
    $ws.Rows.Item(1).Interior.ColorIndex = 37
    $ws.UsedRange.WrapText = $true
    $ws.UsedRange.VerticalAlignment = -4160
    $ws.Columns.AutoFit() | Out-Null
    for ($c = 1; $c -le $ws.UsedRange.Columns.Count; $c++) {
        if ($ws.Columns.Item($c).ColumnWidth -gt 58) {
            $ws.Columns.Item($c).ColumnWidth = 58
        }
    }
    return $ws
}

function Format-Sheet {
    param($Worksheet)
    $Worksheet.UsedRange.WrapText = $true
    $Worksheet.UsedRange.VerticalAlignment = -4160
    $Worksheet.Columns.AutoFit() | Out-Null
    for ($c = 1; $c -le $Worksheet.UsedRange.Columns.Count; $c++) {
        if ($Worksheet.Columns.Item($c).ColumnWidth -gt 58) {
            $Worksheet.Columns.Item($c).ColumnWidth = 58
        }
    }
}

$scriptRows = @(
    @('E2E-SAAS-001','test_e2e_saas_001_enterprise_certification.py','企业注册认证','租户注册企业；主站审核认证；租户回查认证通过','tenant.register -> admin.approve -> tenant.certification_passed_visible','企业、手机号、认证状态'),
    @('E2E-SAAS-003','test_e2e_saas_003_wanyouji_app_opening.py','丸友集应用开通','已认证商户申请丸友集；主站审批；租户端入口可用','tenant.apply_wanyouji -> admin.approve -> tenant.wanyouji_available','应用状态、入口可用'),
    @('E2E-SAAS-004','test_e2e_saas_004_wanzhanggui_app_opening.py','丸掌柜应用开通','已认证商户申请丸掌柜；主站审批；租户端入口可用','tenant.apply_wanzhanggui -> admin.approve -> tenant.wanzhanggui_available','应用状态、入口可用'),
    @('E2E-AUTH-001','test_e2e_auth_001_authorization_closed_loop.py','授权密钥闭环','丸友集生成密钥；丸掌柜激活；丸友集保存权限；丸掌柜校验创建订单权限','generate_wanyouji_key -> activate_key_for_wanzhanggui -> verify_and_save_wanyouji_permissions -> verify_wanzhanggui_authorized_permissions','授权关系、创建订单权限'),
    @('E2E-PUSH-001','test_e2e_push_001_wanzhanggui_workshop_authorization.py','工作室授权','丸掌柜添加已绑定丸友集工作室授权','add_wanyouji_workshop_authorization','工作室授权状态'),
    @('E2E-PUSH-003','test_e2e_push_003_dispatch_strategy.py','推单策略','丸掌柜新增推单配置，支持单平台指定/多工作室同时分发/多平台分发','create_dispatch_strategy','策略名称、策略类型、工作室'),
    @('E2E-ORDER-001','test_e2e_order_001_manual_order_dispatch_closed_loop.py','手工录单发单','丸掌柜手工录单创建外来订单；丸友集抢单大厅验证可见','create_manual_order -> verify_order_reached_hall','source_order_no、order_title、strategy_name、strategy_type'),
    @('E2E-ORDER-002','test_e2e_order_002_revoke_dispatch_closed_loop.py','撤销派单','丸掌柜撤销已派订单；丸友集验证抢单大厅不可见','revoke_dispatched_order -> verify_order_not_in_hall','撤销后可见性'),
    @('E2E-TASK-001','test_e2e_task_001_external_order_dispatch_to_player.py','外部订单接单并发给打手','丸友集管理员接外部订单；立即发单；打手抢单并进入我的订单','accept_external_order_from_hall -> dispatch_workshop_order_to_player_hall -> grab_order_from_player_hall_and_verify_my_order','task_accepted、task_dispatched_to_player_hall、task_player_accepted'),
    @('E2E-DELIVERY-001','test_e2e_delivery_001_player_submit_acceptance.py','打手提交验收','打手确认上号、上传首图、提交验收；主账号订单列表看到待验收/申请上游验收','submit_player_delivery_for_acceptance -> search_workshop_order_list','delivery_submitted'),
    @('E2E-DELIVERY-002','test_e2e_delivery_002_upstream_acceptance_settlement.py','上游验收结算收入','丸友集申请上游验收；丸掌柜完结主单；丸友集查看完成/结算中；打手查看收入','apply_upstream_acceptance -> complete_upstream_order -> verify_workshop_order_completed_or_settling -> verify_player_income_for_order','upstream_acceptance_applied、upstream_completed、income_verified'),
    @('E2E-MEMBER-001','test_e2e_member_001_enterprise_member_invitation.py','成员邀请','丸友集主账号邀请客服/打手；子成员短信登录；校验角色菜单权限','invite_member -> verify_member_can_login_by_sms -> verify_wanyouji_role_menu_permissions','客服、打手、子账号权限')
)

$caseHeaders = @('用例编号','优先级','来源脚本/依据','所属流程','用例标题','前置条件','测试数据','操作步骤','预期结果','跨端/数据断言','分支来源','自动化状态')

$mainRows = @(
    @('TC-MAIN-001','P0','test_e2e_main_001_full_closed_loop.py','注册-开通-授权-配置-发单主流程','从企业注册到手工录单发单的自动化主流程',
        '测试手机号可用；主站账号可登录；丸友集/丸掌柜产品可申请；运行台账可写入。',
        '企业名称自动生成；应用：丸友集、丸掌柜；策略类型默认单平台指定；订单来源默认手工录单。',
        (Steps @(
            '租户平台使用测试手机号注册企业并提交认证资料。',
            'SaaS主站使用管理员账号登录，进入企业认证审核并通过该企业。',
            '租户平台重新登录企业账号，确认认证状态已通过。',
            '租户平台申请开通丸友集应用。',
            'SaaS主站审批丸友集应用开通申请。',
            '租户平台刷新订阅/应用页，确认丸友集入口可进入。',
            '租户平台申请开通丸掌柜应用。',
            'SaaS主站审批丸掌柜应用开通申请。',
            '租户平台刷新订阅/应用页，确认丸掌柜入口可进入。',
            '丸友集主账号生成授权密钥。',
            '丸掌柜主账号激活该授权密钥。',
            '丸友集主账号回到授权管理，保存授权权限，至少校验丸掌柜具备创建订单权限。',
            '丸掌柜新增丸友集工作室授权。',
            '丸掌柜新增推单配置策略，并记录策略名称、策略类型、绑定工作室。',
            '丸掌柜手工录单创建订单并选择该策略。',
            '丸友集对应账号进入抢单大厅验证订单可见。'
        )),
        '企业认证成功；两个应用均开通；授权关系可用；工作室授权成功；推单策略可用；手工录单发单后丸友集抢单大厅可见订单。',
        '台账中企业状态、应用状态、授权密钥、工作室授权、推单策略、订单号均可追踪；丸掌柜订单与丸友集大厅订单的source_order_no/order_title/strategy_name一致。',
        '自动化脚本主流程整理','已自动化'
    ),
    @('TC-MAIN-002','P0','test_e2e_order_001 + test_e2e_task_001 + test_e2e_delivery_001/002','派单-打手履约-上游验收-收入主流程','外来订单从丸掌柜发到丸友集，再分发给打手并结算收入',
        '已存在丸掌柜已派单订单；丸友集账号和打手账号可登录；订单状态在台账中为dispatched或后续可继续状态。',
        'source_order_no、order_title、strategy_type、wanyouji_account、player_account来自runtime/e2e_accounts.json台账。',
        (Steps @(
            '丸友集企业管理员登录。',
            '进入工作室履约应用-抢单大厅。',
            '按source_order_no或order_title搜索丸掌柜派出的外部订单。',
            '根据策略类型确认订单出现在私单或公池单/私单范围。',
            '管理员点击立即接单，订单从抢单大厅进入工作室订单列表。',
            '管理员进入工作室订单列表，找到该外部订单。',
            '点击立即发单。',
            '选择或确认分配方式，默认脚本参数支持抢单大厅、指派打手、智能派单。',
            '填写/确认发单价、服务时限、订单标题等页面必填项并提交。',
            '打手子账号短信登录。',
            '进入打手应用-抢单大厅。',
            '按source_order_no或order_title搜索订单。',
            '打手点击立即抢单。',
            '进入打手应用-我的订单，确认订单出现。',
            '打手在我的订单中确认上号、上传首图或成果材料，并提交验收。',
            '丸友集主账号进入工作室订单列表，确认订单出现待验收/申请上游验收状态。',
            '丸友集主账号点击申请上游验收。',
            '丸掌柜主账号登录并进入订单管理/验收入口。',
            '搜索同一source_order_no或order_title，执行完结主单/验收通过。',
            '丸友集主账号回查工作室订单状态为已完成或结算中。',
            '打手子账号进入我的收入，校验该订单产生收入记录。'
        )),
        '订单依次进入已接外部订单、已发给打手、打手已接单、已提交验收、已申请上游验收、上游已完结、收入已验证状态。',
        '台账状态依次更新为task_accepted、task_dispatched_to_player_hall、task_player_accepted、delivery_submitted、upstream_acceptance_applied、upstream_completed、income_verified；三端订单号、标题、金额、打手信息一致。',
        '自动化脚本主流程整理','已自动化'
    )
)

$branchRows = @(
    @('TC-SOURCE-001','P0/P1','test_e2e_order_001_manual_order_dispatch_closed_loop.py + 用户补充1','订单来源分支','手工录单与抖音录单正向流程对照',
        '已完成授权、工作室授权、推单策略；手工录单已自动化；抖音录单入口和店铺授权待确认。',
        '订单来源：手工录单/抖音录单；子单数：1；策略类型：单平台指定；分配方式：抢单大厅；价格模式：固定发单价。',
        (Steps @(
            '先按脚本E2E-ORDER-001执行手工录单，生成source_order_no和order_title。',
            '确认丸掌柜订单创建成功，并在丸友集抢单大厅可见。',
            '记录手工录单的页面字段、来源标识、策略名称、订单状态。',
            '切换到丸掌柜抖音录单或抖音订单入口。',
            '创建或同步一笔抖音来源订单，记录抖音订单号。',
            '为抖音订单添加1个子单，选择同一推单策略。',
            '保存并发布抖音来源订单。',
            '丸友集企业管理员进入抢单大厅，分别用抖音订单号、丸掌柜source_order_no、标题搜索。',
            '确认抖音订单可进入与手工录单相同的接单和履约链路。',
            '将两个来源的字段差异记录到待确认项，特别是撤销入口和外部平台回传。'
        )),
        '手工录单正向链路已自动化且通过；抖音录单正向应与手工录单一致可见、可接、可发给打手。',
        '两类来源的source_order_no、外部订单号、订单来源、策略名称、价格、状态映射清楚；撤销差异不在正向用例中强行断言。',
        '用户8条-1','手工已自动化；抖音待脚本化'
    ),
    @('TC-CANCEL-001','P1','test_e2e_order_002_revoke_dispatch_closed_loop.py + 用户补充2','撤销分支','丸掌柜已派单但丸友集未接单前撤销',
        '台账存在status=dispatched的订单；丸掌柜账号可操作撤销；丸友集验证账号可登录。',
        'source_order_no、order_title、strategy_type；expected_scope=单平台指定时私单，否则公池单/私单。',
        (Steps @(
            '从台账选择最新已派单订单。',
            '丸掌柜主账号登录。',
            '进入商家管理-订单管理。',
            '按source_order_no或order_title搜索订单。',
            '进入订单详情或列表操作区。',
            '点击撤销派单/撤销订单。',
            '按页面要求填写撤销原因并确认。',
            '丸掌柜侧刷新订单，记录撤销后的主单/子单状态。',
            '丸友集验证账号登录。',
            '进入工作室履约应用-抢单大厅。',
            '按source_order_no或order_title搜索。',
            '确认订单在预期范围内不可见或不可接。',
            '如果策略类型为多工作室或多平台，逐个丸友集账号重复验证。',
            '将台账订单状态标记为revoked。'
        )),
        '撤销成功后丸友集抢单大厅不再展示该订单；多工作室/多平台下所有相关账号均不可见。',
        '丸掌柜撤销状态与丸友集不可见状态一致；无打手订单、无验收、无收入。',
        '用户8条-2','已自动化基础分支'
    ),
    @('TC-CANCEL-002','P1','脚本状态链路推导：task_accepted之前/之后','撤销分支','丸友集管理员已接外部订单但未立即发给打手时撤销',
        '订单已执行E2E-TASK-001中的accept_external_order_from_hall，台账状态为task_accepted；尚未执行dispatch_workshop_order_to_player_hall。',
        'source_order_no、order_title、wanyouji_account。',
        (Steps @(
            '丸友集企业管理员登录。',
            '进入工作室履约应用-订单列表。',
            '搜索source_order_no或order_title。',
            '确认订单已从抢单大厅进入订单列表，且尚未生成打手订单。',
            '在订单详情或列表操作区查找撤销/关闭/拒绝/取消接单入口。',
            '如果丸友集侧提供撤销入口，填写撤销原因并提交。',
            '如果丸友集侧无撤销入口，则切换丸掌柜账号，从订单管理发起撤销。',
            '刷新丸友集订单列表，确认该订单状态关闭或撤销。',
            '打手账号进入抢单大厅搜索该订单。',
            '确认打手侧不可见该订单。',
            '丸掌柜侧回查主单/子单状态和撤销原因。'
        )),
        '工作室已接但未发给打手的订单撤销后，不得继续出现在打手抢单大厅；丸掌柜和丸友集状态应一致。',
        '该分支填补自动化状态task_accepted与task_dispatched_to_player_hall之间的撤销空白。',
        '用户8条-2','待自动化'
    ),
    @('TC-CANCEL-003','P1','脚本状态链路推导：task_player_accepted','撤销分支','打手已抢单但未提交验收时撤销',
        '订单已完成E2E-TASK-001，台账状态为task_player_accepted；尚未执行E2E-DELIVERY-001。',
        'source_order_no、order_title、player_account、wanyouji_account、wanzhanggui_account。',
        (Steps @(
            '打手子账号登录。',
            '进入打手应用-我的订单。',
            '搜索并确认订单处于待履约/进行中状态。',
            '不要执行确认上号、上传首图、提交验收。',
            '丸友集企业管理员登录。',
            '进入工作室履约应用-订单列表或打手订单详情。',
            '发起撤销/取消打手订单/终止履约，按页面要求填写原因。',
            '如撤销入口只在丸掌柜，则丸掌柜主账号进入订单管理发起撤销。',
            '打手刷新我的订单，确认订单关闭、撤销或不可继续提交验收。',
            '打手进入抢单大厅确认订单不会异常重新出现，除非业务规则明确释放。',
            '丸掌柜侧查看子单状态、打手信息、撤销原因。',
            '收入页面确认无该订单收入。'
        )),
        '打手已接单未履约时撤销，订单不能继续提交验收，且不生成收入；若订单重新释放，需有明确状态和记录。',
        '打手我的订单、丸友集订单列表、丸掌柜子单状态一致；撤销责任方和原因可追踪。',
        '用户8条-2','待自动化'
    ),
    @('TC-CANCEL-004','P1','test_e2e_delivery_001_player_submit_acceptance.py + 用户补充2','撤销分支','打手已提交验收但丸友集未申请上游验收前撤销/驳回',
        '订单已执行E2E-DELIVERY-001，台账状态为delivery_submitted；尚未执行E2E-DELIVERY-002申请上游验收。',
        'source_order_no、order_title、player_account、wanyouji_account；成果内容已提交。',
        (Steps @(
            '打手子账号登录，进入打手应用-我的订单。',
            '确认订单已提交验收，状态为待验收/等待验收。',
            '丸友集主账号登录。',
            '进入工作室履约应用-订单列表。',
            '搜索source_order_no或order_title。',
            '打开订单详情，查看打手提交的上号/首图/成果内容。',
            '不要点击申请上游验收。',
            '查找驳回成果、撤销订单、关闭订单等入口。',
            '填写驳回或撤销原因并提交。',
            '打手刷新我的订单，确认能看到驳回原因或订单关闭状态。',
            '如果是驳回，打手重新提交成果。',
            '如果是撤销，确认打手不能再次提交验收。',
            '丸掌柜侧搜索订单，确认未出现可完结主单的待验收记录。',
            '打手我的收入确认无该订单收入。'
        )),
        '提交验收后、申请上游验收前的异常处理必须可追踪；驳回时可重新提交，撤销时不可继续履约；未上游验收通过前不生成收入。',
        '成果内容、驳回/撤销原因、订单状态在打手侧和丸友集主账号侧一致；丸掌柜不可误完结。',
        '8类继续细分-撤销节点','待自动化'
    ),
    @('TC-CANCEL-005','P1','test_e2e_delivery_002_upstream_acceptance_settlement.py + 用户补充2','撤销分支','已申请上游验收但丸掌柜未完结前撤销/驳回',
        '订单已执行apply_upstream_acceptance，台账状态为upstream_acceptance_applied；丸掌柜尚未complete_upstream_order。',
        'source_order_no、order_title、wanyouji_account、wanzhanggui_account。',
        (Steps @(
            '丸友集主账号登录，确认订单已申请上游验收。',
            '丸掌柜主账号登录。',
            '进入订单管理/验收/待完结入口。',
            '搜索source_order_no或order_title。',
            '打开待验收或待完结详情。',
            '核对丸友集提交的成果内容。',
            '选择验收驳回/不通过/撤销完结。',
            '填写原因并提交。',
            '丸友集主账号回到工作室订单列表查看状态回写。',
            '打手登录我的订单查看状态和原因。',
            '如允许重新提交，打手重新提交成果并由丸友集再次申请上游验收。',
            '如订单关闭，确认无法继续申请验收。',
            '打手我的收入确认未生成收入。'
        )),
        '上游验收申请后被丸掌柜驳回或撤销时，状态必须回写丸友集和打手；不得生成收入。',
        '丸掌柜原因、丸友集订单状态、打手状态一致；后续重提或关闭规则明确。',
        '8类继续细分-撤销节点','待自动化'
    ),
    @('TC-CANCEL-006','P1','test_e2e_delivery_002_upstream_acceptance_settlement.py + 用户补充2/6','撤销分支','丸掌柜已完结后冲正/撤销完成态校验',
        '订单已执行complete_upstream_order并验证收入，台账状态为income_verified。',
        '已完成订单；收入记录；可能的冲正或售后撤销入口。',
        (Steps @(
            '选择一笔已完结且打手收入已验证的订单。',
            '记录订单号、子单号、完结时间、收入金额。',
            '丸掌柜主账号进入订单详情。',
            '查找售后撤销、完结后撤销、冲正、退款、异常处理等入口。',
            '如入口存在，发起完结后撤销/冲正并填写原因。',
            '如入口不存在，记录系统不允许完结后撤销的提示或权限限制。',
            '主站或财务页面查看是否生成冲正/追缴流水。',
            '丸友集主账号查看工作室订单状态是否仍为完成或追加异常标识。',
            '打手账号查看我的收入是否出现扣回/冻结/追缴记录。',
            '再次尝试对同一订单发起完结后撤销，校验重复限制。'
        )),
        '完结后如果允许冲正，应保留原完成记录并追加冲正/追缴流水；如果不允许，应明确拦截；不能静默修改历史收入。',
        '原收入、冲正流水、订单终态、异常标识可追踪；避免重复冲正。',
        '8类继续细分-撤销节点/处罚追缴','待自动化'
    ),
    @('TC-MULTI-001','P1','test_e2e_order_001 + 用户补充3','主子单结构分支','一个主单一次生成2个子单并分别进入履约',
        '手工录单页面支持添加多个子单；至少存在一个有效推单策略。',
        '主单1个；子单2个；可同策略或不同策略；每个子单都有独立标题/金额/服务项目。',
        (Steps @(
            '丸掌柜主账号登录。',
            '进入订单管理-手工录单。',
            '创建主单并填写主单基础字段。',
            '点击添加子单，创建子单1。',
            '子单1选择策略A，填写服务项目、数量、金额、交付要求并保存。',
            '再次点击添加子单，创建子单2。',
            '子单2选择策略A或策略B，填写不同服务项目、数量、金额、交付要求并保存。',
            '回到主单页面，确认子单列表有2条记录。',
            '核对主单金额、子单金额合计、子单号。',
            '保存并发布主单。',
            '丸友集管理员进入抢单大厅，按主单号搜索。',
            '确认页面按实际规则展示两个子任务，或展示一个主单并能展开两个子单。',
            '分别接入两个子单。',
            '检查工作室订单列表中两个子单状态可独立流转。'
        )),
        '一主两子保存和发布成功；两个子单均能被丸友集识别；子单状态、金额、策略独立。',
        '主单号相同、子单号不同；主单汇总金额与子单金额合计一致；策略与工作室可见范围正确。',
        '用户8条-3','待自动化'
    ),
    @('TC-MULTI-002','P1','test_e2e_delivery_002 + 用户补充3','主子单结构分支','第1个子单完成后续发第2个子单',
        '系统支持对已存在主单追加/续发子单；第1子单已走完验收收入。',
        '主单1个；子单1已完成；子单2后续追加。',
        (Steps @(
            '按TC-MAIN-002完成一个一主一子订单，直到打手收入验证通过。',
            '丸掌柜主账号进入该主单详情。',
            '确认主单下子单1状态为已完成/已结算或实际完成态。',
            '查找追加子单/添加子单/续发入口。',
            '点击追加子单。',
            '填写子单2的服务项目、数量、金额、策略和交付要求。',
            '保存子单2。',
            '发布或提交子单2。',
            '丸友集抢单大厅搜索同一主单号或子单2标题。',
            '接入子单2并继续发给打手。',
            '打手完成子单2履约验收。',
            '丸掌柜完结子单2。',
            '检查主单最终状态和两个子单的状态历史。'
        )),
        '第1子单完成后仍可按规则续发第2子单；第2子单能独立履约；主单最终状态在全部子单结束后正确汇总。',
        '子单1收入不被续发影响；子单2生成新的接单/验收/收入记录；主单汇总状态准确。',
        '用户8条-3','待自动化'
    ),
    @('TC-MULTI-003','P1','用户补充3','主子单结构分支','一主多子部分完成、部分撤销的主单汇总',
        '主单下存在2个及以上子单；至少1个子单可完成，至少1个子单可撤销。',
        '主单1个；子单A完成；子单B撤销；价格模式可固定价。',
        (Steps @(
            '丸掌柜创建一主两子订单并发布。',
            '丸友集接入两个子单。',
            '对子单A发给打手并完成履约。',
            '丸友集对子单A申请上游验收。',
            '丸掌柜对子单A验收通过并完结。',
            '确认子单A收入生成。',
            '对子单B在未接单、已接单或待履约节点执行撤销。',
            '填写撤销原因并提交。',
            '丸掌柜回到主单详情。',
            '核对子单A状态为已完成，子单B状态为撤销完成。',
            '查看主单汇总状态、完成金额、撤销金额、总金额。',
            '主站或财务侧查看金额统计。',
            '打手侧确认只对子单A产生收入。'
        )),
        '部分完成部分撤销时，子单状态必须独立；主单状态按业务规则展示为部分完成/已完成/异常完成；收入只对完成子单生成。',
        '主单汇总不能把撤销子单误计入已完成收入；子单A收入不受子单B撤销影响。',
        '8类继续细分-多子单','待自动化'
    ),
    @('TC-MULTI-004','P1','用户补充3/4/5','主子单结构分支','一主多子不同策略、不同分配方式、不同价格模式组合',
        '至少存在2个推单策略或同策略下允许子单独立设置分配/价格；工作室和打手账号充足。',
        '子单A：策略A+抢单大厅+固定价；子单B：策略B+指派打手+SKU报价或百分比价。',
        (Steps @(
            '丸掌柜创建主单。',
            '添加子单A，选择策略A和固定发单价。',
            '添加子单B，选择策略B和SKU报价或百分比发单价。',
            '保存并发布主单。',
            '工作室A账号登录，确认只看到子单A或对应范围。',
            '工作室B账号登录，确认只看到子单B或对应范围。',
            '对子单A按抢单大厅方式发给打手。',
            '对子单B按指派打手或智能派单方式发给打手。',
            '两个打手分别完成履约。',
            '分别申请上游验收并由丸掌柜完结。',
            '核对子单A和子单B的价格计算、打手收入、主单汇总金额。',
            '尝试跨工作室、跨打手查看或处理不属于自己的子单。'
        )),
        '不同子单的策略、分配方式、价格模式互不污染；权限隔离正确；主单金额按子单维度汇总。',
        '子单级别的工作室、打手、价格、收入独立可追踪。',
        '8类继续细分-多子单/分配/价格','待自动化'
    ),
    @('TC-DISPATCH-001','P1','test_e2e_task_001_external_order_dispatch_to_player.py + 用户补充4','打手分配方式','分配方式=抢单大厅',
        '订单已由丸友集管理员接入工作室订单列表；企业存在至少2个打手。',
        'assignment_method=抢单大厅；source_order_no、order_title、player A/B。',
        (Steps @(
            '丸友集管理员打开工作室订单列表。',
            '搜索目标外部订单。',
            '点击立即发单。',
            '选择分配方式为抢单大厅。',
            '填写发单价、服务时限、订单标题等必填项。',
            '提交发单。',
            '打手A短信登录。',
            '进入打手应用-抢单大厅，搜索该订单。',
            '打手B短信登录。',
            '进入打手应用-抢单大厅，搜索该订单。',
            '打手A点击立即抢单。',
            '打手A进入我的订单确认订单出现。',
            '打手B刷新抢单大厅，确认订单不可重复抢或库存按规则减少。'
        )),
        '抢单大厅方式下，符合条件打手可见；一个打手抢单后进入我的订单，其他打手不可重复抢同一份库存。',
        '抢单大厅、打手我的订单、工作室订单列表中的订单号、价格、打手信息一致。',
        '用户8条-4','脚本已参数化支持'
    ),
    @('TC-DISPATCH-002','P1','test_e2e_task_001_external_order_dispatch_to_player.py + 用户补充4','打手分配方式','分配方式=指派打手',
        '订单已进入工作室订单列表；目标打手账号可登录。',
        'assignment_method=指派打手；指定player_account。',
        (Steps @(
            '丸友集管理员进入工作室订单列表。',
            '搜索目标外部订单。',
            '点击立即发单。',
            '选择分配方式为指派打手。',
            '在打手选择框中选择目标打手。',
            '填写发单价、服务时限、交付要求。',
            '提交发单。',
            '目标打手登录打手应用。',
            '进入我的订单或待处理任务，确认订单出现。',
            '非目标打手登录打手应用。',
            '搜索同一订单。',
            '确认非目标打手不可见或不可操作。',
            '管理员回查订单详情中的指派打手信息。'
        )),
        '被指派打手可处理订单；非指派打手不可抢；管理员侧展示指派打手正确。',
        '指派打手手机号/姓名、订单号、状态在管理员和打手侧一致。',
        '用户8条-4','脚本已参数化支持'
    ),
    @('TC-DISPATCH-003','P1','test_e2e_task_001_external_order_dispatch_to_player.py + 用户补充4','打手分配方式','分配方式=智能派单',
        '智能派单策略已配置；候选打手满足策略条件。',
        'assignment_method=智能派单；候选打手列表。',
        (Steps @(
            '丸友集管理员确认智能派单规则启用。',
            '进入工作室订单列表搜索目标订单。',
            '点击立即发单。',
            '选择分配方式为智能派单。',
            '填写或确认发单价、服务时限、订单标题。',
            '提交发单。',
            '查看页面是否展示派单成功或目标打手。',
            '使用命中打手登录，进入我的订单确认订单出现。',
            '使用未命中打手登录，确认不能处理该订单。',
            '管理员回查订单详情中的分配方式和打手信息。',
            '如派单失败，记录失败提示和候选规则。'
        )),
        '智能派单成功时订单进入命中打手的任务/我的订单；失败时有明确提示且订单不进入异常半派单状态。',
        '派单方式、命中打手、订单状态、失败提示可追踪。',
        '用户8条-4','脚本已参数化支持'
    ),
    @('TC-DISPATCH-004','P1','用户补充4','打手分配方式异常','抢单大厅重复抢单/库存不足拦截',
        '订单以抢单大厅方式发出；任务库存为1或剩余量可控；至少两个打手账号可登录。',
        '打手A、打手B；同一source_order_no/order_title。',
        (Steps @(
            '丸友集管理员将订单发到抢单大厅。',
            '打手A登录抢单大厅并搜索订单。',
            '打手B登录抢单大厅并搜索同一订单。',
            '打手A点击立即抢单并成功。',
            '打手A进入我的订单确认订单出现。',
            '打手B刷新抢单大厅。',
            '打手B再次搜索该订单。',
            '如果订单仍可见，打手B点击立即抢单。',
            '观察系统是否提示库存不足、已被抢、不可重复接单。',
            '管理员查看订单接单记录。',
            '确认接单记录只有打手A或数量按库存规则正确扣减。'
        )),
        '同一库存不能被重复抢；库存不足有明确提示；订单数量不出现负数或重复承接。',
        '抢单记录、库存、打手我的订单一致。',
        '8类继续细分-分配方式异常','待自动化'
    ),
    @('TC-DISPATCH-005','P1','用户补充4','打手分配方式异常','指派打手不可用或无权限时拦截',
        '存在禁用、离职、未授权或不满足技能的打手；指派打手入口可用。',
        '不可用打手账号；目标订单。',
        (Steps @(
            '丸友集管理员打开工作室订单列表。',
            '点击立即发单。',
            '选择分配方式为指派打手。',
            '在打手下拉中查找禁用/离职/未授权打手。',
            '如果不可用打手不展示，记录过滤结果。',
            '如果可选择该打手，尝试提交发单。',
            '观察系统是否拦截并提示不可指派原因。',
            '改选可用打手并提交。',
            '可用打手登录后确认订单出现。',
            '不可用打手登录后确认订单不可见。'
        )),
        '不可用打手不得被指派；系统应在选择阶段过滤或提交阶段明确拦截；可用打手流程不受影响。',
        '打手状态、指派结果、订单可见范围一致。',
        '8类继续细分-分配方式异常','待自动化'
    ),
    @('TC-PRICE-001','P1','test_e2e_push_003 + test_e2e_order_001 + 用户补充5','价格模式','固定发单价贯穿到收入',
        '推单配置或发单设置支持固定发单价；订单能走到收入验证。',
        'price_mode=固定发单价；金额例如10.00。',
        (Steps @(
            '丸掌柜主账号进入推单配置或发单设置。',
            '创建固定发单价策略，填写固定金额。',
            '手工录单创建订单并选择该策略。',
            '保存发布订单。',
            '丸友集抢单大厅查看订单展示价。',
            '管理员接单并发给打手。',
            '打手抢单后进入我的订单，确认可见金额。',
            '打手提交验收。',
            '丸友集申请上游验收。',
            '丸掌柜完结主单。',
            '打手进入我的收入查看订单收入。',
            '主站或财务页面查看结算记录。'
        )),
        '订单展示价、打手可见价、收入金额均符合固定发单价规则。',
        '金额、币种、小数精度、订单号在丸掌柜、丸友集、主站一致。',
        '用户8条-5','待自动化'
    ),
    @('TC-PRICE-002','P1','用户补充5','价格模式','百分比发单价计算',
        '发单设置支持百分比发单价；主单金额可控。',
        'price_mode=百分比发单价；主单金额样本A/B；百分比配置。',
        (Steps @(
            '丸掌柜进入发单设置或推单配置。',
            '创建百分比发单价策略。',
            '填写百分比、最低价、最高价、取整规则等页面必填项。',
            '创建主单金额为样本A的手工录单。',
            '添加子单并选择百分比策略。',
            '保存后查看系统计算出的子单发单价。',
            '发布订单并在丸友集查看价格。',
            '完成接单、打手履约、上游验收和收入验证。',
            '创建主单金额为样本B的第二笔订单。',
            '重复选择同一百分比策略。',
            '核对样本B计算结果与样本A不同且符合公式。',
            '记录小数、封顶、保底规则。'
        )),
        '子单发单价随主单金额按百分比计算；结算和收入使用同一计算结果。',
        '百分比配置、主单金额、计算价、收入金额可追踪；边界规则明确。',
        '用户8条-5','待自动化'
    ),
    @('TC-PRICE-003','P1','用户补充5','价格模式','SKU报价匹配',
        'SKU报价表已配置；订单能选择或携带SKU。',
        'SKU-A、SKU-B、未配置SKU；对应报价。',
        (Steps @(
            '丸掌柜进入SKU报价配置。',
            '配置SKU-A报价和SKU-B报价。',
            '创建包含SKU-A的手工录单或抖音订单。',
            '添加子单并选择SKU报价模式。',
            '保存后核对子单发单价命中SKU-A。',
            '发布并完成履约验收收入。',
            '创建包含SKU-B的订单，重复核对报价。',
            '创建未配置SKU的订单。',
            '尝试保存或发布。',
            '记录系统是拦截、提示补报价，还是按默认价处理。'
        )),
        '已配置SKU命中对应报价；不同SKU不串价；未配置SKU处理明确。',
        'SKU编码/名称、报价、订单商品、子单价、收入金额一致。',
        '用户8条-5','待自动化'
    ),
    @('TC-PRICE-004','P1','用户补充5','价格模式边界','百分比发单价小数、保底、封顶、零金额边界',
        '百分比价格配置支持保底/封顶/小数规则；可创建不同金额订单。',
        '主单金额：0、低金额、普通金额、高金额；百分比、最低价、最高价。',
        (Steps @(
            '配置百分比发单价策略。',
            '设置百分比、最低发单价、最大发单价和小数规则。',
            '创建主单金额为0或极低金额的订单。',
            '添加子单并选择该策略，观察是否拦截或按最低价计算。',
            '创建普通金额订单，核对正常百分比计算。',
            '创建高金额订单，核对是否触发封顶价。',
            '分别发布订单到丸友集。',
            '丸友集查看每笔订单展示价格。',
            '完成其中至少一笔订单到收入。',
            '核对收入金额与计算价一致。',
            '记录小数保留、四舍五入、保底封顶触发规则。'
        )),
        '百分比价格边界处理明确；异常金额被拦截或按规则计算；结算不偏离计算价。',
        '主单金额、百分比、保底/封顶、计算价、收入金额可追踪。',
        '8类继续细分-价格边界','待自动化'
    ),
    @('TC-PRICE-005','P1','用户补充5','价格模式边界','SKU多数量、多SKU、未命中报价处理',
        'SKU报价配置已存在；订单可含多个SKU或数量字段。',
        'SKU-A数量2；SKU-A+SKU-B组合；未配置SKU-C。',
        (Steps @(
            '配置SKU-A和SKU-B报价。',
            '创建SKU-A数量为2的订单。',
            '保存子单，核对价格是单价乘数量还是固定SKU价。',
            '创建同时包含SKU-A和SKU-B的订单。',
            '保存子单，核对价格是累加、拆子单还是拦截。',
            '创建SKU-C未配置报价订单。',
            '尝试保存子单。',
            '观察系统提示补充报价、禁止发布或使用默认价。',
            '对可发布订单完成履约到收入。',
            '核对SKU价格和收入一致。'
        )),
        'SKU数量、组合SKU、未命中SKU处理规则明确；不出现错误套价或静默默认价。',
        'SKU编码、数量、报价、子单价、收入金额一致。',
        '8类继续细分-价格边界','待自动化'
    ),
    @('TC-PENALTY-001','P1','test_e2e_delivery_002 + 用户补充6','处罚追缴','已完成子单处罚追缴',
        '订单已执行到income_verified或至少upstream_completed；处罚入口和审核账号可用。',
        '已完成子单；责任方=打手/工作室；处罚金额；处罚原因。',
        (Steps @(
            '选择一笔已完成并产生收入的订单。',
            '记录source_order_no、order_title、子单号、打手收入金额。',
            '使用有处罚权限的账号登录。',
            '进入处罚/追缴入口，或从订单详情进入处罚操作。',
            '选择目标订单和子单。',
            '选择责任方为打手或工作室。',
            '填写处罚原因、处罚金额、凭证或备注。',
            '提交处罚申请。',
            '如存在审核流，使用审核账号审批通过。',
            '打手登录我的收入或账户流水。',
            '确认出现扣减/追缴记录。',
            '管理员查看处罚单状态。',
            '主站或财务查看关联流水。'
        )),
        '已完成子单可按规则发起处罚追缴；审核通过后产生扣减或追缴流水；原订单完成状态不应被错误回退。',
        '处罚单号、订单号、子单号、责任方、金额、流水在各端一致；不得重复扣款。',
        '用户8条-6','待自动化'
    ),
    @('TC-PENALTY-002','P1','TC-CANCEL分支 + 用户补充6','处罚追缴','撤销完成子单处罚追缴',
        '存在撤销完成子单；处罚入口或拦截规则待确认。',
        '撤销完成子单；责任方；处罚原因。',
        (Steps @(
            '通过TC-CANCEL-002或TC-CANCEL-003构造撤销完成子单。',
            '记录撤销节点、责任方、是否已有冻结或收入。',
            '管理员进入订单详情。',
            '查找处罚/追缴入口。',
            '如果入口可用，填写责任方、金额、原因并提交。',
            '如果入口不可用，记录系统提示和权限限制。',
            '如存在审核流，执行审核通过或拒绝。',
            '责任方账号查看流水或处罚记录。',
            '再次尝试对同一子单发起处罚。',
            '确认重复处罚被限制，或二次处罚有独立审批记录。'
        )),
        '撤销完成子单若允许追缴，应有明确处罚单和流水；若不允许，应明确拦截；不得重复扣款。',
        '撤销状态、处罚状态、责任方、金额、流水一致。',
        '用户8条-6','待自动化'
    ),
    @('TC-PENALTY-003','P1','用户补充6','处罚追缴审批','处罚追缴审核拒绝与重复发起限制',
        '存在可处罚订单；处罚审核流可用。',
        '处罚申请A；审核结果=拒绝；再次发起申请B。',
        (Steps @(
            '管理员选择已完成或撤销完成子单。',
            '发起处罚追缴申请A。',
            '填写处罚原因、金额、责任方。',
            '提交后使用审核账号登录。',
            '打开处罚审核列表。',
            '选择申请A并审核拒绝。',
            '填写拒绝原因。',
            '责任方账号查看收入或流水。',
            '确认未发生扣款。',
            '管理员再次对同一子单同一原因发起处罚申请B。',
            '观察系统是否允许再次发起。',
            '如允许，确认申请B有独立单号和状态；如不允许，确认提示明确。'
        )),
        '审核拒绝不应扣款；重复发起有明确限制或独立审批记录；拒绝原因可追踪。',
        '处罚申请状态、拒绝原因、流水状态一致。',
        '8类继续细分-处罚审批','待自动化'
    ),
    @('TC-ACCOUNT-001','P0','test_e2e_member_001 + 用户补充7','账号登录矩阵','注册派单履约链路账号和权限梳理',
        '主账号、子成员、打手、主站账号、财务/审核账号可用；台账保存账号角色。',
        '丸掌柜主账号、丸友集主账号、客服子账号、打手子账号、主站wangss、财务/审核账号。',
        (Steps @(
            '丸友集主账号登录，进入成员管理。',
            '邀请或确认客服子成员存在。',
            '邀请或确认打手子成员存在。',
            '客服子成员短信登录，确认可见客服/收单相关菜单。',
            '打手子成员短信登录，确认可见打手应用、抢单大厅、我的订单、我的收入。',
            '打手尝试访问企业管理、工作室履约应用、授权配置入口。',
            '确认越权入口不可见或被拦截。',
            '丸掌柜主账号登录，确认可访问手工录单、抖音录单、订单管理、验收。',
            '丸友集主账号登录，确认可访问工作室履约应用、抢单大厅、订单列表、立即发单。',
            '主站账号登录，确认可监管认证、应用、订单或结算。',
            '财务/审核账号登录，确认可查看结算、处罚追缴入口。',
            '使用企业B账号搜索企业A订单，确认数据隔离。'
        )),
        '每类账号只能执行角色允许动作；外来订单链路能由正确账号串联完成；越权访问被拦截。',
        '账号手机号、角色、企业归属、可见菜单、不可执行动作记录完整。',
        '用户8条-7','成员邀请已自动化；矩阵待完善'
    ),
    @('TC-ACCOUNT-002','P1','test_e2e_member_001 + 权限隔离推导','账号与租户隔离','跨企业账号不可查看或操作外来订单',
        '至少存在企业A和企业B；企业A有外来订单；企业B账号可登录。',
        '企业A订单；企业B丸掌柜账号、丸友集主账号、打手账号。',
        (Steps @(
            '使用企业A丸掌柜创建并发布外来订单。',
            '企业A丸友集主账号接入订单。',
            '使用企业B丸掌柜账号登录。',
            '在订单管理中搜索企业A订单号。',
            '确认搜索不到或无权限查看。',
            '使用企业B丸友集主账号登录。',
            '进入抢单大厅和订单列表搜索企业A订单。',
            '确认不可见或不可操作。',
            '使用企业B打手账号登录。',
            '进入抢单大厅搜索企业A订单。',
            '确认不可见或不可抢。',
            '尝试直接访问企业A订单详情URL。',
            '确认系统拦截或提示无权限。'
        )),
        '跨企业账号不可查看或操作企业A订单；直接URL访问也应拦截。',
        '企业ID、账号归属、订单归属隔离一致。',
        '8类继续细分-账号/租户隔离','待自动化'
    ),
    @('TC-MAIN-003','P0','用户补充8 + 已有脚本串联','main流程','手工录单注册派单履约结算main流程',
        '企业和应用已准备；授权、工作室、策略、成员、打手已准备。',
        '来源=手工录单；子单=1；策略=单平台指定；分配方式=抢单大厅；价格=固定发单价。',
        (Steps @(
            '丸掌柜手工录单创建一主一子订单。',
            '选择单平台指定策略并发布。',
            '丸友集主账号在抢单大厅看到私单。',
            '丸友集主账号接外部订单。',
            '丸友集主账号立即发单到抢单大厅。',
            '打手在抢单大厅抢单。',
            '打手在我的订单确认上号并上传首图。',
            '打手提交验收。',
            '丸友集主账号在订单列表查看待验收状态。',
            '丸友集主账号申请上游验收。',
            '丸掌柜主账号完结主单。',
            '丸友集主账号查看订单完成或结算中。',
            '打手查看我的收入。'
        )),
        '该main流程全链路完成，是当前自动化最稳定基线。',
        '状态链路为dispatched -> task_accepted -> task_dispatched_to_player_hall -> task_player_accepted -> delivery_submitted -> upstream_acceptance_applied -> upstream_completed -> income_verified。',
        '用户8条-8','已自动化组合'
    )
)

$accountRows = @(
    @('丸掌柜主账号','丸掌柜','手工录单、抖音录单、撤销、订单验收、完结主单','test_e2e_order_001/002、delivery_002','必须已开通丸掌柜；台账wanzhanggui_status=opened'),
    @('丸友集主账号/企业管理员','丸友集','生成授权密钥、保存权限、接外部订单、立即发单、申请上游验收','auth_001、task_001、delivery_001/002','必须已开通丸友集；可访问工作室履约应用'),
    @('打手子账号','丸友集','抢单大厅抢单、我的订单提交验收、我的收入查看收益','member_001、task_001、delivery_001/002','短信登录；角色菜单必须只开放打手能力'),
    @('客服子账号','丸友集/收单应用','客户自助填单、收单协作、打手订单验收候选角色','member_001、后续扩展','权限待脚本继续完善'),
    @('SaaS主站管理员','SaaS主站','企业认证、应用审批、订单/结算监管','saas_001/003/004','wangss / 123456'),
    @('财务/处罚审核账号','主站/丸友集/丸掌柜','结算流水、处罚追缴审核','待扩展','如无独立账号，先用管理员代查')
)

$stateRows = @(
    @('dispatched','丸掌柜已发布/已派单','丸友集抢单大厅可见','打手不可见','无收入','order_001后状态'),
    @('revoked','丸掌柜撤销完成或取消派单','丸友集抢单大厅不可见','打手不可见','无收入','order_002后状态'),
    @('task_accepted','丸掌柜订单仍可追踪','工作室订单列表已接入','打手不可见','无收入','管理员接外部订单后'),
    @('task_dispatched_to_player_hall','丸掌柜订单可追踪','工作室订单已立即发单','打手抢单大厅可见','无收入','发给打手后'),
    @('task_player_accepted','丸掌柜可回查打手信息','工作室订单显示打手已接','打手我的订单出现','无收入','打手抢单后'),
    @('delivery_submitted','丸掌柜待验收/待上游验收候选','工作室订单待验收/申请上游验收','打手我的订单已提交验收','无收入','打手提交验收后'),
    @('upstream_acceptance_applied','丸掌柜出现待完结/待验收','工作室订单已申请上游验收','打手等待收入','无收入或待结算','丸友集申请上游验收后'),
    @('upstream_completed','丸掌柜主单完结','工作室订单完成/结算中','打手等待收入或可见收入','结算中/已生成','丸掌柜完结主单后'),
    @('income_verified','丸掌柜主单完结','工作室订单完成/结算中','打手我的收入可见','收入已验证','delivery_002最终状态')
)

$todoRows = @(
    @('抖音录单入口、店铺授权、订单同步字段、撤销回传规则','订单来源分支','先生成正向用例，撤销差异实测后再拆脚本'),
    @('撤销节点是否覆盖发布前、发布后未接、管理员已接、打手已接、提交验收后、上游验收后','撤销分支','当前已有发布后未接自动化，其余按状态链路补'),
    @('多子单能力：一次发2个、完成后续发、部分完成部分撤销时主单终态','主子单分支','需要页面确认是否支持追加子单和主单汇总规则'),
    @('指派打手、智能派单入口是否和抢单大厅在同一个立即发单弹窗中','分配方式分支','脚本已参数化assignment_method，需实测DOM字段'),
    @('固定发单价、百分比发单价、SKU报价配置入口和金额公式','价格模式','先记录实际结果，再固化公式断言'),
    @('已完成/撤销完成子单处罚追缴入口、审核流、重复处罚限制','处罚追缴','需要确认权限和流水页面'),
    @('客服、打手、管理员、财务/审核账号的最小权限和菜单隔离','账号矩阵','member_001已有打手/客服雏形，需补财务/审核'),
    @('main流程需要保留几条：手工录单固定价抢单大厅、抖音录单固定价抢单大厅、多子单扩展main','main流程','本版先给手工录单main，抖音和多子单作为扩展')
)

$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$excel.DisplayAlerts = $false
try {
    if (Test-Path $Output) { Remove-Item $Output -Force }
    $wb = $excel.Workbooks.Add()
    while ($wb.Worksheets.Count -gt 1) { $wb.Worksheets.Item($wb.Worksheets.Count).Delete() }
    $wb.Worksheets.Item(1).Name = '说明'
    $intro = $wb.Worksheets.Item('说明')
    $intro.Cells.Item(1,1).Value2 = '项目'
    $intro.Cells.Item(1,2).Value2 = '内容'
    $intro.Cells.Item(2,1).Value2 = '生成依据'
    $intro.Cells.Item(2,2).Value2 = '基于D:\01SaaS\e2e\tests下已生成自动化脚本反推测试用例，不沿用前面已整理混乱的Excel。'
    $intro.Cells.Item(3,1).Value2 = '设计方式'
    $intro.Cells.Item(3,2).Value2 = '先整理已自动化注册、开通、授权、工作室授权、推单配置、手工录单、接单、打手履约、上游验收、收入主链路，再插入8类分支。'
    $intro.Cells.Item(4,1).Value2 = '步骤颗粒度'
    $intro.Cells.Item(4,2).Value2 = '操作步骤按脚本方法顺序拆为编号步骤；预期结果覆盖页面状态、台账状态、跨端字段一致性。'
    $intro.Rows.Item(1).Font.Bold = $true
    $intro.Rows.Item(1).Interior.ColorIndex = 37
    Format-Sheet $intro

    Add-Sheet $wb '自动化脚本映射' @('用例编号','脚本文件','脚本覆盖点','脚本流程摘要','关键方法/状态','可沉淀断言') $scriptRows | Out-Null
    Add-Sheet $wb '主流程E2E用例' $caseHeaders $mainRows | Out-Null
    Add-Sheet $wb '分支扩展E2E用例' $caseHeaders $branchRows | Out-Null
    Add-Sheet $wb '账号与数据台账' @('账号/数据类型','平台','用途','来源脚本/关联流程','备注') $accountRows | Out-Null
    Add-Sheet $wb '状态流转与断言' @('台账状态','丸掌柜视角','丸友集主账号视角','打手视角','账务/收入视角','说明') $stateRows | Out-Null
    Add-Sheet $wb '待确认项' @('待确认项','影响范围','处理策略') $todoRows | Out-Null

    $wb.Worksheets.Item('说明').Activate()
    $wb.SaveAs($Output, 51)
    $wb.Close($true)
}
finally {
    $excel.Quit()
}

$excel2 = New-Object -ComObject Excel.Application
$excel2.Visible = $false
$excel2.DisplayAlerts = $false
try {
    $wb2 = $excel2.Workbooks.Open($Output)
    Write-Output 'excel_open_ok=True'
    Write-Output ('sheets=' + $wb2.Worksheets.Count)
    Write-Output ('main_rows=' + $wb2.Worksheets.Item('主流程E2E用例').UsedRange.Rows.Count)
    Write-Output ('branch_rows=' + $wb2.Worksheets.Item('分支扩展E2E用例').UsedRange.Rows.Count)
    $wb2.Close($false)
}
finally {
    $excel2.Quit()
}

Write-Output $Output
