交付出件箱

← 返回出件箱

门店签约并行化方案 v2 · 微信支付版

2026-08-13 · 云机拟 · 取代 08-12 初稿(初稿的"线下付款保留/批1"等结论已作废)
现状描述均物理核实(源码行号/grep),非推测。已含红军自检修正 4 处(见 §八)。


导读(接力会话先读这段)

状态:方案已定稿,✅ 待拍板清零;代码一行未写,无半成品。
配套效果图:signup-v2-mockup.html(9 节,含三端),与本文互为一套。

想干什么 看哪节
先了解拍了哪些板 〇(含各章内 🔴 拍板行)
做批 A 价格管理 五 N(数据模型)· 五 T.2(原价样式)
做批 B 微信支付 五 N(选型/坑清单/13 项材料)· 五 E(资金链 7 步移植)
做批 C/D 页面改造 一 / 二 / 四 / 五 · 五 B(退款)· 五 F(列改动纪律)
做批 E 增购续费 五 G(计价)· 五 I(续费顺延)· 五 J(席位三态硬约束)
切换存量数据 五 C(处置矩阵)
排查影响面 五 A(24 页清单)
追溯"某细节为何这样定" 文末附录(八轮评审流水)

🔴 动手前必读三条红线
1. 支付代码落 settlement/,不落核心 app/
2. 支付页绝不能自动跳微信授权(快照页虚拟 openid,项目已踩过)
3. 算金额/判到期/算席位禁止读 profile.service_end_at(已知失真的遗留字段)

〇、已拍板(长官 08-13 定,不再问)

# 拍板
1 线下付款整体去掉,微信支付为唯一支付方式
2 首开 + 增购 + 续费一块切微信(不留两套并存)
3 退款走线下,但系统必须标记已退款(可查)
4 平台定单价 · 门店选购买席位数量 · 金额 = 单价 × 数量,服务端算
5 不做套餐;PC 新增「价格管理」,默认带出「赵阳智脑」一个商品,数量默认 1
6 两审通过后仍手动点开通(维持现状机制)
7 商户号/资质等前提后议,先出方案出图

小项(云机定,写明可改):资料已驳回时支付按钮保留可用 + 黄条提示"建议先修改资料"(并行原则;微信支付本身无驳回态)。


一、门店手机端流程

现状(实测)

等待页 review/page.tsx:598:canGoPayment = store?.status === "待线下付款" —— 资料审核通过前,门店根本看不到付款入口。

改造:提交资料后进「进度页」,双卡片并行

提交资料成功
   ↓
┌─ 📋 资料审核 ────────────┐   ┌─ 💰 服务付款 ───────────────┐
│ 审核中 / 已通过 / 已驳回   │   │ 未支付 → [去支付](随时可点) │
│ 驳回:红字原因+[修改资料]  │   │ 已支付 ✓(微信实时到账)     │
└─────────────────────────┘   └────────────────────────────┘
   两个都绿 → 「等待平台开通账号」 → 已开通

组合态展示矩阵(微信化后无"凭证驳回"态,矩阵简化为 3×2):

资料 \ 付款 未支付 已支付
待审核 审核中 + 可去支付 审核中 + ✓已支付
已驳回 红卡驳回+修改资料;支付卡可用+黄条提示 红卡驳回;支付卡 ✓ 不受影响
已通过 绿卡 + 醒目[去支付] → 等待平台开通账号

驳回+已支付且门店放弃 → 平台线下退款 + 系统标记(§四)。

二、支付页(推倒重做)

现付款页 4 个输入(payment/page.tsx:23:seatCount / amount / payerName / voucher)→ 只剩数量一个可操作项:

赵阳智脑  ¥2,380 / 席          ← 单价来自价格管理 · 只读
数量  [-] 2 [+]           ← 门店选(唯一输入)
──────────────────────
合计  ¥4,760               ← 服务端算 · 展示给门店
        [ 微信支付 ]

三、数据模型(新增 2 表,零 CHECK 变更)

service_prices 价格管理:id / name(默认"赵阳智脑") / unit_price / default_quantity(默认1) / is_active / updated_at / updated_by

🔴 service_price_changes 改价历史(必须独立成表):
id / price_id / old_price / new_price / changed_by / changed_at / remark(可空)

为什么不能只靠 service_prices.updated_by/at:那是覆盖式的,只留最后一次,
而效果图的「改价记录」弹窗要列出历次改价(时间 / 原价 / 改后价 / 操作人),
且订单按 price_snapshot 结算,事后对账需要追溯"某笔订单成交时单价是多少、谁改的"。
一改一行,永不覆盖。

payment_orders 支付订单:
id / org_id / biz_type(首开|增购|续费) / service_order_id(可空) / price_id / price_snapshot / quantity / total_amount / out_trade_no(唯一) / transaction_id(唯一·可空) / status(待支付|已支付|已退款) / paid_at / refunded_at / refunded_by / refund_note / created_at

退款标记(拍板 3 落地):status=已退款 + 退款时间/操作人/备注;列表付款列显蓝色「已退款」(配色见 §5.1),可筛选。

payment_vouchers 退役为只读历史(首开+增购+续费的存量凭证仍可查)。

状态机:零 schema 变更路线(CHECK 约束一个字不动 —— 核心主仓 app/models/settlement.py:116 有同表同约束副本,动它必须两边同步,收益不值):
- profile.status 只走资料/开通链:待提交资料 → 资料待审核 →(通过)待线下付款 → 已开通/已到期/已关闭
- 库值 待线下付款 展示层改叫「待付款」(语义=资料已通过),命名债记账,微信化稳定后再议两边同步改名
- 库值 待确认到账 退出新流程(仅存量行使用)
- 付款维度完全由 payment_orders 表达,总状态 API 层派生(照抄现有 待开通账号 派生机制 stores.py:475)

四、平台后台

五、列表与筛选(PC + 手机,副标记方案作废)

展示:独立「付款」列(方案甲)
- PC 表格加一列:已支付(绿) / 未支付(黄) / 已退款(蓝) —— 配色见 §5.1(已拍板)
- 平台手机端卡片:右上角小角标同三态
- 渠道端如有机构状态展示 → 批 D 排查同步(涉及面清单项)

主状态 8 值(展示层;比现状 10 个筛选项还少 2 个):
待提交资料 / 资料待审核 / 资料已驳回 / 待付款 / 待开通账号 / 已开通 / 已到期 / 已关闭

派生规则:

库 status 付款维度 展示
待提交资料 — 待提交资料
资料待审核 任意(付款列另示) 资料待审核
资料已驳回 任意 资料已驳回
待线下付款 未支付 待付款
待线下付款 已支付 待开通账号
已开通/已到期/已关闭 — 原样
(存量)待确认到账 — 待开通账号(沿现行派生)

筛选:
- PC:状态多选换成上述 8 项 + 新增「付款状态」下拉(全部/已支付/未支付/已退款),两条件 AND
- 手机端:现有 chips 组(platform/minicup/stores/page.tsx:202 renderFilterGroup)加一组「付款」chips

5.1 付款状态配色(2026-08-13 长官拍板 · 实施清单硬项)

值 配色 来源
已支付 绿 #e7f8f2 / #15906f / 边 #bfe9dc 复用现有「确认·开通」档
已退款 蓝 #eef3ff / #3456b8 / 边 #cbd8ff 复用现有「已入结算单」档
未支付 黄(沿用「含"未"→黄」规则) 零改动

实现(落统一仓 settlement/ 侧 · settlement/web/app/admin/minicup/_components/minicup-ui.tsx 的 statusClass(),
在兜底 return "bg-[#eef3f6]..." 之前插入两行精确匹配):

if (status === "已支付") return "bg-[#e7f8f2] text-[#15906f] border-[#bfe9dc]";
if (status === "已退款") return "bg-[#eef3ff] text-[#3456b8] border-[#cbd8ff]";

用 === 精确匹配而非 includes:现有规则靠子串命中,「已支付」若写成 includes("支付") 会与未来
「待支付」「支付失败」等值抢匹配。放兜底灰之前、四档规则之后,不影响任何既有状态取色。
⚠️ 平台手机端若另有取色实现,需同步(批 D 排查项)。

