交付出件箱

← 返回出件箱

机构管理搬平台手机端 · 开发任务拆解

dev2 · 2026-09-07 · 长官令「三批一块整,一起做完再发版」
本文所有接口/文件/行号均已物理 grep 验证(基线 origin/main = 882eac65),未验证的单独标注。
方案见 platform-mobile-org-admin-plan-20260907.md;产品决策以 9-07 交接件 12 条为准。


零、先说三个结论(影响全局)

# 结论 证据
1 🔴 核心侧 /internal/settlement/* 一个机构 CRUD 端点都没有 该文件 12 个端点全清单已核对,只有 provision(签约专用)+ 席位 + 渠道账号 + 门店素材。对照组:orgs/{org_id}/seats 能查到 10 处,证明查法有效
2 🔴 PC 端机构管理不在结算仓,在主仓自己的后台 web/app/(dashboard)/admin/orgs/page.tsx,走 require_role("admin")。所以不是"把结算的页面改改",是跨系统新建一整条链路
3 ✅ 凭证复制格式不用抄 buildSeatCredentialsText 在 settlement/web/lib/seat-credentials-text.ts,与新页面同包;平台手机端 stores/[orgId]/page.tsx:8 已在 import。长官 9-07 已确认:直接复用

链路长这样(每加一个功能都要走完四层)

主系统 internal_settlement.py 加端点(X-Service-Token 鉴权)
   ↓
结算 core_client/client.py 加 wrapper
   ↓
结算 platform_mobile.py 加端点(ZhaoYangPermission 权限位鉴权)
   ↓
结算 web/app/platform/minicup/orgs/ 加页面

⚠️ 少一层就不通。 拆单时每个功能都按这四层算工作量。


一、基础设施(三批共用,必须最先做)

任务 干什么 改哪里 验收
T0-1 加 3 个权限位:ORG_READ / ORG_WRITE / ORG_ADMIN,在 ROLE_PERMISSION_MAP 只给 admin(长官 9-08 定:机构管理只给管理员,platform_staff 不给) settlement/backend/app/auth/zhaoyang_permissions.py:94(枚举)、:145(角色映射) 现有 14 个权限位不动;新账号登录后 /api/platform/mobile/* 能拿到新权限位
T0-2 前端权限位同步 settlement/web/app/platform/minicup/_components/platform-mobile-context.tsx:17-31 前后端权限位字符串逐字一致(现有 13 个是这么对的)
T0-3 新建页面目录 orgs/,套用现有 shell 新建 settlement/web/app/platform/minicup/orgs/ 目录下页面能被 platform-mobile-shell 正常包裹、底部导航态正确

| T0-4 | 🆕 新建 platform_mobile_orgs.py,机构管理全部端点放这里 | 新建 settlement/backend/app/api/settlement/platform_mobile_orgs.py;在 settlement/backend/app/main.py:94 那一串 include_router 后加一行,照抄同款写法 | 新 router 前缀仍是 /api/platform/mobile(前端路径不变);platform_mobile.py 一行不动 |

✅ 长官 9-07 已定:另开一个文件。 理由:platform_mobile.py 现有 71 个端点 / 1288 行,机构管理再加 10+ 个会逼近 2000 行,改一处看不清影响面。
挂载点已验证:settlement/backend/app/main.py:94 app.include_router(settlement_platform_mobile.router, ...),新文件照同款加一行。


二、批一 · 只读(先让长官看见界面)

后端 · 主系统侧

任务 干什么 改哪里 关键点
T1-1 新增 GET /internal/settlement/orgs(机构列表) app/api/internal_settlement.py 🔴 必须带 page/size 分页 —— 现有 list_orgs(api_v1.py:276)全量无分页,手机端「加载更多」用不了
T1-2 同上响应体里加 seat_count / active_seat_count 聚合 同上 🔴 现有 OrgItem(api_v1.py:218-249)没有这两个字段,只有 total_requests/total_tokens。要新写聚合 SQL(按 users.linked_org_id + seat_type 统计)
T1-3 新增 GET /internal/settlement/orgs/{org_id}(机构详情,含 14 开关) 同上 ✅ 好消息:14 个开关主系统已经能取到(OrgItem 里 enable_* 系列已从 org_brain_config join 出来),照抄那段 join 即可,不用新写
T1-4 席位列表复用现有端点,不新建 — GET /internal/settlement/orgs/{org_id}/seats(internal_settlement.py:589)已存在 ✅

后端 · 结算侧

任务 干什么 改哪里
T1-5 core_client 补 2 个 wrapper:list_orgs / get_org settlement/backend/app/core_client/client.py(照抄 list_org_seats:101-108 的写法,读操作用 _READ_TIMEOUT 5s 那条,别用 30s 的写超时)
T1-6 平台手机端加 3 个只读端点:GET /orgs、GET /orgs/{id}、GET /orgs/{id}/seats platform_mobile_orgs.py(新文件),鉴权 require_zhaoyang_permission(ORG_READ)

前端

任务 页面 要点(照方案图)
T1-7 orgs/page.tsx 机构列表 每机构一张卡;「席位 24 · 启用 18 · 创建于 2025-03-12」一行;底部加载更多(复用 _components/load-more.tsx);顶部搜索 + 字段下拉
T1-8 orgs/[orgId]/page.tsx 机构详情(只读) 5 区块:基本信息 / 凭证(脱敏) / 14 开关分 3 组折叠·只读 / 席位统计入口 / 危险操作占位
T1-9 orgs/[orgId]/seats/page.tsx 席位列表 席位卡四行:席位账号 / 微信昵称·脱敏ID / 门店名 / 到期+设备。字段名叫「席位账号」不叫「账号」
T1-10 orgs/[orgId]/seats/[seatId]/page.tsx 席位详情(只读) PC 端是右侧抽屉 → 手机端改全屏页
T1-11 首页入口:「渠道资料审核」替换成「机构管理」 决策 3:两者与底部「渠道」栏同目的地,重复
T1-12 🆕 机构列表搜索(6 个字段) 🔴 这条有坑,必读:PC 端 6 个字段里只有 seat_username/experience_store_name 走后端,「机构名称/联系人/联系电话」三个是前端本地过滤(orgs/page.tsx:724-762,因为 PC 全量加载所以能这么干)。手机端改「加载更多」分页后本地过滤就废了 —— 搜「张三」,张三在第 5 页没加载出来就搜不到。→ 这三个字段必须一并改成后端搜索(核心侧新端点加 query 参数)。另注意:「全部」模式 PC 端是分两次请求取并集(接口两参数同传是 AND,而「全部」要 OR),新端点最好一次搞定
T1-14 🆕 席位列表搜索(5 个字段) 席位页检索是全服务端(seats/page.tsx:365-391,300ms 防抖后必发 GET),字段:全部/用户名/账号/电话/微信昵称。新端点要支持
T1-13 🆕 席位登录诊断(席位为什么登不上 · 纯前端计算不调接口 · 判三条:登录冷却中→该点「解锁」/ 席位已停用→该点「启用席位」/ 席位已到期→该点「延长」;⚠️ 页面注释写「四道门」是旧话,第四道「初始密码过期」9-04 已拆除)· ✅ 长官 9-08 定:手机端也做(PC 端是长官 8-08 自查踩坑后加的 · seats/page.tsx:1205)。✅ 方案图已画全(mockup-v3:970-988:绿色正常态「登录状态诊断:该席位当前可正常登录」+ 红色阻塞态逐条列原因与该点按钮 + 图注说明第三类「到期」在「有效期」区)→ 不用补图,照图做 PC 端 seats/page.tsx:51 import diagnoseSeatLogin、:963 调用、:1205 有诊断区块(Phase 70.7 · 长官 8-08 自查加的)。⚠️ 该函数在主仓 web/ 包,与结算 settlement/web/ 是两个独立 npm 包(doctor-ai-brain-web vs tongban-settlement-web)→ 必须抄一份;好在它零 import 依赖,整段搬即可。两处文件头互相加注释指认防漂移

三、批二 · 写(改配置)

任务 干什么 改哪里 关键点
T2-1 主系统新增 PUT /internal/settlement/orgs/{org_id} internal_settlement.py 参照现有 update_org(api_v1.py:420)的字段集,但不能直接复用(那个走 admin 鉴权)
T2-2 主系统新增 PATCH /internal/settlement/orgs/{org_id}/status 同上 服务层直接调现成的 org_service.set_org_active(app/services/org_service.py:442)
T2-3 core_client 补 2 个 wrapper client.py 写操作用 _TIMEOUT 30s
T2-4 平台手机端加 2 个端点 platform_mobile_orgs.py 鉴权 ORG_WRITE
T2-5 前端:编辑基本信息弹层 orgs/[orgId]/ 弹窗单列(两列会把字段名和值拆到左右半屏)
T2-6 前端:14 开关改可写 同上 分 3 组:品牌价值(4) / 访问与登录(4) / 首页功能(6)。✅ 已查实:PC 端确实有双向联动(orgs/page.tsx:548-583 —— 开「用户注册」自动关「密码登录」,反之亦然,代码注释写「二选一·防误触同开」)→ 手机端照做,保留联动
T2-7 前端:停用 / 启用 同上 收进「更多操作」二级弹层防误触(决策 10),带二次确认
T2-8 🆕 改席位资料(用户名 / 联系电话) 四层全补 PC 端 PATCH .../seats/{seat_id}/profile(admin_seats.py:209)。🔴 内部通道0 命中,核心侧要新建端点
T2-9 🆕 延长席位有效期 四层全补 PC 端 PATCH .../seats/{seat_id}/extend(admin_seats.py:233)。内部通道有相近的 /seats/renew(批量续费),但单席位 extend 语义不同,确认能不能复用,不能就新建
T2-10 🆕 席位停用 / 启用 中间层+手机端端点+界面 ✅ 内部通道已有 /seats/{user_id}/status(internal_settlement.py:827),只是 core_client 没封装 → 轻

四、批三 · 高危(涉凭证)

任务 干什么 改哪里 关键点
T3-1 主系统新增 POST /internal/settlement/orgs(通用新建机构) internal_settlement.py 🔴 不能复用 provision_org(:502)—— 那是签约专用:套餐 10 个开关写死不接受传参、幂等键绑签约编号
T3-2 主系统新增 POST /internal/settlement/orgs/{org_id}/regenerate-credential 同上 参照 admin_orgs_access_link.py:69 的实现
T3-3 主系统新增 POST /internal/settlement/orgs/{org_id}/seats/generate(批量生成席位) 同上 参照 admin_seats.py:114
T3-4 core_client 补 3 个新 wrapper + 顺手补 2 个历史缺的 client.py(现有 10 个公开 wrapper) 🔴 历史缺口:席位启停 /seats/{uid}/status(核心侧 :827 已有)、密码重置 /seats/{uid}/password-reset(:846 已有),客户端一直没封装
T3-5 平台手机端加 3 个端点 platform_mobile_orgs.py 鉴权 ORG_ADMIN
T3-6 前端:新建机构页 orgs/new/page.tsx 创建后不弹凭证窗、不回列表,直接进该机构详情页(决策 5)
T3-7 前端:凭证区(查看 / 重新生成 / 一键复制全部凭证) orgs/[orgId]/ 直接 import { buildSeatCredentialsText } from "@/lib/seat-credentials-text",不抄;api_secret 库里只存哈希,只能重新生成(决策 6);「下载 Excel」→「一键复制全部凭证」(决策 7)。⚠️ 已复验坐实:PC 端那个按钮虽然叫「复制全部凭证」(seats/page.tsx:1408),但代码是 const single = seats.length === 1 ? seats[0] : null → 多个席位时可复制文本为 null,只能下 Excel。所以它名不副实。手机端「一键复制全部凭证」确实是新增能力,不是复刻
T3-8 前端:批量生成席位 orgs/[orgId]/seats/ 生成后凭证明文只显示一次,UI 要提示
T3-9 🆕 重置席位密码 中间层+手机端端点+界面 ✅ 内部通道已有 /seats/{user_id}/password-reset(:846),core_client 没封装 → 轻。新密码明文只显示一次
T3-10 🆕 换绑使用人 四层全补 PC 端 POST .../seats/{seat_id}/reassign(admin_seats.py:175)。🔴 内部通道 0 命中,核心侧要新建。PC 端规则:未绑微信时禁用
T3-11 🆕 生成机构访问链接 四层全补 PC 端 POST /admin/orgs/{org_id}/access-link(admin_orgs_access_link.py:49)。🔴 内部通道 0 命中
T3-12 🆕 席位解锁(登录失败被锁后解开) 四层全补 PC 端 POST /api/admin/users/{user_id}/unlock(admin_users.py:203 —— ⚠️ 注意它不在 admin_seats.py,我第一轮只查 seats 文件所以漏了)。🔴 内部通道 0 命中。PC 端这个按钮没有二次确认,点了立即生效,手机端建议补一个确认

五、收尾

任务 干什么
T4-1 手机端现有 stores 页标题「机构管理」→「机构签约」(决策 1,与 PC 端结算侧对齐,避免和新功能撞名)
T4-2 🔴 全链路自测(见下节)
T4-3 三批一次性发 test,长官 UAT 后再发生产

六、自测怎么做(这条线我能自己测)

已验证:平台手机端没有微信鉴权门——

查什么 结果
后端鉴权 require_zhaoyang_permission(ZhaoYangPermission.*),权限位鉴权
前端有没有微信门 settlement/web/app/platform/minicup/ 下 23 个文件,0 个含 MicroMessenger / isInWeChat / wx-oauth(对照组 23 有效)

所以整条链路我可以自己跑完再交长官验收,不用每轮等人工测。

自测顺序(每层单独验,别攒到最后):
1. 主系统新端点 → 直接 curl 带 X-Service-Token 打(env 名 SETTLEMENT_SERVICE_TOKEN,校验在 internal_settlement.py:56-69,router 级统一挂)
2. core_client wrapper → 结算侧起服务打
3. platform_mobile 端点 → 带权限位账号打
4. 前端页面 → 浏览器真开、真点


六之二、🔴 一条架构级冲突(本次 verify 新发现 · 必须长官定)

核心侧 app/api/internal_settlement.py:305-322 那段注释写着(原文):

门店标准套餐 …… 核心侧写死
结算侧不传任何开关参数(安全红线:结算不得决定门店能用什么功能)。

也就是说:核心系统当初设计这条通道时,明确禁止结算侧决定机构能用什么功能,所以签约开通机构时 10 个开关全部由核心侧写死。

但批二 T2-6 要做的事,正是让平台手机端(= 结算侧)去改这 14 个开关 —— 与这条红线直接冲突。

三条路:

选项 说明
A. 明确豁免 新增的改开关端点明确写成「平台管理专用,不受签约红线约束」,并在代码注释里写明是长官哪天拍板豁免的
B. 换通道 改开关不走 /internal/settlement/*,另开一条平台管理专用通道(工作量增加,但红线不破)
C. 批二砍掉改开关 手机端只做只读 + 基本信息编辑,14 开关仍只能在 PC 端改

⚠️ 这条不定,批二做出来就是违反核心侧写死的安全红线,将来出事没法交代。


七、不做的(决策已定,别返工)



八、待长官定

无。 43 个任务全部有主,可以开工。

方案图与任务清单已完全对齐:图上画了的(解锁 9 处 / 换绑 10 处 / 重置密码 10 处 / 停用 28 处 / 延长 4 处 / 访问链接 7 处 / 搜索 9 处 / 筛选 4 处 / 登录状态诊断 mockup-v3:970-988)任务清单全覆盖,反之亦然。不需要补图。

记一笔教训:9-08 我曾误判「图上没有登录诊断」——因为 grep 的是「登录诊断」,而图上写的是「登录状态诊断」。当时带了对照组(「解锁」9 处),但对照组只证明 grep 能跑,不证明关键词选得对。以后查「图上有没有 X」,要用多个近义词交叉查,或直接打开那一帧看。