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(已知失真的遗留字段)
| # | 拍板 |
|---|---|
| 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 ← 服务端算 · 展示给门店
[ 微信支付 ]
payer_name 列保留供历史展示。引用面 15 文件,删除时逐个过)out_trade_no);transaction_id 唯一约束;真发生双付 → 一笔线下退+标记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)
资料 ✅ · 付款 ✅ → [开通账号]activated_seat_count 默认带出支付订单的 quantity,平台可改(两字段本就分开:purchase_seat_count 决定收钱,activated_seat_count 决定给席位)展示:独立「付款」列(方案甲)
- PC 表格加一列:已支付(绿) / 未支付(黄) / 已退款(蓝) —— 配色见 §5.1(已拍板)
- 平台手机端卡片:右上角小角标同三态
- 渠道端如有机构状态展示 → 批 D 排查同步(涉及面清单项)
主状态 8 值(展示层;比现状 10 个筛选项还少 2 个):
待提交资料 / 资料待审核 / 资料已驳回 / 待付款 / 待开通账号 / 已开通 / 已到期 / 已关闭
派生规则:
| 库 status | 付款维度 | 展示 |
|---|---|---|
| 待提交资料 | — | 待提交资料 |
| 资料待审核 | 任意(付款列另示) | 资料待审核 |
| 资料已驳回 | 任意 | 资料已驳回 |
| 待线下付款 | 未支付 | 待付款 |
| 待线下付款 | 已支付 | 待开通账号 |
| 已开通/已到期/已关闭 | — | 原样 |
| (存量)待确认到账 | — | 待开通账号(沿现行派生) |
筛选:
- PC:状态多选换成上述 8 项 + 新增「付款状态」下拉(全部/已支付/未支付/已退款),两条件 AND
- 手机端:现有 chips 组(platform/minicup/stores/page.tsx:202 renderFilterGroup)加一组「付款」chips
| 值 | 配色 | 来源 |
|---|---|---|
| 已支付 | 绿 #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 排查项)。
触发:初稿只画了直接改动的 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) | 状态列 | 付款独立列 + 筛选 |
全仓 grep 退款 / refund → 命中 0 处,系统当前无退款概念,从零新建:
payment_orders.status = 已退款 + refunded_at / refunded_by / refund_note原则:老表 payment_vouchers 转只读、数据一条不删;新表 payment_orders 只装微信订单;
支付记录页两表合并展示、以「来源」列区分;切换期人工确认入口限时保留,在途清零后关闭。
| 存量类型 | 切换后 | 处置与展示 |
|---|---|---|
| 已开通(已成交) | 不受影响 | 详情页来源显示「线下转账」,交易号「人工确认·无交易号」,历史凭证图仍可查 |
| 待确认到账(已传凭证) | 走完老流程 | 保留人工「确认到账/驳回」入口,清零后关闭 |
| 待线下付款(未传凭证) | 切新流程 | 付款入口切微信;展示层改称「待付款」(库值不变) |
| 凭证已驳回 | 切新流程 | 驳回态终结,改走微信;原记录只读留存 |
| 资料待审核 / 待提交资料 | 无影响 | 未进入付款环节 |
| 增购/续费在途 | 分两类 | 「待确认金额」需人工兜底(该态将废除);「待付款」直接切微信 |
⚠️ 切换前须跑存量盘点 SQL 统计各态实际条数(批 D 前置动作),上表为处置口径,条数以实际盘点为准。
席位选择出现在三个入口,都是「选数量 → 服务端算钱 → 微信支付」同一模式:
Field(ge=1, le=500) 同口径)→ 实时显示 ¥2,380 × 2 席 = ¥4,760 → 微信支付落库:门店所选数量写 purchase_seat_count(决定计费);平台开通时 activated_seat_count 默认带出该值、可调整(决定实际开通席位)。
「确认到账」不是一个待办动作,是整条资金链的唯一发动机(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 必含。
| # | 议题 | 拍板 |
|---|---|---|
| 1 | 退款冲销收益 | 快照未进结算单 → 作废;已进未打款 → 负数冲销快照;已打款给渠道 → 只做标记,年底统计时不计入收入(不追讨、不冲抵) |
| 2 | 分成比例快照时点 | 支付成功那一刻锁定对应比例;取消 24h 锁价设计(不做价格锁定) |
| 3 | 结算期归属 | 沿用原口径:原按确认到账时间归月 → 微信支付按支付成功时间归月 |
| 4 | 门店侧发票 | 线下开,系统不管(现有发票流程是渠道开给平台,与门店付款无关,不动) |
| 5 | 增购计价 | 全价不折算(原"按剩余天数折算"经 D8 模型查证后作废,见 §五G) |
| 6 | 商品名 | 「智脑」统一改称 「赵阳智脑」 |
✅ 待拍板项已全部清零(续费顺延口径 2026-08-13 查证结案,见 §五I)。
PC 端功能列表不得随意增删列;确有改动必须在图上标记并说明原因。
本轮自查发现并修正的违规:
| 表 | 我的违规 | 修正 |
|---|---|---|
| 机构列表 | 擅自删除「二级渠道」「手机号」,凭空加「提交时间」 | 恢复原 7 列,仅新增「付款」1 列并标注 |
| 支付记录 | 擅自删除「付款单」「一级渠道」「二级渠道」「购买席位」,加「业务」「金额」等 | 恢复原 9 列,保持 9 列不增不减 |
支付记录最终改动(共 3 处,均已在图上标注):
1. 原「提交时间」+「到账确认时间」两列合并为「支付时间」 —— 微信实时到账,两时间点合一;存量数据原提交/确认时间移入「查看凭证」查看
2. 新增「来源」列 —— 区分微信支付与存量线下转账,否则两类数据无法分辨
3. 原「核对状态」改名「支付状态」 —— 人工核对动作退役,语义已变
检索条件:原按提交/确认时间检索 → 改为按支付时间检索。
金额不入列(原表即如此),仍在详情弹窗展示。
🔴 我此前的「按剩余天数/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 天」,不写「/ 席 · 年」——写"年"会让人误以为跨自然年清零。
| # | 议题 | 决策 |
|---|---|---|
| 1 | 进度页要不要显示单价 | 显示原价划线 + 现价(8-13 修正:促销信息对转化有价值)。两处均取自 service_prices,同源不会漂移 |
| 2 | 「服务付款」文案 | 改 「服务费用」(付款是动作、费用是对象,卡片标题用名词) |
| 3 | 支付页数量选择器过大 | 数字 26px→20px,数量与合计并排同一行,省一整块高度 |
| 4 | 「支付成功即到账,无需上传凭证」小字 | 删(微信支付本就如此,属废话);改为提示 365 天额度与首登起算 |
| 5 | 支付按钮文案 | 「立即支付」(不带金额)。金额在按钮正上方的合计已展示,按钮再写一遍是重复;不写"微信"——点了自然拉起,将来加其他支付方式不必改文案 |
| 6 | 价格管理 | 不加「计价说明」列(无实际用途);单价列直接写 ¥2,380.00 / 席 · 365 天;「最近改价」列只显示时间+操作人,价格变化过程进改价弹窗;「新增服务项目」按钮本期不做(只有一个商品,无新增场景) |
| 7 | 存量切换器 | 取消。切换前先把在途凭证人工跑完(跑成已开通),切换后只剩「历史线下只读 + 微信新单」两类,无中间态 |
| 8 | 标记退款位置 | 不放列表(危险且不可撤销)。列表操作列维持现网单按钮,命名 「查看详情」;弹窗一页承载:支付信息 / 凭证(仅线下单)/ 退款处理 / 退款留痕 |
| 9 | 开通后展示(已出图) | 现网「账号与服务管理」已有四块(手机号验证/服务概况/进行中的申请/历史服务记录),不新增区块:服务概况补席位到期状态(未激活按现网铁律显示「未激活(首登后起算)」),历史服务记录每单补支付方式 |
| 10 | 手机端机构卡片 | 照现网结构不改版:状态胶囊右上角绝对定位、联系人+手机一行、归因渠道、所属一级;仅在下方新增付款状态胶囊 |
长官令:续费要顺延。查证结论 —— 系统现状已顺延,无需改造。
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)。
长官口径:「增购只有首登后才开始算时间」「续费支付后自动累加时间」。查证后系统实现如下:
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 天额度、首登起算;续费=额度累加或到期顺延。无需改造。
seat_overview.py:51-53 原文记录了已发生过的事故:
此前一律标「未激活」,永久席位混在续费勾选里,用户勾了才炸——而续费是先付款后开通,
撞上时钱已经收了、订单只能人工改单。核心已实测确认两者会同机构混存(org5=8永久+9带额度)
现状(线下付款 + 人工确认到账)尚有人工拦截余地;切微信后实时到账,将退化为"秒收钱秒失败",
且退款已定为走线下人工,代价更高。
约束(批 E 必做,两道):
1. 前端:续费选席位列表中,永久席位置灰不可勾选,并标注「永久有效,无需续费」
2. 服务端:下单建 payment_orders 前再校验一次所选席位无永久席位,命中则拒绝建单
—— 绝不能等到支付成功后才发现
判据只有
seat_valid_days一个字段能区分永久与未激活(两者service_end_at都是 null),
校验必须读该字段,不能只看service_end_at是否为空。
原图写「平台审核中,一般 1 个工作日内完成」—— 这是我自己加的,必须撤:
review/page.tsx:399):「资料已提交,请等待平台处理。」定稿:资料已提交 / 请等待平台处理 · 提交于 2026-08-13 14:02
—— 说清"已收到 + 在等 + 什么时候交的",不说"多久好"。
通用红线:门店端文案一律不出现具体时长承诺(X 个工作日 / X 小时内)。
方案冻结后做全量自检,抓出 4 处内部矛盾(已修)+ 2 个实施盲点(新增待办)。
| # | 位置 | 矛盾 | 修正 |
|---|---|---|---|
| 1 | §五D 增购流程 | 仍写「按剩余月折算金额(计价规则待拍板)」,与已拍板的「全价不折算」冲突 | 改为「单价 × 数量全价(不折算)」 |
| 2 | §五D 首开流程 | 写「进度页显示 ¥2,380/席·年」,与「进度页不显价」及「365 天」口径双重冲突 | 改为「进度页不显价,价格只在支付页出现一处」 |
| 3 | §八 自检记录 | 仍写「进付款页锁价、订单 24h 有效」,该设计已取消 | 标注已取消,改为「支付成功那刻锁单价与分成比例」 |
| 4 | §六 计价表 | 表头「建议规则(待拍板)」+ 首开行「服务期 1 年」 | 改为「已拍板」+ 首开同 D8 口径(每席 365 天、首登起算) |
教训:同一事实在长文档里散落多处,改了一处必须全文回扫。这 4 处若不修,实施会照着写出与拍板相反的逻辑。
现网 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。
全仓 grep MicroMessenger / isWechat / 请在微信中打开 → 命中 0 处,现网门店端没有任何环境判断。
影响:JSAPI 只能在微信内调起。门店若在系统浏览器打开签约链接(转发到备忘录、复制到 Safari 等),
点「立即支付」会静默失败或报错,门店不知所措。
建议(批 C 一并做):支付页加 UA 检测,非微信环境显示「请在微信中打开本页面完成支付」并隐藏支付按钮,
不调必然失败的 JSAPI(dev1 调研 §8 也提过此项,本方案原文未落实)。
| # | 我自检时的写法 | 长官纠正 | 已改 |
|---|---|---|---|
| 1 | 增购计价改「全价不折算」 | ✅ 对 —— 增购起步就是一年 365 天,折算反而不对 | 保持 |
| 2 | 进度页付款卡不显价 | ❌ 错 —— 原价/现价该显示就显示(促销信息有价值) | 进度页补上原价划线 + 现价 |
| 3 | 订单 24h 有效 | ❌ 错 —— 订单不设有效期:门店隔天回来照样要能付,过期反而挡住成交 | 「锁价 + 24h 过期」整体取消 |
| 4 | 计价表头「待拍板」 | ✅ 已过期,改掉 | 改为「已拍板」 |
价格一致性说明:进度页与支付页两处价格同取
service_prices,单一数据源,不会漂移。
现网两处都是同一个反模式(都靠"先查再加一",无锁):
| 位置 | 写法 | 唯一约束 |
|---|---|---|
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 前置,三步):
sql
CREATE SEQUENCE IF NOT EXISTS payment_order_seq;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 原子操作,并发安全;且不回滚(事务失败也不会重号),正合支付场景。
payment_orders.out_trade_no 加 unique=True;IntegrityError → 重取号重试一次,仍失败则报错,绝不静默吞掉。⚠️ 存量两个函数本期不动(线下凭证链将退役);但若批 E 前 order_no 仍在用,
建议顺手加唯一约束,至少让撞号"报错"而不是"写脏"。
核心侧已有可直接复用的函数(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,两处):
「请在微信中打开本页面完成支付」+ 引导:复制链接 → 微信内粘贴打开
不调必然失败的 JSAPI(JSAPI 只能在微信内调起,外部浏览器点了只会静默失败或报错)
三处口径统一:
settlement/web侧新建一份同名isInWeChat()(不跨仓 import 核心),
正则与核心一致/MicroMessenger/i,避免两套判断漂移。
现网:签约页 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/昵称随签约档案存下,支付下单时直接取,不再二次授权。
现网机制(review/page.tsx:774 注释):点「修改资料」后本卡隐藏、由表单卡接管,是同页展开、不是弹窗。
问题:本期下方新增了「服务费用」卡,表单展开后页面变长,门店可能滚不到、以为付款入口没了。
长官定:点「修改资料」后「服务费用」区整块隐藏,页面只剩表单,专注填写;
点「提交修改」或「取消」后自动恢复显示。(比"收成一行"更简洁,长官 8-13 定)
授权提前到入口后,全量自检发现 3 处未覆盖,都会导致「拿不到 openid → 支付断掉」:
stores.py:1385 生成的复核链接是独立一条:/store/minicup/review?rt=<review_token>,
平台可单独发给门店。门店从这条直接进时,根本不经过签约页 → 没授权 → 无 openid → 点「去支付」必断。
解法:授权判断不绑在签约页,绑在「当前会话有没有本人 openid」上(会话级,非档案级 —— 与 §五Y.3 同一口径)——
签约页与复核页都做同一个前置检查:会话无 openid → 先进授权落地页 → 授权成功回跳原页。
即:授权是全流程的入口守卫,不是签约页专属的一屏。
说明:复核链接由平台单独发给该门店本人(如驳回后通知其修改)是正常业务路径,必须支持;
与 §五Y.3 禁止的「转给他人代付」是两回事 —— 前者是本人换入口,后者是他人代办。
微信授权页用户可以点「取消」,也可能因 appid 配置错、网络问题失败。现方案未定义此分支。
解法:回跳后未拿到 code → 停在授权落地页,显示
「未完成授权,无法继续入驻。[重新授权]」,按钮可重试。
绝不静默放行进资料页 —— 否则门店填完一整套资料,到支付才发现没身份,白填。
核心侧现成实现已考虑:
seat-wx-auth-landing.tsx注释「onStart resolve 且未发生页面跳转(如 appId 拉取失败)→ 按钮恢复可点,允许重试」。
签约链接可转发。老板授权后填了资料,若店员用自己微信打开复核链接并支付,
则档案里存的 openid 与实际付款人 openid 不同。
判断:这不算 bug,但要明确口径 —— 支付订单记录的是本次付款人的 openid(微信回调里带的),
用于对账与退款溯源;档案里那个只是首次授权人,两者不必强制一致。
落地要求:
- 支付订单表存本次付款人 openid(回调 payer.openid),不复用档案里的
- 支付详情弹窗展示的「付款微信」取订单表,不取档案 —— 否则会显示错人
- 档案 openid 仅用于免二次授权(有就直接下单),不作为身份校验依据
长官定:门店可能在手机微信、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 端不支持,退路是提示门店改用手机微信打开(不是放弃授权)。
流程:落地页点「确认」→ 跳微信授权页 → 若用户在微信侧点「取消」 → 回跳我方页面但无 code
→ 停在授权落地页,不给进资料页,显示:
「需完成微信授权后才能填写机构资料」+ [重新授权] 按钮。
不做任何降级放行 —— 涉及支付,没有身份就不允许往下走。
规则:签约链接可以分享给多人,但每个人打开都是独立一份签约 ——
该授权授权、该填资料填资料、该自己付款自己付款。不允许 A 提交资料、B 来付款。
现网机制天然支持(查证 store_signup.py:236-256):签约链接 token 里只有 channel_id(渠道),
不绑门店;每次打开→填写→提交,都会新建一份独立门店档案。渠道把同一条链接发给两家门店,两家各建各的档,互不干扰。
落地要求:
- 复核链接(/store/minicup/review?rt=)不作为转发付款入口 ——
它是门店本人的进度页;本期不设计任何"转给他人代付"的路径
- 授权判定基于当前会话是否已有本人 openid:同一人换设备/重开 → 重新授权后回到自己那份档案的进度
- 付款人以实际支付那次的 payer.openid 为准(微信回调携带),用于对账与退款溯源
- 档案 openid 作首次授权人留档
⚠️ 此前表述易误导,特此澄清:temp-exchange 换出的 120 分钟是本次填写的登录态,
不是链接有效期。
generate_store_signup_token() → channel_id + HMAC 签名
parse_store_signup_token() → 只验签名,无任何时间校验
⇒ 签约链接本身永久有效(只要渠道存在且开放签约)。门店隔数小时、数天再打开照常可用;
仅当单次填写超过 120 分钟,临时会话过期,重新点链接即可,不影响链接可用性。
本期只做 JSAPI,不做 H5 支付、不做 Native 扫码。非微信环境仅提示「请在微信中打开」。
其他支付方式后续再议。
长官要求:支付代码写完,必须跑一遍官方 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 装好前,知识检索改用本地文档直读(能溯源,但用不了语义检索)。
长官指出:退款、改价这些危险动作,谁改的、谁退的都要记账号。核对结果:
| 操作 | 留痕字段 | 状态 |
|---|---|---|
| 标记退款 | 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 做法一致。
来源:dev1《微信支付接入·调研交接文档》(2026-08-12),其 12 维物理证据我抽验 3 条全部属实
(二维码微信内长按识别 /exchange_code现成 / 快照页红线代码在),可直接采信。
| 项 | 结论 |
|---|---|
| 接口选型 | 只做 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 元真实交易,云机不可能跑通 |
web/app/h5/chat/components/seat-wx-auth-landing.tsx 及其测试用例⚠️ 长官提到「智脑手机端登录页一打开就微信授权」—— 查证:现网不是这样,正是因为这条红线才改成点击式。支付页同理
WeixinJSBridgeReady 再调,否则首次进页拉不起支付(全库 0 处,纯新写)err_msg 不可信:只能触发轮询,绝不能用它改订单状态app/models/settlement_idempotency.pytrade_state 只需处理 SUCCESS/NOTPAY/CLOSED/REFUND| # | dev1 建议 | 裁定 |
|---|---|---|
| 支付时点 | 建议维持「先审后付」 | ❌ 以长官拍板为准:资料审核 ∥ 付款并行。副作用:可能出现「已付款但资料被驳回」→ 退款成硬需求,但已定线下退+系统标记,无需接微信退款 API |
| 线下通道 | 待定 | ❌ 全部去掉(长官已拍) |
参数 A 单行表 settlement_billing_params |
只存一个 signup_fee |
❌ 装不下。以本方案 service_prices 为准(单价 × 数量、覆盖首开/增购/续费、含改价留痕) |
| 成功页显示什么 | A 微信昵称 / B 门店名 二选一 | ✅ 两个都显示(长官定):「付款微信:明眼小赵 / 杭州明眸视光中心 · 赵阳智脑 × 2 席」→ 需 snsapi_userinfo scope |
| 二维码支付 | — | ❌ 不做(长官定):直接拉起微信支付,不放二维码手动扫 |
微信支付无付款凭证,付款人身份改由 微信昵称 + openid 记录,替代原「付款方名称」字段。以下三处均须展示:
🔴 渠道展示特殊规则(payment-confirm/page.tsx:201-205):channel_type === "platform_direct_secondary"(平台直签二级)时,
「一级渠道」标签要变成 「渠道归属」,且现网注释 :1000 明确 不可与二级渠道合并展示。新页面必须沿用此规则。
密钥类:mchid / apiclient_key.pem / 证书序列号 / APIv3 密钥 / 平台公钥+公钥 ID
配置类:AppID 与商户号绑定 / JSAPI 已开通 / 支付授权目录 / 公众平台网页授权域名(最多 2 个,直接决定 test 能否验收) / 确认是已认证服务号
辅助:商户主体名称 / 退款权限 / 可小额真付的微信号(0.01 元验收)
私钥与 APIv3 密钥不进聊天、不进仓库,放
~/secrets/wechatpay/,只进settlement/.env,仓库留.env.example占位。
没问题,走本仓即可。 判据:本会话 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(解除付款前置 + 付款人自动取执照名)作废 —— 那是给线下凭证续命的。
| 批 | 内容 | 依赖 |
|---|---|---|
| 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 在途「待线下付款 / 待确认到账 / 待确认金额」数据
在切换前人工跑完(长官定),不设切换器。
待线下付款 原地复用零迁移isInWeChat()(批 C)保留全过程供追溯;实施只看正文,附录仅在需要确认"某个细节为何这样定"时查阅。
| # | 调整 | 说明 |
|---|---|---|
| 1 | 门店端去掉「费用按平台定价自动计算」 | 属内部话术,不给客户看;改为「选择购买席位数量」 |
| 2 | 数量选择器留足三位数宽度 | 上限 500 席,数字位加宽到 58px,右侧标注 1–500 席 |
| 3 | 价格管理去掉「计价说明」列 | 无用;单价直接写 / 席 · 365 天 |
| 4 | 「最近改价」列展示混乱 | 列上只留「时间 + 操作人」;改价记录改为弹窗内表格(时间/原价/新价/操作人),已出图 |
| 5 | 「新增服务项目」按钮 | 本期不做 —— 当前仅赵阳智脑一个商品,无新增场景,等真有第二个再加 |
| 6 | 手机卡片过大 | 付款胶囊由独占一行改为并入联系人行尾,卡片高度回到现网水平 |
| 7 | 退款处理区层次乱 | 退款方式 / 退款说明各自独立成组,操作按钮右对齐单独一行,风险提示降为脚注 |
| 8 | ⑧ 节一大堆小字 | 内部话术全部删除,只留业务说明 |
| 9 | 账号与服务管理没画出来 | 已补全四块结构图(手机号验证 / 服务概况 / 进行中的申请 / 历史服务记录),标出两处新增:服务概况加席位到期状态、历史服务记录加支付方式 |
| # | 调整 | 说明 |
|---|---|---|
| 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 |
| # | 调整 | 说明 |
|---|---|---|
| 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 本就有,原图漏画 → 已补。注意现网细规则:待审核展开表单时不给刷新(会重置正在填的内容),驳回态才给 |
| # | 调整 | 说明 |
|---|---|---|
| 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),非跳页 ✅ |
| # | 调整 | 说明 |
|---|---|---|
| 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,
原价不参与任何金额计算、不入订单快照,避免与实付金额产生第二个真值源。
| # | 调整 | 说明 |
|---|---|---|
| 1 | 🔴 DOM 缺 </div> 修复(根因) |
⑦ 节少一个闭合标签,导致它把后续所有节吞进去 —— 此前反馈的「格式乱 / 更丑」根因在此,不是设计问题 |
| 2 | 弹窗恢复单列 | 两列虽省高度,但字段名与值被拆到左右半屏,眼睛要横跨对应。单列成对贴着更耐读,采纳长官判断 |
| 3 | 弹窗宽度 | 整页维持 1180 基准,弹窗收窄至 700px 两窗并排(700×2 正好放下)—— 既保成对易读、又能一眼对比「未退款 vs 已退款」。 ⚠️ 期间曾用负 margin 强行撑到 1440,导致内容溢出屏幕左侧、字段被切 → 已废弃该做法:不做局部撑破版式 |
| 4 | 增购/续费流程简化 | 现网三步(提申请 → 平台逐单定价 → 门店付款)→ 简化为门店选数量 → 直接支付 → 自动生效,平台不介入。「待确认金额」态与平台定价入口一并废除 |
| 5 | 平台改价余地 | 长官定:先不做(后期再说)。当前统一按价格表走 |
| 6 | 增购是否需审 | 长官定:不需要,买越多越好,无上限 |
| 7 | 存量 待确认金额 订单 |
长官定:动之前先人工处理完(与支付切换同批做) |
首开的一次性弹层保持现网原样,不动(store/minicup/service/page.tsx:817)。
🔴 真空在增购:现网账号密码只存在于平台点开通那一次的 provisionResult 中
(admin/minicup/stores/[orgId]/services/page.tsx:470 copyProvisionCredentials)。
增购改微信支付自动开通后,平台从未点过开通 → provisionResult 为空 → 账号密码无处可查。
本期补齐:在机构详情 →「账号与服务管理」页的订单列表中,每单增加「查看账号」入口,
PC 与平台手机端两端都要:
seat_credentials.py 存在 Redis 的同一份凭证:72h TTL + 最多查看 3 次、读满即焚(Lua 原子扣减)store/minicup/service/page.tsx 首开弹层照旧)buildSeatCredentialsText 既有口径)| # | 调整 | 说明 |
|---|---|---|
| 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 遍反而乱);续费不带「打开方式」(账号早在用,引导多余)。
长官令:单个复制与复制全部,内容都要带「公司」与「打开方式」,账号按 账号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,空则回退招牌名,两者都空不输出「公司」行、绝不留空行;
密码缺失兜底为 未携带,请联系工作人员重置;冒号统一半角(对齐长官现用微信格式)。
长官问:「平台直签二级渠道时『一级渠道』标签变为『渠道归属』…」这句灰字会不会显示在系统里?
答:不会,那是给实施看的批注。 但原图把批注与真 UI 文案混在同一个卡片内,存在被实施会话当界面文案抄进去的风险。
已修正:全图批注统一改为灰竖线块 + 「批注 · 不出现在系统中」标头,与 UI 文案视觉隔离;图例新增该说明。
| 类型 | 样式 | 数量 |
|---|---|---|
| 批注(不进系统) | 灰竖线块,带「批注 · 不出现在系统中」标头 | 7 处 |
| 真 UI 文案(要进系统) | 卡片内常规灰字 | 1 处 —— 退款弹窗底部「标记后不可撤销。已打款给渠道的分成不再追讨,年底统计时该笔不计入收入。」(给平台操作员的风险提示,属界面文案) |
实施红线:图上凡带「批注」标头的文字,一律不写进代码;其余卡片内文字才是 UI 文案。
背景:增购简化(去掉「平台逐单定价」,改为门店选数量、系统按价格表自动算钱)依赖批 A 价格表,
不依赖微信支付,理论上可在批 A 后独立上线(仍走线下凭证)。
长官定:不拆,与微信支付一起做。 理由:拆了等于同一块代码改两遍
(先接线下凭证,微信上线后再拆掉),且中间态还要让存量订单过两次。
⚠️ 说明:本项代码一行未写,此前仅在方案图与文档中体现,不存在半成品需要收尾。
原用 .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)是主角,原价只做参照,压一档才有对比
应用处:门店端支付页、增购弹层(续费弹层同款,届时一并使用)。