五、A 影响面全清单(2026-08-13 全量 grep 扫描 · 24 个前端页面被牵连)

触发:初稿只画了直接改动的 5 个页面,长官指出"所有相关联受改动影响的都得考虑进去"。
判据:git grep -ln "payment_voucher|voucher|付款凭证|到账|payer_name" settlement/web/app/**/*.tsx → 24 个文件。

🔴 两个最深的坑(初稿完全没提)

坑 1 · 收益账本按「到账确认」生成 —— admin/minicup/revenue-ledger/page.tsx:324 写明「按到账确认生成」。
人工「确认到账」动作是财务链路的触发点,直接退役会掐断收益账本与渠道分成。
⇒ 微信回调必须接上这个触发点,属资金链改造,不是展示层改动。批 B 必含。

坑 2 · 增购/续费有「待确认金额」状态 —— models/settlement.py:524
status in ('待确认金额','待付款','待确认到账','待开通','已完成',...)。
即平台与门店逐单谈价后才进付款 —— 正是"无定价能力"的产物。价格表落地后该状态应废除。
⇒ 服务订单状态机改为 待付款 → 已支付 → 已完成;存量「待确认金额」订单需人工兜底清零(批 E 前置)。

受影响页面清单

页面 现状 处置
payment-confirm(admin + platform 两端) 待确认/已驳回/已确认到账 三态 + 计数 + 人工核对 改造为只读支付记录,新增「来源」列,承载退款标记入口
revenue-ledger 按到账确认生成 触发点改接微信回调(坑 1)
finance 入口卡 文案「查看付款凭证并核对实收金额」 改文案
admin/platform 首页待办 pending_payment_confirmation「待确认到账·待人工核对」 该待办项归零/移除
channel/minicup/store-progress 渠道端可筛「待线下付款/待确认到账/付款已驳回」 渠道端同步改(易漏:渠道也看得到门店付款进度)
store/minicup/opening-result 五态含「凭证已驳回」+ 展示凭证图 状态收敛,凭证图仅存量可查
stores/[orgId]/services(admin/platform/store 三端) 订单状态机 + 席位输入 增购/续费全改(坑 2)
stores/[orgId](admin/platform) 付款步骤条 + 开通后账号概览 双进度 + 支付信息块 + 开通后展示
机构列表(admin/platform) 状态列 付款独立列 + 筛选

五、B 退款标记(全新功能)

全仓 grep 退款 / refund → 命中 0 处,系统当前无退款概念,从零新建:

五、C 存量数据处置矩阵(切换日 T)

原则:老表 payment_vouchers 转只读、数据一条不删;新表 payment_orders 只装微信订单;
支付记录页两表合并展示、以「来源」列区分;切换期人工确认入口限时保留,在途清零后关闭。

存量类型 切换后 处置与展示
已开通(已成交) 不受影响 详情页来源显示「线下转账」,交易号「人工确认·无交易号」,历史凭证图仍可查
待确认到账(已传凭证) 走完老流程 保留人工「确认到账/驳回」入口,清零后关闭
待线下付款(未传凭证) 切新流程 付款入口切微信;展示层改称「待付款」(库值不变)
凭证已驳回 切新流程 驳回态终结,改走微信;原记录只读留存
资料待审核 / 待提交资料 无影响 未进入付款环节
增购/续费在途 分两类 「待确认金额」需人工兜底(该态将废除);「待付款」直接切微信

⚠️ 切换前须跑存量盘点 SQL 统计各态实际条数(批 D 前置动作),上表为处置口径,条数以实际盘点为准。

五、D 门店选席位的位置(长官指出初稿未体现)

席位选择出现在三个入口,都是「选数量 → 服务端算钱 → 微信支付」同一模式:

  1. 首开:进度页付款卡显示原价划线 + 现价(促销信息有价值,长官 8-13 定)→ 点「去支付」→ 支付页 stepper 选数量(1 起,上限 500,与后端 Field(ge=1, le=500) 同口径)→ 实时显示 ¥2,380 × 2 席 = ¥4,760 → 微信支付
  2. 增购:账号与服务管理页 →「增购席位」弹层 → stepper 选数量 → 单价 × 数量全价(不折算,见 §五G) → 立即支付
  3. 续费:同上,按现有席位数续 1 年

落库:门店所选数量写 purchase_seat_count(决定计费);平台开通时 activated_seat_count 默认带出该值、可调整(决定实际开通席位)。

五、E 资金链改造(2026-08-13 拍板 · 坑 1 的真实体量)

「确认到账」不是一个待办动作,是整条资金链的唯一发动机(payment_confirmation.py:190-250),做 7 件事:

1. 建 PaymentConfirmation(确认金额/人/时间)
2. 锁两级渠道 _locked_revenue_channels
3. 按订单类型取分成比例 —— 三个独立列:
     首签 rate_percent / 增购 expansion_rate_percent / 续费 renewal_rate_percent
4. 算两级渠道分成 source_channel_amount / channel_amount
5. 建 RevenueSnapshot 收益快照,冻结当时比例 rate_percent_snapshot
6. 归入结算期 period_month / period_key / period_start  ← 决定进哪个月结算单
7. 推进订单状态到「待开通」

⇒ 微信回调必须整段承接这 7 步,一步不能漏。漏 5/6 步则渠道分成与月度结算单直接断。属资金逻辑,批 B 必含。

已拍板(2026-08-13)

# 议题 拍板
1 退款冲销收益 快照未进结算单 → 作废;已进未打款 → 负数冲销快照;已打款给渠道 → 只做标记,年底统计时不计入收入(不追讨、不冲抵)
2 分成比例快照时点 支付成功那一刻锁定对应比例;取消 24h 锁价设计(不做价格锁定)
3 结算期归属 沿用原口径:原按确认到账时间归月 → 微信支付按支付成功时间归月
4 门店侧发票 线下开,系统不管(现有发票流程是渠道开给平台,与门店付款无关,不动)
5 增购计价 全价不折算(原"按剩余天数折算"经 D8 模型查证后作废,见 §五G)
6 商品名 「智脑」统一改称 「赵阳智脑」

✅ 待拍板项已全部清零(续费顺延口径 2026-08-13 查证结案,见 §五I)。

五、F 列改动纪律(长官令 · 永久生效)

PC 端功能列表不得随意增删列;确有改动必须在图上标记并说明原因。

本轮自查发现并修正的违规:

表 我的违规 修正
机构列表 擅自删除「二级渠道」「手机号」,凭空加「提交时间」 恢复原 7 列,仅新增「付款」1 列并标注
支付记录 擅自删除「付款单」「一级渠道」「二级渠道」「购买席位」,加「业务」「金额」等 恢复原 9 列,保持 9 列不增不减

支付记录最终改动(共 3 处,均已在图上标注):
1. 原「提交时间」+「到账确认时间」两列合并为「支付时间」 —— 微信实时到账,两时间点合一;存量数据原提交/确认时间移入「查看凭证」查看
2. 新增「来源」列 —— 区分微信支付与存量线下转账,否则两类数据无法分辨
3. 原「核对状态」改名「支付状态」 —— 人工核对动作退役,语义已变

检索条件:原按提交/确认时间检索 → 改为按支付时间检索。
金额不入列(原表即如此),仍在详情弹窗展示。

五、G 增购计价推翻重定 —— D8 首登激活模型(2026-08-13 查证后纠正)

🔴 我此前的「按剩余天数/365 折算」方案已作废。查证结论:

seat_overview.py:4    未激活(D8:真实到期为空,首登才起算)
seat_overview.py:46   有额度 → 「未激活」:额度还没开始烧,首登才起算
seat_overview.py:105  D8 首登激活模型下每个席位从各自首登起算一年,到期日天然各不相同
seat_overview.py:112  全未激活 → 前端显示"未激活(首登后起算)",绝不摆假日期

每个席位从自己首次登录当天起算 365 天,机构层面根本没有统一服务期。
⇒ 增购 = 单价 × 数量,全价,不折算;新席位买的就是完整 365 天额度。
⇒ 全仓亦无任何按天折算的计价代码(service_orders.py:376 的 service_days 只是补建首开单时记录时长,非计价)。

单位口径:一律写「/ 席 · 365 天」,不写「/ 席 · 年」——写"年"会让人误以为跨自然年清零。

