交付出件箱

← 返回出件箱

品牌码扫码席位「真到期」· 开工前 13 维 verify

2026-09-29 · 基线 origin/test(7f6d6a1f);核心文件与 origin/main 无差异 · 只读核查,未改代码
需求(长官 9-18 拍板):品牌码新扫码的家长号从扫码当天起算 365 天硬到期;到期挡住并提示「服务已到期,请联系机构续期」;机构在品牌码页点「延期」续期;老号不动;天数固定 365。

主会话亲自复核(带对照组)

核查 命令要点 结果
require_org_seat 无到期检查 git show origin/test:app/auth/middleware.py \| sed -n 335,410p is_active 命中 3(对照有效)· seat_expires_at/assert_seat_org_usable 命中 0
到期检查现有位置 同文件 grep assert_seat_org_usable 仅 :289-290(require_h5_user 分支)
品牌码建号从不写到期 brand_code_service.py grep seat_expires_at/valid_days 只有 :94 注释、:289 读;签 token 在 :274 前无检查
用 require_org_seat 的接口 git grep -c "Depends(require_org_seat)" brand_code_claim / parent_ocr / parent_patients / middleware(require_store_seat)
生产现有品牌码号 seatops 只读 SQL 生产 brand_codes=0 · activations=0 · 品牌码席位 0(对照 org_seat 177)
test 现有品牌码号 同上 3 个,到期字段全 NULL

→ 「老号不动」在生产上没有存量要兼容;test 3 个号保持 NULL 即永久。

13 维结论(子代理逐维贴原文,摘要)

  1. 建号:_provision_family_seat(brand_code_service.py:81-113)调 generate_seats 不传 valid_days → seat_valid_days / seat_expires_at 都是 NULL。改造:建号时显式写 seat_expires_at = 绑定时刻 + 365 天(不能只传天数,否则起算点会变成「首次密码登录」)。
  2. 平台号硬到期现状:seat_auth.py:560 登录拦 403「席位已到期」;:644 首登激活写到期日;assert_seat_org_usable(seat_expiry.py:22-42)被 refresh(:944)、require_h5_user(middleware:289)、verify_api_key(h5_chat:127)调用。
  3. 品牌码家长进入路径:① 扫码 activate(:274 签 token,不查到期)② 老家长重扫命中旧号(同一路径,不查)③ 手机号/账号密码登录(查,:560)④ refresh(查,:944)⑤ wx-identity 只给 h5_user 发 token,不涉及。
  4. 每次请求:require_org_seat(middleware.py:335-408)只查 is_active 和设备号,不查到期 → 孩子档案 / OCR / 认领 / 门店接口都漏。
  5. token:access 30 分钟;org_seat 的 refresh 到期时刻 = seat_expires_at(jwt_local.py:92-98)。
  6. 延期:品牌码页「延期」→ PATCH /api/admin/parent-seats/{id}/extend → extend_seat(seat_service.py:663-719),只改 users.seat_expires_at,已过期从当前时刻起算;两字段都 NULL 的号返回 400「永久席位无需续期」。
  7. 品牌码页日期:「到期日 / 当前有效 / 30 天内到期」全部取 brand_code_activations.expires_at(admin_brand_code.py:186-192、314、430、442),延期后与席位真实到期必然不一致。
  8. 重扫:到期但未停用的号 → 命中旧号直接发 token(activate 不拦);已停用的号 → 不命中 → 新开一个号,拿到新的 365 天。
  9. 前端:brand-scan 页直接显示后端 detail,不用改;已登录家长遇到 403 → 被踢出,弹「请重新登录以继续使用」,看不到「联系机构续期」。
  10. 老号:NULL 在现有判断下天然放行,无需迁移或回填。
  11. 其他读到期字段的地方:子席位管理页(已排除品牌码号,不受影响)、家长账户配额窗口(parent_quota_service:12,品牌码号会从滚动窗口变成「扫码日起 365 天」固定窗口)、到期提醒脚本 scripts/seat_expiry_reminder.py(没有排除品牌码号,365 天后会提醒)、机构洞察、结算接口。
  12. 测试:品牌码相关测试 0 条到期断言;「席位已到期」旧文案有 4 处测试断言(test_seat_extend.py:373、test_seat_expiry_e2e.py:177/474、test_login_alert_org_nickname_e2e.py:107)。
  13. 迁移:users.seat_expires_at / seat_valid_days 已存在且可空,不用改表。

推荐改法(拦截点)

  1. 建号写到期日:brand_code_service.py _provision_family_seat,和开通记录用同一个时间点
  2. 扫码发 token 前:activate_brand_code 对命中的旧号检查到期,到期返回 403「服务已到期,请联系机构续期」
  3. 每次请求:require_org_seat 加到期检查(复用 assert_seat_org_usable),一处覆盖孩子档案/OCR/认领/门店接口
  4. 保留已有 4 处检查不动
  5. 文案只在一个函数里出

预估改动:brand_code_service.py、middleware.py、seat_expiry.py(文案)、admin_brand_code.py(视 D2)、前端两处(视 D4)、测试若干。不需要迁移。

待长官拍板


第二轮 verify(D1-D5 落地点 · 2026-09-29 下午)