白标化 L1 零代码换皮:admin-console 品牌配置页(表单 + 校验 + fixture 自测,零后端等待)。
日期:2026-09-14。闸门:admin-console/src 🔒一号(可直推)。
| 项 | 冻结值 | 核验来源 | 结果 |
|---|---|---|---|
| 读 | GET /api/miniapp/config/site → {code:0,data:{...品牌5字段在顶层...}} | backend/app/routers/miniapp_site.py L50-84 | ✅ |
| 写 | PUT /api/miniapp/config/site/brand body {value:{brandName,slogan,tibetanText,logoUrl,heroBgUrl}} | 同文件 L86-118 | ✅ |
| 写响应 | {code:0,data:{key:'brand',updated_by:<uid>,name:'品牌信息'}} | 同上 | ✅ |
| 403 | 非 staff/admin → 403「需要员工权限」 | L101-106 | ✅ 前端显式提示 |
| 422 | validate_config('brand', value),brand 必须是对象 | site_config_registry.py L114-119 | ✅ 前端更严先拦+422兜底提示 |
| 读打平/写嵌套 | GET 把 site_config.brand 子对象打平到顶层;PUT 整对象写 brand 单 key | miniapp_site.py L57-61 / L110-114 | ✅ service 层收口不对称 |
| 生产实证 | GET 生产返回 brandName/slogan/tibetanText/logoUrl/heroBgUrl 全 5 字段顶层 | curl https://api.kailashi.cn/api/miniapp/config/site 2026-09-14 | ✅ 实测 |
生产 GET 实测片段:
{"code":0,"data":{"brandName":"神山秘境","slogan":"在世界的屋脊,遇见真正的自己","tibetanText":"གངས་རིན་པོ་ཆེ","logoUrl":"https://doc.kailashi.cn/assets/miniprogram/kailash-logo-horiz-376.png","heroBgUrl":"https://doc.kailashi.cn/assets/miniprogram/kailash-bg.jpg",...}}
| 文件 | 说明 |
|---|---|
| admin-console/src/services/brand.ts | 品牌 service:BrandDraft 类型 + 字段元数据单一来源 BRAND_FIELDS + validateBrand 纯函数 + isHttpUrl + siteViewToDraft(打平→表单)+ fetchBrand/saveBrand(读/写) |
| admin-console/src/services/brand.fixture.ts | fixture:GET 视图/期望 draft/PUT body/PUT 响应/最小集/边界集 + 11 条非法样例 |
| admin-console/scripts/selftest-brand.mjs | 零后端零网络自测(ts.transpileModule 加载源 + data:URL),47 断言 |
| admin-console/src/pages/BrandConfig.tsx | 页面:5 字段表单 + 逐字段校验/红框/字数计数 + 保存/撤销/dirty 态 + 加载失败诚实重试 + 403/422 区分提示 + C 端手机实时预览(home 顶栏+分享卡,logo/hero 缺省降级) |
| admin-console/src/pages/BrandConfig.css | 沿用后台暖米/墨绿体系,响应式(≤980px 单栏) |
| src/App.tsx / src/components/menu.ts | 路由 /brand-config + 菜单项「品牌配置 B」 |
| 字段 | 必填 | 上限 | 规则 |
|---|---|---|---|
| brandName | ✅ | 20 | trim 后非空 |
| slogan | — | 40 | — |
| tibetanText | — | 40 | — |
| logoUrl | — | 500 | 非空时必须 http(s) 绝对地址 |
| heroBgUrl | — | 500 | 同上 |
非字符串(number/object 污染)逐字段拦截;提交即 trim 归一。
node admin-console/scripts/selftest-brand.mjs → 47 通过 / 0 失败,exit 0(输出见 selftest-output.txt)
覆盖:①GET 打平映射(含空/脏值归一)②合法/最小/边界集 ③11 条非法样例逐字段命中且无误伤 ④URL 判定 ⑤PUT 写契约 shape(仅 value 键、恰 5 字段、响应 key=brand)⑥字段元数据完整性。
npx tsc --noEmit:exit 0npx vite build:exit 0(dist CSS 25.06 kB / JS 228.39 kB)本包交付的是「契约对齐 + 422/403 错误面 + fixture 全绿」,写链路端到端待小冈 323 后端插点后/staging 机位代跑验收(328 验收标准中的 L3 半句)。