五、H 交互决策(2026-08-13 长官逐条定)

# 议题 决策
1 进度页要不要显示单价 显示原价划线 + 现价(8-13 修正:促销信息对转化有价值)。两处均取自 service_prices,同源不会漂移
2 「服务付款」文案 改 「服务费用」(付款是动作、费用是对象,卡片标题用名词)
3 支付页数量选择器过大 数字 26px→20px,数量与合计并排同一行,省一整块高度
4 「支付成功即到账,无需上传凭证」小字 删(微信支付本就如此,属废话);改为提示 365 天额度与首登起算
5 支付按钮文案 「立即支付」(不带金额)。金额在按钮正上方的合计已展示,按钮再写一遍是重复;不写"微信"——点了自然拉起,将来加其他支付方式不必改文案
6 价格管理 不加「计价说明」列(无实际用途);单价列直接写 ¥2,380.00 / 席 · 365 天;「最近改价」列只显示时间+操作人,价格变化过程进改价弹窗;「新增服务项目」按钮本期不做(只有一个商品,无新增场景)
7 存量切换器 取消。切换前先把在途凭证人工跑完(跑成已开通),切换后只剩「历史线下只读 + 微信新单」两类,无中间态
8 标记退款位置 不放列表(危险且不可撤销)。列表操作列维持现网单按钮,命名 「查看详情」;弹窗一页承载:支付信息 / 凭证(仅线下单)/ 退款处理 / 退款留痕
9 开通后展示(已出图) 现网「账号与服务管理」已有四块(手机号验证/服务概况/进行中的申请/历史服务记录),不新增区块:服务概况补席位到期状态(未激活按现网铁律显示「未激活(首登后起算)」),历史服务记录每单补支付方式
10 手机端机构卡片 照现网结构不改版:状态胶囊右上角绝对定位、联系人+手机一行、归因渠道、所属一级;仅在下方新增付款状态胶囊

五、I 续费顺延口径(2026-08-13 查证结案 · 最后一项待拍板已清)

长官令:续费要顺延。查证结论 —— 系统现状已顺延,无需改造。

seat_provisioning_executor.py:89   执行一笔待开通订单(增购=分趟建号 / 续费=分趟延期)
seat_provisioning_executor.py:123  action = "创建" if 增购 else "续费"
seat_provisioning_executor.py:126  seat_ids = order.renewal_seat_user_ids  ← 按指定席位逐个延期

续费经核心接口对指定席位逐个延期,本就顺延。

🔴 但有一条红线:档案表三个字段是脏的,新代码禁止读

seat_overview.py:100 原文:

activated_seat_count 只在首开写一次(增购不累加)、service_end_at 续费不顺延、status 无人置"已到期"
——三个档案值当展示都会说谎。席位数据可用时一律以接口 1 实时席位派生;核心不可达时回退档案值(绝不编数)

即 org_signup_profiles 上的 activated_seat_count / service_end_at / status(服务期语义)
是已知失真的历史遗留冗余字段,展示层早已绕开、改用实时席位数据派生。

批 E 实施红线:算续费金额、判到期、算席位数时
一律走实时席位接口,禁止图省事读 profile.service_end_at —— 会拿到永远停在首开那天的过期值。
(activated_seat_count 例外:仅在"开通时写入席位数"这一处使用,见 §五E。)

计价:续费同样按 D8 模型 —— 单价 × 续费席位数,每席续 365 天额度,全价不折算(与增购同口径,见 §五G)。

五、J 席位三态与续费额度累加(2026-08-13 查证 · 批 E 硬约束)

长官口径:「增购只有首登后才开始算时间」「续费支付后自动累加时间」。查证后系统实现如下:

席位有三种,不是两种(seat_overview.py:44-58)

类型 判据 计时行为 能否续费
未激活 service_end_at 空 + 有 seat_valid_days 额度未开始烧,首次登录当天才起算 ✅ 可以,额度累加(365+365=730 天额度,仍等首登)
已激活 service_end_at 有值 已从首登起算 ✅ 到期日顺延
🔴 永久席位 service_end_at 空 + 无 seat_valid_days 永久有效 ❌ 核心侧对其续费会整单返 400

⇒ 长官两句口径均已由系统实现:增购新席位=发放 365 天额度、首登起算;续费=额度累加或到期顺延。无需改造。

🔴 批 E 硬约束:永久席位必须拦在支付之前

seat_overview.py:51-53 原文记录了已发生过的事故:

此前一律标「未激活」,永久席位混在续费勾选里,用户勾了才炸——而续费是先付款后开通,
撞上时钱已经收了、订单只能人工改单。核心已实测确认两者会同机构混存(org5=8永久+9带额度)

现状(线下付款 + 人工确认到账)尚有人工拦截余地;切微信后实时到账,将退化为"秒收钱秒失败",
且退款已定为走线下人工,代价更高。

约束(批 E 必做,两道):
1. 前端:续费选席位列表中,永久席位置灰不可勾选,并标注「永久有效,无需续费」
2. 服务端:下单建 payment_orders 前再校验一次所选席位无永久席位,命中则拒绝建单
—— 绝不能等到支付成功后才发现

判据只有 seat_valid_days 一个字段能区分永久与未激活(两者 service_end_at 都是 null),
校验必须读该字段,不能只看 service_end_at 是否为空。

T.3 文案红线:不对客承诺时效(2026-08-13 长官指出)

原图写「平台审核中,一般 1 个工作日内完成」—— 这是我自己加的,必须撤:

  1. 对客时效承诺做不到就是打脸,且审核快慢不由系统决定,写死在界面上等于给平台上枷锁
  2. 现网原话本就不承诺(review/page.tsx:399):「资料已提交,请等待平台处理。」

定稿:资料已提交 / 请等待平台处理 · 提交于 2026-08-13 14:02
—— 说清"已收到 + 在等 + 什么时候交的",不说"多久好"。

通用红线:门店端文案一律不出现具体时长承诺(X 个工作日 / X 小时内)。

五、U 定稿后自检发现的问题(2026-08-13 · 红军自审)

方案冻结后做全量自检,抓出 4 处内部矛盾(已修)+ 2 个实施盲点(新增待办)。

U.1 内部口径矛盾 —— 已修 4 处

# 位置 矛盾 修正
1 §五D 增购流程 仍写「按剩余月折算金额(计价规则待拍板)」,与已拍板的「全价不折算」冲突 改为「单价 × 数量全价(不折算)」
2 §五D 首开流程 写「进度页显示 ¥2,380/席·年」,与「进度页不显价」及「365 天」口径双重冲突 改为「进度页不显价,价格只在支付页出现一处」
3 §八 自检记录 仍写「进付款页锁价、订单 24h 有效」,该设计已取消 标注已取消,改为「支付成功那刻锁单价与分成比例」
4 §六 计价表 表头「建议规则(待拍板)」+ 首开行「服务期 1 年」 改为「已拍板」+ 首开同 D8 口径(每席 365 天、首登起算)

教训:同一事实在长文档里散落多处,改了一处必须全文回扫。这 4 处若不修,实施会照着写出与拍板相反的逻辑。

U.2 🔴 盲点一:订单号生成并发不安全(资金链隐患)

现网 service_orders.py:126:

async def _next_order_no(session):
    count = await session.scalar(select(func.count())...where(order_no.like(f"{prefix}%")))
    return f"{prefix}{int(count or 0) + 1:03d}"        # COUNT(*) + 1

问题:COUNT(*)+1 无锁、无唯一约束兜底。现状是平台人工逐单操作,天然串行,所以从未暴露;
切微信支付后变成门店自助并发下单,两个门店同秒下单必然拿到同一个 order_no。

影响:微信侧 out_trade_no 要求商户内唯一 —— 撞号会导致下单失败,或更糟:第二笔支付被微信认为是第一笔的重复请求,资金对账错乱。

建议(批 B 前置):payment_orders.out_trade_no 加唯一索引,生成改用「日期 + 序列/雪花/UUID 短码」,
或复用 PostgreSQL SEQUENCE;不沿用 COUNT(*)+1。

U.3 🟠 盲点二:非微信环境无 UA 检测

全仓 grep MicroMessenger / isWechat / 请在微信中打开 → 命中 0 处,现网门店端没有任何环境判断。

