2026-09-24 · 云机环境只读核查 · 非验收证据
代码基线:feature/subseat-login-v2@0dfa67fd(落后 origin/test498b82527 个 commit,但本次涉及的seat_service.py / seat_auth.py / brand_code_service.py / user.py四个文件git diff --stat HEAD..origin/test0 差异,基线可信)
数据:test 库经 seatops 通道实查(db-query test查 users/organizations 受 RLS 假 0,已用pg_stat_user_tables.n_live_tup=388交叉证实并绕过)
BF100001| # | 维度 | 结论 | 证据(file:line) |
|---|---|---|---|
| 1 | 现在家长号怎么生成 | 全平台按月共用一个计数器,格式「6 位序号 + YYMM」= 10 位纯数字;批量取号后一次查重剔除,最多 5 轮 | app/services/seat_service.py:186 _alloc_seq_batch · :213 _alloc_parent_usernames · :239 f"{seq:06d}{period}" |
| 2 | 开号入口 | 全仓只有一个生成函数 generate_seats,所有开号都走它:PC 后台门店席位、PC 后台家长子席位、门店端开家长子席位、品牌码扫码自动开号、结算开通 ×3 |
admin_seats.py:126 · admin_parent_seats.py:134 · store_subseat.py:79 · brand_code_service.py:94 · internal_settlement.py:577/698/1643 |
| 3 | 账号唯一约束 | users.username 全表唯一(跨所有角色),数据库层没有格式约束 |
app/models/user.py:33 String(64), unique=True · migrations/versions/phase29_001_create_auth_tables.py:35 |
| 4 | 其他角色的账号长什么样 | 管理员 / 平台员工:手填自由文本(admin、xz_admin…);渠道:CH + 年月 + 3 位(如 CH202608019,11 位) |
test 库实查:admin 4 / channel 78 / platform_staff 3 |
| 5 | 机构表有没有现成字段 | 有 org_code(唯一、可空),但它是登录时校验机构一致性用的;test 88 个机构只有 3 个有值,且都是脏数据(uat-181257 / TONGBAN_TEST / uatpwexp09040941)→ 不建议复用,新开专用列 |
app/models/organization.py:17 · app/api/seat_auth.py:95/162 · internal_settlement.py:549 注释「旧兼容字段,全仓无生成规则」 |
| 6 | 计数表能不能直接用 | SeatSeqCounter 主键是 (period, seat_kind),也就是全平台按月,装不下「按机构计数」→ 要新的计数结构 |
app/models/seat_seq_counter.py:10-25 |
| 7 | 后端哪里写死了格式 | 只有门店序号池一处(已按 seat_type 过滤,家长号进不去,不受影响);席位登录没有大小写处理(批次五决策 2 本来就要加) | seat_service.py:154-158 · seat_auth.py:169/231 |
| 8 | 前端登录框能不能输字母 | 手机端、平板账号框都没有 inputMode / maxLength / 过滤,BF100001 能直接输;登录按钮只判断非空 |
web/app/h5/chat/components/seat-login-sheet.tsx:291-305, 110 · web/app/h5/tablet/components/seat-login.tsx:263-277, 95 |
| 9 | 前端写死的文案 | 「席位账号不是手机号,请输入提供给您的10位数字账号」门店、家长两端共用,家长换格式后说错了,要改 | web/app/h5/chat/lib/seat-login-hints.ts:14;断言该文案的测试:seat-login-hints.spec.ts:198、seat-login-sheet-phone-hint.spec.tsx:41 |
| 10 | 后台维护入口放哪 | 机构编辑是列表页弹窗(里面有「家长子席位开通」开关);品牌码是独立子页面 + 独立接口(require_role("admin"))→ 前缀维护可以照品牌码的做法单独做 |
web/app/(dashboard)/admin/orgs/page.tsx:249/315 · app/api/admin_brand_code.py:34/41 · admin/orgs/[id]/brand-code/ |
| 11 | 「重复不让保存」有没有现成做法 | 有:后端返回错误 detail → 前端红字原样显示 | admin/orgs/page.tsx:320-324, 714 · admin/orgs/[id]/seats/page.tsx:271-287(席位电话查重同款) |
| 12 | 开号弹窗 | PC 后台「开通子席位」弹窗没有「机构没设前缀」的提示位置,要新增 | web/app/(dashboard)/admin/parent-seats/page.tsx:1127-1216 |
| 13 | 现有数据 / 测试 | test 家长号 65 个,全是 10 位纯数字;家长号格式断言只在 tests/unit/test_phase83_seat_seq_counter.py(:110-120 等),要重写;门店相关用例保留当回归 |
test 库实查 · 测试文件 grep |
①username约束 user.py:33 username = Column(String(64), unique=True, nullable=True, index=True)
②org_code organization.py:17 org_code = Column(String(50), unique=True, nullable=True, comment="机构编码(旧字段,兼容)")
seat_auth.py:162 4. org_code 一致性:seat.linked_org_id 对应 org.org_code == 请求 org_code → 否则 401
③计数表主键 seat_seq_counter.py:25 """家长席位号码月度序号原子计数器 · 复合 PK (period, seat_kind)"""
④家长号拼装 seat_service.py:239 candidates = [f"{seq:06d}{period}" for seq in seq_range]
⑤开号调用方 brand_code_service.py:94 / store_subseat.py:79 / internal_settlement.py:577,698,1643 / admin_seats.py:126 / admin_parent_seats.py:134
⑥登录错误提示 seat-login-hints.ts:14 "席位账号不是手机号,请输入提供给您的10位数字账号";
⑦后台机构弹窗 admin/orgs/page.tsx:315 enable_parent_subseat: enableParentSubseat, // Phase 74
⑧独立入口先例 admin_brand_code.py:41 router = APIRouter(prefix="/api/admin/orgs", tags=["管理后台-机构-品牌码"])
对照组 seat_service.py 中 "def " 共 17 处(证明查法有效)
BF、ZYZN),不许数字和符号;保存时统一转大写再查重CH —— 渠道账号都是 CH 开头,建议不让机构用 CH,免得看起来像渠道号| 层 | 要做的 |
|---|---|
| 数据库 | 机构表新增「子席位前缀」列(唯一);新的「按机构」计数结构(从 100001 起);迁移挂在 phase83_001 之后 |
| 后端 | seat_service.py:186-255 取号改成「机构前缀 + 机构计数」;新维护接口(照 admin_brand_code.py,查重不过返回错误);三个开号入口加「没设前缀不许开」;席位登录加大写处理(决策 2) |
| 前端 | 新的前缀维护页面(照品牌码页);开子席位弹窗加提示;改 seat-login-hints.ts:14 那句「10位数字账号」 |
| 测试 | 重写 test_phase83_seat_seq_counter.py 家长号格式相关用例;改两条文案断言 |
| 文档 / 图 | 方案决策 1 作废改写;图里 T0000012609 全部换成新格式示例 |
顺带的好处:计数器按机构分开以后,不同机构同时开号不会再互相排队,批次四(一次开 5 万个号)要的提速方向正好一致。