影响:JSAPI 只能在微信内调起。门店若在系统浏览器打开签约链接(转发到备忘录、复制到 Safari 等),
点「立即支付」会静默失败或报错,门店不知所措。

建议(批 C 一并做):支付页加 UA 检测,非微信环境显示「请在微信中打开本页面完成支付」并隐藏支付按钮,
不调必然失败的 JSAPI(dev1 调研 §8 也提过此项,本方案原文未落实)。

U.4 自检后长官逐条纠正(2026-08-13)

# 我自检时的写法 长官纠正 已改
1 增购计价改「全价不折算」 ✅ 对 —— 增购起步就是一年 365 天,折算反而不对 保持
2 进度页付款卡不显价 ❌ 错 —— 原价/现价该显示就显示(促销信息有价值) 进度页补上原价划线 + 现价
3 订单 24h 有效 ❌ 错 —— 订单不设有效期:门店隔天回来照样要能付,过期反而挡住成交 「锁价 + 24h 过期」整体取消
4 计价表头「待拍板」 ✅ 已过期,改掉 改为「已拍板」

价格一致性说明:进度页与支付页两处价格同取 service_prices,单一数据源,不会漂移。

五、V 两个盲点的处理方案(可直接实施)

V.1 订单号并发 —— 用数据库序列,不用查询-再-加一

现网两处都是同一个反模式(都靠"先查再加一",无锁):

位置 写法 唯一约束
service_orders.py:126 _next_order_no COUNT(*) + 1 ❌ 无
store_signup.py:533 _next_payment_no ORDER BY DESC LIMIT 1 取最大再 +1 ✅ 有(models:336 payment_no unique)

payment_no 靠唯一约束兜住了 —— 撞号会 IntegrityError 报错,不会写脏数据,但会失败;
order_no 连约束都没有,撞了直接产生重复单号。人工节奏下都没暴露,自助并发下必现。

做法(批 B 前置,三步):

  1. 建序列(migration 内):
    sql CREATE SEQUENCE IF NOT EXISTS payment_order_seq;
  2. 取号改为原子递增,不再查表:
    python seq = await session.scalar(text("SELECT nextval('payment_order_seq')")) out_trade_no = f"PY{datetime.now(TZ):%Y%m%d}{seq:08d}" # 商户内唯一

    nextval() 是 PostgreSQL 原子操作,并发安全;且不回滚(事务失败也不会重号),正合支付场景。

  3. 唯一约束兜底:payment_orders.out_trade_no 加 unique=True;
    落库遇 IntegrityError → 重取号重试一次,仍失败则报错,绝不静默吞掉。

⚠️ 存量两个函数本期不动(线下凭证链将退役);但若批 E 前 order_no 仍在用,
建议顺手加唯一约束,至少让撞号"报错"而不是"写脏"。

V.2 非微信环境 —— 抄核心侧现成实现

核心侧已有可直接复用的函数(web/app/h5/chat/lib/wx-oauth.ts:22):

/** 是否在微信内置浏览器 */
export function isInWeChat(): boolean {
  if (typeof navigator === "undefined") return false;
  return /MicroMessenger/i.test(navigator.userAgent);
}

做法(批 C,两处):

  1. 支付页进入时判断:非微信环境 → 隐藏「立即支付」按钮,改显提示卡

    「请在微信中打开本页面完成支付」+ 引导:复制链接 → 微信内粘贴打开

    不调必然失败的 JSAPI(JSAPI 只能在微信内调起,外部浏览器点了只会静默失败或报错)

  2. 进度页「去支付」按钮同样判断:非微信环境点击时给同样的提示,别让门店走到支付页才发现

三处口径统一:settlement/web 侧新建一份同名 isInWeChat()(不跨仓 import 核心),
正则与核心一致 /MicroMessenger/i,避免两套判断漂移。

五、W 授权时机与修改资料展开态(2026-08-13 长官定)

W.1 微信授权提前到入口(长官定)

现网:签约页 grep oauth / 授权 / openid / snsapi → 0 处,门店凭签约 token 直接进(temp-exchange 换临时会话),全程不授权。

长官定:扫码进来先授权,再填资料。理由:openid 早拿到,后续支付直接用,不必等到付款时再中断一次。

扫码 → 【授权落地页】点「授权并开始填写资料」→ 规范发起 OAuth
     → 资料页(openid 已存库)→ 填资料/提交/修改
     → 点「去支付」→ 直接用已存 openid 下单,无需二次授权 ✅

🔴 必须是「用户点击才发起」,不能页面加载自动跳 —— 这不是体验取舍,是拿不到真 openid:
微信 2022-07-12 起对「无用户交互自动发起 snsapi_userinfo」打开快照页模式,
返回的 openid/unionid 全是虚拟账号数据;拿它下单,微信侧对不上、或订单绑到假身份。项目已踩过一次。

⇒ 加一个授权落地页,样式与文案照抄智脑现网那页(web/app/h5/chat/components/seat-wx-auth-landing.tsx,含同目录测试
「挂载时不触发 onStart(禁止自动发起授权 · 快照页红线)」):

整屏居中 · 浅绿渐变背景
  ✓ 图标(圆形浅绿底)
  赵阳智脑官方授权微信登录
  机构入驻需获取您的微信身份 / 用于付款人身份绑定
  [ 确认 ]                      ← 点击中显示「正在跳转微信授权…」
  点击后将跳转微信授权页确认

页面不解释任何机制(不提 openid、不提快照页),门店点一下「确认」即可;
授权成功后自动跳转资料填写页,无需再点。

落库:授权拿到的 openid/unionid/昵称随签约档案存下,支付下单时直接取,不再二次授权。

W.2 点「修改资料」后,服务费用区隐藏

现网机制(review/page.tsx:774 注释):点「修改资料」后本卡隐藏、由表单卡接管,是同页展开、不是弹窗。

问题:本期下方新增了「服务费用」卡,表单展开后页面变长,门店可能滚不到、以为付款入口没了。

长官定:点「修改资料」后「服务费用」区整块隐藏,页面只剩表单,专注填写;
点「提交修改」或「取消」后自动恢复显示。(比"收成一行"更简洁,长官 8-13 定)

五、X 授权前置引出的三个连锁盲点(2026-08-13 二次自检)

授权提前到入口后,全量自检发现 3 处未覆盖,都会导致「拿不到 openid → 支付断掉」:

X.1 🔴 复核链接是独立入口,会绕过授权

stores.py:1385 生成的复核链接是独立一条:/store/minicup/review?rt=<review_token>,
平台可单独发给门店。门店从这条直接进时,根本不经过签约页 → 没授权 → 无 openid → 点「去支付」必断。

解法:授权判断不绑在签约页,绑在「当前会话有没有本人 openid」上(会话级,非档案级 —— 与 §五Y.3 同一口径)——
签约页与复核页都做同一个前置检查:会话无 openid → 先进授权落地页 → 授权成功回跳原页。
即:授权是全流程的入口守卫,不是签约页专属的一屏。

说明:复核链接由平台单独发给该门店本人(如驳回后通知其修改)是正常业务路径,必须支持;
与 §五Y.3 禁止的「转给他人代付」是两回事 —— 前者是本人换入口,后者是他人代办。

X.2 🟠 用户拒绝授权 / 授权失败没有出路

微信授权页用户可以点「取消」,也可能因 appid 配置错、网络问题失败。现方案未定义此分支。

解法:回跳后未拿到 code → 停在授权落地页,显示
「未完成授权,无法继续入驻。[重新授权]」,按钮可重试。
绝不静默放行进资料页 —— 否则门店填完一整套资料,到支付才发现没身份,白填。

核心侧现成实现已考虑:seat-wx-auth-landing.tsx 注释「onStart resolve 且未发生页面跳转(如 appId 拉取失败)→ 按钮恢复可点,允许重试」。

X.3 🟠 同一门店换人打开链接,openid 会不一致

签约链接可转发。老板授权后填了资料,若店员用自己微信打开复核链接并支付,
则档案里存的 openid 与实际付款人 openid 不同。

判断:这不算 bug,但要明确口径 —— 支付订单记录的是本次付款人的 openid(微信回调里带的),
用于对账与退款溯源;档案里那个只是首次授权人,两者不必强制一致。

落地要求:
- 支付订单表存本次付款人 openid(回调 payer.openid),不复用档案里的
- 支付详情弹窗展示的「付款微信」取订单表,不取档案 —— 否则会显示错人
- 档案 openid 仅用于免二次授权(有就直接下单),不作为身份校验依据

五、Y 三端授权与换人授权(2026-08-13 长官定)

Y.1 手机 / 平板 / PC 微信 —— 三端都必须授权,不给绕过

长官定:门店可能在手机微信、iPad 微信、Windows/Mac 电脑微信里打开链接,三端一律强制要求微信授权,
不设任何跳过路径(不是"限制某端不能用",而是"三端都必须先授权")。涉及支付,付款人身份必须落实。

查证(项目已有记录,非推测):
- PC 微信能走 OAuth 授权 —— 核心侧注释明写「首次授权 + 会话链接入口(PC 微信唯一入口)必踩(快照页)」
(web/app/h5/chat/page.tsx:937、seat-wx-auth-landing.tsx:6),说明 PC 微信内可正常发起并回跳授权
- 三端 UA 特征项目已有现成判定(tablet-wechat-voice.spec.tsx:53-58):
手机 / 平板:MicroMessenger/8.0.60 + Mobile 电脑端 :MicroMessenger/8.0 WindowsWechat / MicroMessenger/8.0 MacWechat
⇒ 三端 UA 均含 MicroMessenger,isInWeChat() 判定天然覆盖三端,无需分端特判。

🔴 但必须留一个待验证项:JSAPI 官方文档(产品介绍-4012062524.md)只写「微信客户端内部浏览器」,
未明确 PC 端微信是否支持调起 JSAPI 支付,知识库中亦无 PC 端专述。
⇒ 列为批 B 联调必测项:拿到商户号后,在 Windows 微信内实测 0.01 元下单能否拉起支付键盘。
若 PC 端不支持,退路是提示门店改用手机微信打开(不是放弃授权)。

Y.2 拒绝授权 = 不放行(长官定)

流程:落地页点「确认」→ 跳微信授权页 → 若用户在微信侧点「取消」 → 回跳我方页面但无 code
→ 停在授权落地页,不给进资料页,显示:
「需完成微信授权后才能填写机构资料」+ [重新授权] 按钮。
不做任何降级放行 —— 涉及支付,没有身份就不允许往下走。

Y.3 一人一档、各填各的(长官定 · 业务隔离规则)

规则:签约链接可以分享给多人,但每个人打开都是独立一份签约 ——
该授权授权、该填资料填资料、该自己付款自己付款。不允许 A 提交资料、B 来付款。

现网机制天然支持(查证 store_signup.py:236-256):签约链接 token 里只有 channel_id(渠道),
不绑门店;每次打开→填写→提交,都会新建一份独立门店档案。渠道把同一条链接发给两家门店,两家各建各的档,互不干扰。

落地要求:
- 复核链接(/store/minicup/review?rt=)不作为转发付款入口 ——
它是门店本人的进度页;本期不设计任何"转给他人代付"的路径
- 授权判定基于当前会话是否已有本人 openid:同一人换设备/重开 → 重新授权后回到自己那份档案的进度
- 付款人以实际支付那次的 payer.openid 为准(微信回调携带),用于对账与退款溯源
- 档案 openid 作首次授权人留档

Y.3.1 签约链接有效期 —— 永久有效(查证澄清)

⚠️ 此前表述易误导,特此澄清:temp-exchange 换出的 120 分钟是本次填写的登录态,
不是链接有效期。

generate_store_signup_token()  → channel_id + HMAC 签名
parse_store_signup_token()     → 只验签名,无任何时间校验

⇒ 签约链接本身永久有效(只要渠道存在且开放签约)。门店隔数小时、数天再打开照常可用;
仅当单次填写超过 120 分钟,临时会话过期,重新点链接即可,不影响链接可用性。

Y.4 只做 JSAPI(长官再确认)

本期只做 JSAPI,不做 H5 支付、不做 Native 扫码。非微信环境仅提示「请在微信中打开」。
其他支付方式后续再议。

五、Z 验收门禁:微信支付 Skill 接入质量评估(长官令 · 2026-08-13)

长官要求:支付代码写完,必须跑一遍官方 Skill 的「接入质量评估」,🔴🟠 项清零才提验收。

Skill 位置:~/.claude/skills/wechatpay-payment-integration/(官方包 · 知识库 4122 篇)
评估能力:Skill 能力 3「接入质量评估」——
以金融支付专家视角扫描接入代码,覆盖安全合规、资金链路、开发时业务常见质量问题,按 🔴🟡🟠 分级输出。

门禁规则(批 B / C / E 完工前各跑一次):

级别 处置
🔴 致命 必须修,清零才允许提验收
🟠 重要 必须修,清零才允许提验收
🟡 建议 逐条评估,不修的须写明理由

其余使用原则(长官定):
- 照 Skill 原样跑,不做预防性降级;哪一步跑不通(sync / CLI / 接口)如实报错上报,说清哪一步、什么报错、影响什么,等指令,不自行绕路、不硬凑答案
- 产品选型用它复核(门店 H5 在微信内打开 → JSAPI,已复核确认,见 §五N)
- 后端下单 / 回调 / 退款实现照它的官方示例代码写,不凭记忆生成接口与字段

⚠️ 当前状态:Skill 本体已装、知识库可检索可溯源;
wechatpay-dev-cli 因 npm 全局目录权限未装成(/usr/lib/node_modules 属 root),已上报长官转 root 处理。
CLI 装好前,知识检索改用本地文档直读(能溯源,但用不了语义检索)。

五、AA 危险操作留痕(长官指出 · 自检补齐)

长官指出:退款、改价这些危险动作,谁改的、谁退的都要记账号。核对结果:

操作 留痕字段 状态
标记退款 payment_orders.refunded_by / refunded_at / refund_note ✅ 原方案已有
改价 🔴 原方案只有 updated_by/at(覆盖式,只留最后一次) ✅ 已补独立表 service_price_changes
开通账号 activated_seat_count + 现网 AuditLog ✅ 沿用现网
资料审核 reviewed_by / reviewed_at(现网既有) ✅ 不动

🔴 补齐点:改价必须独立历史表

原方案把改价留痕写成一句括号备注「(价格历史行或日志表)」,没定义表;
而效果图的「改价记录」弹窗要列历次改价 —— 图上有、表里没有,实施照表建库就做不出那个弹窗。

service_price_changes
  id / price_id / old_price / new_price / changed_by / changed_at / remark(可空)

一改一行,永不覆盖。理由:订单按 price_snapshot 结算,事后对账要追溯"某笔订单成交时单价多少、谁改的"。

与现网留痕体系对齐

现网已有完整审计体系(models/settlement.py:12-14):
- 6 处 *_by 字段:reviewed_by / contract_recorded_by / actor_user_id / confirmed_by / reviewed_by(Invoice) / paid_by
—— 均为 Integer FK users.id
- AuditLog 表:actor_id(Integer,无 FK,与 DAB 现有 audit 表风格一致)

⇒ 新表的 refunded_by / changed_by 一律沿用 Integer FK users.id,与现网同构;
危险操作(标记退款 / 改价 / 开通)同时写一条 AuditLog,与现网 _audit_store 做法一致。

五、N 微信支付接入(合并 dev1 调研 + 长官拍板 · 2026-08-13)

来源:dev1《微信支付接入·调研交接文档》(2026-08-12),其 12 维物理证据我抽验 3 条全部属实
(二维码微信内长按识别 / exchange_code 现成 / 快照页红线代码在),可直接采信。

N.1 采纳 dev1 的结论(并修正我方案的错误)

项 结论
接口选型 只做 JSAPI 支付(APIv3)。🔴 修正我方案「JSAPI + H5 双通道」的错误 —— H5 支付在微信内根本不可用,而入口是渠道二维码在微信里长按识别(store-signup-link/page.tsx:200 QRCodeSVG + 微信长按保存),场景恒为微信内。非微信环境做 UA 检测提示「请在微信中打开」即可
落地位置 settlement/ 独立子项目(backend:8002 / web:3001 / 独立 DB / 独立 alembic),不落核心 app/
SDK wechatpayv3(无官方 Python SDK)。dev1 已真打:云机可装,密码学 PoC 三段链路全通(签名 / AES-256-GCM 解密 / SDK 实例化)
openid 补法 settlement 拿 code → 调核心内部端点换 openid(复用 wechat_oauth_service.exchange_code,走 CORE_API_BASE + SETTLEMENT_SERVICE_TOKEN),公众号 appsecret 不扩散到第二个服务
金额单位 微信 amount.total = 分(int);结算域是 Numeric(12,2) 元 → 支付表存分,换算边界写进字段 comment
无沙箱 APIv3 无仿真环境,验收只能 test 环境 0.01 元真实交易,云机不可能跑通

N.2 dev1 的坑清单(全部采纳,写入实施红线)

  1. 🔴 快照页红线:支付页绝不能页面加载自动跳授权 —— 微信 2022-07-12 起对「无用户交互自动发起 snsapi_userinfo」打开快照页模式,拿到的 openid/unionid 全是虚拟账号数据,订单会绑到假身份。必须用户点按钮才发起,抄现网 web/app/h5/chat/components/seat-wx-auth-landing.tsx 及其测试用例

    ⚠️ 长官提到「智脑手机端登录页一打开就微信授权」—— 查证:现网不是这样,正是因为这条红线才改成点击式。支付页同理

  2. WeixinJSBridge 时序:必须监听 WeixinJSBridgeReady 再调,否则首次进页拉不起支付(全库 0 处,纯新写)
  3. 前端 err_msg 不可信:只能触发轮询,绝不能用它改订单状态
  4. 回调幂等:微信收不到 200 会反复重推,范式抄 app/models/settlement_idempotency.py
  5. 状态来源三选:回调(权威·主)+ 查单(兜底)+ 前端轮询我方端点;trade_state 只需处理 SUCCESS/NOTPAY/CLOSED/REFUND

N.3 与我方案冲突处的裁定(以长官拍板为准)

# dev1 建议 裁定
支付时点 建议维持「先审后付」 ❌ 以长官拍板为准:资料审核 ∥ 付款并行。副作用:可能出现「已付款但资料被驳回」→ 退款成硬需求,但已定线下退+系统标记,无需接微信退款 API
线下通道 待定 ❌ 全部去掉(长官已拍)
参数 A 单行表 settlement_billing_params 只存一个 signup_fee ❌ 装不下。以本方案 service_prices 为准(单价 × 数量、覆盖首开/增购/续费、含改价留痕)
成功页显示什么 A 微信昵称 / B 门店名 二选一 ✅ 两个都显示(长官定):「付款微信:明眼小赵 / 杭州明眸视光中心 · 赵阳智脑 × 2 席」→ 需 snsapi_userinfo scope
二维码支付 — ❌ 不做(长官定):直接拉起微信支付,不放二维码手动扫

N.4 支付人身份的展示口径(长官令 · 三处都要)

微信支付无付款凭证,付款人身份改由 微信昵称 + openid 记录,替代原「付款方名称」字段。以下三处均须展示:

🔴 渠道展示特殊规则(payment-confirm/page.tsx:201-205):channel_type === "platform_direct_secondary"(平台直签二级)时,
「一级渠道」标签要变成 「渠道归属」,且现网注释 :1000 明确 不可与二级渠道合并展示。新页面必须沿用此规则。

N.5 商户材料(13 项 · dev1 已交长官,等提供)

密钥类:mchid / apiclient_key.pem / 证书序列号 / APIv3 密钥 / 平台公钥+公钥 ID
配置类:AppID 与商户号绑定 / JSAPI 已开通 / 支付授权目录 / 公众平台网页授权域名(最多 2 个,直接决定 test 能否验收) / 确认是已认证服务号
辅助:商户主体名称 / 退款权限 / 可小额真付的微信号(0.01 元验收)

私钥与 APIv3 密钥不进聊天、不进仓库,放 ~/secrets/wechatpay/,只进 settlement/.env,仓库留 .env.example 占位。

N.6 发版链(长官问:走我们现在的仓有问题吗)

没问题,走本仓即可。 判据:本会话 8-13 已用本仓流程发过 4 个 MR(#381~384),改的正是 settlement/web 与
settlement/backend,sudo release-to-test → settle-test 流水线 run#131/132 SUCCESS,已验证 settlement/ 目录的改动走本仓 MR 可正常构建部署。
dev1 §12.4 的疑虑(结算文档写"独立仓自主发版")属文档陈旧,实际链路已收口到本仓。
⚠️ 唯一注意:改 settlement/ 要看 settle-test 流水线,不是 test。

六、增购/续费一块切(🔴 含必须先拍的计价规则)

现状:增购/续费与首开共用凭证机制(store_service.py / service_orders.py / 门店服务页上传凭证),金额人工谈、门店手填 → 系统从不需要计价规则。切微信后服务端必须自己算钱。规则已于 8-13 拍板:

业务 计价规则(已拍板 · 8-13) 依据
首开 单价 × 席位数,每席 365 天额度、首登起算 与增购/续费同一 D8 口径
续费 单价 × 续费席位数,每席续 365 天,全价不折算(已拍板) 系统现状已顺延(seat_provisioning_executor.py:126 按席位逐个延期);红线见 §五I
增购 单价 × 新增席位,全价不折算(D8 首登激活模型:每席独立 365 天额度,首登起算) 见 §五G

增购/续费支付成功后自动生效(无资料要审,钱到即加席/顺延)—— 与首开的"手动开通"不同,因为首开的人工闸门是给资质审核兜底的,增购/续费没有这个前提。此条如长官有不同意见可改。

七、批次(原"批 1"作废)

原批 1(解除付款前置 + 付款人自动取执照名)作废 —— 那是给线下凭证续命的。

批 内容 依赖
A service_prices 表(含改价留痕)+ PC 价格管理页(列表 + 编辑弹窗 + 改价记录) 无,随时可开工
B 微信支付接入(仅 JSAPI)+ payment_orders 表 + 下单/回调/查单
🔴 前置:out_trade_no 用 PostgreSQL 序列 + 唯一约束(§五V.1)
🔴 必含:资金链 7 步整段移植进回调(§五E)
🔴 联调必测:PC 端微信能否调起 JSAPI(官方文档未明述 · §五Y.1)
商户 13 项材料(长官办)+ A
C 微信授权入口守卫(签约页 + 复核页共用 · §五W.1/§五X.1)+ 门店进度页双卡片 + 新支付页 + 支付成功态
🟠 必含:非微信环境 UA 检测(§五V.2)· 授权失败重试(§五X.2)· 三端授权覆盖(手机/平板/PC 微信 · §五Y.1)
B
D 列表/筛选双端改造(付款列 + 筛选项改名)+ 收款管理改支付记录 + 退款标记弹窗
+ 存量在途人工跑完 + 凭证链退役(含渠道端 store-progress)
C
E 增购/续费切微信 + 支付后自动生效 + 流程简化(废「待确认金额」态)
+ 平台侧账号密码查看入口(PC + 平台手机端 · §五Q.1)
🔴 必含:永久席位不可续费的前端置灰 + 服务端二次校验(§五J)
B(计价规则已定)

存量:生产 6 家已开通不涉及;test 在途「待线下付款 / 待确认到账 / 待确认金额」数据
在切换前人工跑完(长官定),不设切换器。

八、红军自检记录(v2 相对讨论稿修正 4 处)

  1. 增购/续费计价规则缺失 —— "一块切"逼出(线下手填金额时代不需要规则)→ §六给建议规则并列为批 E 前置拍板项
  2. 状态列表漏"待付款" —— 讨论稿 7 值漏了"资料过了没付钱"(平台最想筛的一类)→ 修正为 8 值,且库值 待线下付款 原地复用零迁移
  3. 重复支付/回调丢失未设计 → §二补:未支付单复用、transaction_id 唯一、回调+查单双保险、双付线下退
  4. 改价撞单时序 → 原设计「进付款页锁价 + 订单 24h 过期」整体取消(长官 8-13 定):不锁价、订单也不过期(隔天回来还能付)。改为支付成功那一刻锁定当时单价与渠道分成比例写入订单快照(见 §五E 拍板 2)

九、待拍板清单(下一步)

  1. ✅ 自检发现的两个盲点已给出可实施方案(见 §五V):订单号改 PostgreSQL 序列 + 唯一约束(批 B 前置);UA 检测抄核心侧 isInWeChat()(批 C)
  2. ✅ 增购/续费计价规则 —— 已定(全价不折算,见 §五G / §五I)
  3. 增购/续费支付后自动生效(云机建议,默认执行,有异议喊停)
  4. 微信商户号 / 公众号资质(后议,批 B 前置,不堵批 A)

附录 · UI 评审调整流水(八轮 · 2026-08-13)

保留全过程供追溯;实施只看正文,附录仅在需要确认"某个细节为何这样定"时查阅。

五、K 二轮细节调整(2026-08-13 长官逐条 · 已全部落图)

# 调整 说明
1 门店端去掉「费用按平台定价自动计算」 属内部话术,不给客户看;改为「选择购买席位数量」
2 数量选择器留足三位数宽度 上限 500 席,数字位加宽到 58px,右侧标注 1–500 席
3 价格管理去掉「计价说明」列 无用;单价直接写 / 席 · 365 天
4 「最近改价」列展示混乱 列上只留「时间 + 操作人」;改价记录改为弹窗内表格(时间/原价/新价/操作人),已出图
5 「新增服务项目」按钮 本期不做 —— 当前仅赵阳智脑一个商品,无新增场景,等真有第二个再加
6 手机卡片过大 付款胶囊由独占一行改为并入联系人行尾,卡片高度回到现网水平
7 退款处理区层次乱 退款方式 / 退款说明各自独立成组,操作按钮右对齐单独一行,风险提示降为脚注
8 ⑧ 节一大堆小字 内部话术全部删除,只留业务说明
9 账号与服务管理没画出来 已补全四块结构图(手机号验证 / 服务概况 / 进行中的申请 / 历史服务记录),标出两处新增:服务概况加席位到期状态、历史服务记录加支付方式

五、L 三轮调整(2026-08-13 · 含两处「查证后发现原图偏离现网」)

# 调整 说明
1 服务费用卡「待支付」与选席位提示冲突 合并为一句「选择购买席位数量后完成支付」,状态胶囊仍保留在标题右侧
2 数量右侧「1–500 席」提示 删(已有输入限制,提示多余,且占手机宽度)
3 改价记录入口 操作列合并为单按钮「改价」,改价记录做在同一弹窗内 —— 拆两个按钮用户不知道记录藏在哪;记录表列名「新价」改 「改后价」
4 手机卡片付款胶囊位置 由「联系人行尾」改为右上角机构状态胶囊正下方,两状态纵向对齐成一列,不挤正文
5 🔴 增购/续费交互(查证纠错) 现网是 CenterSheet 弹层(service/page.tsx:743 申请增购席位 / :856 申请续费席位),不跳页。原图画成卡片内嵌,偏离现网 → 已改为弹层形态
6 🔴 手机端开通账号能否改席位数(查证纠错) 现网本就能改(platform/.../[orgId]/page.tsx:189 默认带出申报值、:893 上限 500 校验),是原图漏画 → 已补席位数选择器
7 支付时间检索格式 恢复现网的开始 — 结束两段式,不自造格式
8 PC 增购金额 由折算价 ¥4,355 更正为全价 ¥4,760(与 §五G 全价不折算口径一致),合计同步 ¥9,520

五、M 四轮调整(2026-08-13)

# 调整 说明
1 时间格式分两档(8-13 补齐) 详情 / 卡片 / 弹窗 → 带年份(2026-08-13 14:02):审核提交时间、开通时间、支付时间、退款时间、改价记录等,共补 10 处。
列表表格内 → 保持现网短日期(08-13 15:12)不加年,与现网一致。
句子中的口语日期("已于 08-12 银行转账退回")不动
2 价格管理操作按钮命名 「改价」→ 「编辑」。叫"改价"会让人以为只能改价、不知道弹窗里还有改价记录;「编辑」涵盖改价 / 上下架 / 查看记录
3 手机卡片付款胶囊位置(定稿) 联系人行尾 ❌(挤正文)→ 状态胶囊正下方 ❌(撞 ChevronRight,钉在 right-4 top-1/2)→ 与状态并排同一行 ❌(机构名被挤断行)→ 落在卡片右下角 ✅:右上角保持现网单个机构状态胶囊、机构名宽度不受影响、右下角是空位不撞小三角
4 PC 端筛选零改动(长官令) 不新增任何筛选框;机构状态多选沿用现网,仅其中三项付款态(待线下付款 / 待确认到账 / 付款已驳回)随新流程退役,替换为 待付款 / 已支付 / 已退款
5 手机端 chips 合并 不拆两组占两块高度,付款态并入「机构状态」一组
6 渠道端 store-progress 现网本就是单列 11 项、且已把三个付款态混在机构状态里 —— 与「合一」口径天然一致,只需同步改选项名称

判据:channel/minicup/store-progress/page.tsx:60-72 现网筛选项清单;
platform/minicup/stores/page.tsx:222/231 状态胶囊与 ChevronRight 的绝对定位坐标。

| 9 | 「刷新状态」按钮 | 🔴 现网 review/page.tsx:655 本就有,原图漏画 → 已补。注意现网细规则:待审核展开表单时不给刷新(会重置正在填的内容),驳回态才给 |

五、O 五轮调整(2026-08-13 · 含三处「原图偏离现网」纠错)

# 调整 说明
1 🔴 按钮位置错(纠错) 现网「修改资料」与「刷新状态」是并排一对、放在资料区内(review/page.tsx:763/779,mobile-action-row),与支付无关。原图把刷新按钮放到支付按钮下方 → 已改:审核中态「修改资料 ‖ 刷新状态」、驳回态「重新提交资料 ‖ 刷新状态」
2 价格展示 加原价划线 + 现价 + 单月换算:~~¥2,880~~ ¥2,380 / 席·365天,副行「约 ¥198 / 月 × 12 个月」
3 支付成功页的出现时机 不是独立页:支付成功停留 2 秒自动回进度页。① 资料未过/审核中/已驳回 → 进度页付款卡显示「已支付」;② 资料已过 → 直接进「等待平台开通账号」
4 🔴 手机端开通席位选择器(纠错) 现网本就能改席位数,图上一度补画后又被覆盖丢失 → 已补回(默认带出购买数量,上限 500)
5 🔴 筛选条件不用动(纠错) 现网支付记录页本就有 6 个筛选:付款单号 / 机构名称 / 付款方名称 / 核对状态 / 开始时间 / 结束时间。时间本就是起止一对,框一个都不用增删,只需改维度名:「核对状态」→「支付状态」、时间维度由到账确认时间→支付时间。原图自造筛选框是错的
6 支付详情弹窗重排 由一坨混排改为三块分明:①支付信息(业务/金额/方式/状态/交易号/时间/付款微信)②机构与渠道(机构/营业执照全称/一二级渠道)③付款凭证(仅线下单)。退款后上述两块原样保留,下方仅追加「退款记录」块
7 付款人身份 微信支付无凭证,「付款微信(昵称 + openid)」替代原「付款方名称」字段位置
8 增购/续费入口 确认为弹层(CenterSheet),非跳页 ✅

五、P 六轮调整(2026-08-13)

# 调整 说明
1 「修改资料」旁补现网小字 照现网 review/page.tsx:775:「资料填写有误?可在平台审核前自行更正。」
2 单价改双行 原价 ~~¥2,880 / 席 · 年~~ 独立一行 + 现价 ¥2,380 / 席 · 年;单位由「365 天」统一改回「年」(对客话术),365 天口径保留在计价说明与实施文档中
3 支付成功图标 改用微信绿 #07c160 圆形白勾,52px
4 手机端开通账号 改为弹层:点「开通账号」→ 弹层内选开通席位数量(默认带出购买数量、上限 500)→「确认开通」;主按钮不再带席位数
5 收款管理页头 照现网:标题「收款管理」+ 副标「付款单处理台」+ 右上角汇总行(付款单 N 笔 · 已支付 x · 已退款 y),对应现网 payment-confirm:380 的 headerSummaryText
6 支付详情弹窗重做 上一版 DOM 塌陷(替换未匹配干净,旧结构残留)→ 整节推倒重写。改用现网 ReadonlyLine 风格:标签左、值右、下划线分隔,分四区:支付信息 / 机构与渠道 / 付款凭证 / 退款处理
7 退款后展示 支付信息与机构渠道两块原样保留,下方仅追加「退款记录」块
8 增购/续费入口 确认为弹层 ✅ 无需再改

✅ 已定(长官 8-13):原价划线纯前端展示,service_prices 不加字段。
实施口径:前端常量(营销话术性质),改动随发版走;服务端下单/计费只认 service_prices.unit_price,
原价不参与任何金额计算、不入订单快照,避免与实付金额产生第二个真值源。

五、Q 七轮调整(2026-08-13)

# 调整 说明
1 🔴 DOM 缺 </div> 修复(根因) ⑦ 节少一个闭合标签,导致它把后续所有节吞进去 —— 此前反馈的「格式乱 / 更丑」根因在此,不是设计问题
2 弹窗恢复单列 两列虽省高度,但字段名与值被拆到左右半屏,眼睛要横跨对应。单列成对贴着更耐读,采纳长官判断
3 弹窗宽度 整页维持 1180 基准,弹窗收窄至 700px 两窗并排(700×2 正好放下)—— 既保成对易读、又能一眼对比「未退款 vs 已退款」。
⚠️ 期间曾用负 margin 强行撑到 1440,导致内容溢出屏幕左侧、字段被切 → 已废弃该做法:不做局部撑破版式
4 增购/续费流程简化 现网三步(提申请 → 平台逐单定价 → 门店付款)→ 简化为门店选数量 → 直接支付 → 自动生效,平台不介入。「待确认金额」态与平台定价入口一并废除
5 平台改价余地 长官定:先不做(后期再说)。当前统一按价格表走
6 增购是否需审 长官定:不需要,买越多越好,无上限
7 存量 待确认金额 订单 长官定:动之前先人工处理完(与支付切换同批做)

Q.1 增购自动开通后的账号密码查看(长官令 · 补真空)

首开的一次性弹层保持现网原样,不动(store/minicup/service/page.tsx:817)。

🔴 真空在增购:现网账号密码只存在于平台点开通那一次的 provisionResult 中
(admin/minicup/stores/[orgId]/services/page.tsx:470 copyProvisionCredentials)。
增购改微信支付自动开通后,平台从未点过开通 → provisionResult 为空 → 账号密码无处可查。

本期补齐:在机构详情 →「账号与服务管理」页的订单列表中,每单增加「查看账号」入口,
PC 与平台手机端两端都要:

五、R 八轮调整(2026-08-13)

# 调整 说明
1 支付详情弹窗改窄条 + 可滚动 宽度收到 560px(窄条更耐读),内容超出时内部纵向滚动(max-height:660px; overflow-y:auto),不再无限拉长
2 查看账号弹层补齐首开同款结构 席位账号 / 临时密码 逐席成对 + 每席「复制」+ 末尾「打开方式」区(原文:关注「瞳伴智能」公众号,根据使用设备选择「平板端」或「手机端」)+ 底部「复制全部账号密码」
3 🔴 端画错了(纠错) 上一版把「查看账号」弹层画在了门店端手机上 —— 门店端本来就有(首开一次性弹层 store/minicup/service/page.tsx:817),不需要新增。
正确位置是平台手机端 · 机构服务管理(platform/minicup/stores/[orgId]/services/page.tsx,现网存在且有 buildSeatCredentialsText、:470 复制能力,同样只在 provisionResult 内)→ 已把该页与弹层补画进 ⑧ 节,现为门店端 / 平台手机端 / PC 三端并列

现网口径沿用(web/lib/seat-credentials-text.ts:9-15):
「打开方式」全文只出现一次、固定放末尾(重复 N 遍反而乱);续费不带「打开方式」(账号早在用,引导多余)。

R.1 复制文本格式(现网唯一事实源 · 一字不改)

长官令:单个复制与复制全部,内容都要带「公司」与「打开方式」,账号按 账号1 / 账号2 编号。
查证:该格式在现网已有全站唯一事实源 settlement/web/lib/seat-credentials-text.ts
(2026-07-28 长官定稿),注释明确 5 个复制入口全部调这里
(PC 机构详情 / PC 服务页 / 平台手机端机构详情 / 平台手机端服务页 / 门店 H5),本期新增入口一并复用,不另写。

公司:温州瞳趣视光门诊有限公司
账号1:store_wz003
临时密码:Tb#7k92m

账号2:store_wz004
临时密码:Tb#3x81p

打开方式:关注“瞳伴智能”公众号,根据使用设备选择“平板端”或“手机端”

格式规矩三条(源码注释原文):
1. 单账号一字不变(长官现在就这么发的,不许改);多账号才加序号,账号组之间空行分隔
2. 「打开方式」全文只出现一次、固定放末尾 —— 重复 N 遍反而乱
3. 续费不带「打开方式」,且密码行为 临时密码:不变(续费不修改密码)

其他细节:公司名取 license_company_name,空则回退招牌名,两者都空不输出「公司」行、绝不留空行;
密码缺失兜底为 未携带,请联系工作人员重置;冒号统一半角(对齐长官现用微信格式)。

五、S 批注与 UI 文案的区分(2026-08-13 长官指出)

长官问:「平台直签二级渠道时『一级渠道』标签变为『渠道归属』…」这句灰字会不会显示在系统里?
答:不会,那是给实施看的批注。 但原图把批注与真 UI 文案混在同一个卡片内,存在被实施会话当界面文案抄进去的风险。

已修正:全图批注统一改为灰竖线块 + 「批注 · 不出现在系统中」标头,与 UI 文案视觉隔离;图例新增该说明。

类型 样式 数量
批注(不进系统) 灰竖线块,带「批注 · 不出现在系统中」标头 7 处
真 UI 文案(要进系统) 卡片内常规灰字 1 处 —— 退款弹窗底部「标记后不可撤销。已打款给渠道的分成不再追讨,年底统计时该笔不计入收入。」(给平台操作员的风险提示,属界面文案)

实施红线:图上凡带「批注」标头的文字,一律不写进代码;其余卡片内文字才是 UI 文案。

五、T 排期与视觉细节(2026-08-13)

T.1 增购简化的排期 —— 长官定:与微信支付一起做

背景:增购简化(去掉「平台逐单定价」,改为门店选数量、系统按价格表自动算钱)依赖批 A 价格表,
不依赖微信支付
,理论上可在批 A 后独立上线(仍走线下凭证)。

长官定:不拆,与微信支付一起做。 理由:拆了等于同一块代码改两遍
(先接线下凭证,微信上线后再拆掉),且中间态还要让存量订单过两次。

⚠️ 说明:本项代码一行未写,此前仅在方案图与文档中体现,不存在半成品需要收尾。

T.2 原价划线的视觉

原用 .strike(opacity:.42)+ #93a49e 灰绿 → 双重削弱,显得虚。
改为原价专用类,不叠加透明度:

/* 权重必须压过 .m-row .v / .rl .v,否则颜色被覆盖(踩过) */
.m-row .v.oldprice, .rl .v.oldprice, .oldprice{
  color:#b4becb !important;                        /* 定稿值 */
  font-weight:500 !important;
  text-decoration:line-through;
  text-decoration-color:rgba(180,190,203,.55);     /* 删除线单独调浅,不盖住数字 */
  text-decoration-thickness:1px;
}

调了四版才定:#93a49e+opacity:.42 太虚 → #64748b 比现价还抢眼 → #94a3b8 仍偏深 → 定稿 #b4becb。

⚠️ 中途踩坑:类挂上了但颜色没变 —— .m-row .v 与 .oldprice 权重相同且定义在后,把颜色覆盖了。
必须提高选择器权重(.m-row .v.oldprice)才生效。改完要放大截图确认,不能只看类名挂没挂上。

要点两条:
1. 不叠 opacity —— 整体透明会让数字和删除线一起变淡,这是"虚"的根源;文字色与删除线色独立控制
2. 原价不能比现价重 —— 现价(#bd4150 红 + 900)是主角,原价只做参照,压一档才有对比
应用处:门店端支付页、增购弹层(续费弹层同款,届时一并使用)。