7.5 KiB
7.5 KiB
小程序首页第二屏配置 - 品牌故事 + 主题卡片入口
- 日期: 2026-04-18
- PR: #811 (Closes #810)
- 服务: hl-user-service (8081) + hl-mp-service (8085)
- 状态: 已合并到 dev,待测试环境部署
背景
小程序首页分两屏:
- 第一屏:Hero 大图/轮播(现有
sys_banner,不变) - 第二屏(本次新增):品牌故事文案 + 大主图 + 2 张主题卡片入口
原型节点:用户端页面(5).pen / M57M7 首页 / Screen2 plwfj
1. 管理端接口(Admin)
1.1 查询品牌故事(含草稿)
GET /admin/home-config/brand-story
Authorization: Bearer <admin token>
响应 Result<AdminBrandStoryRespVO>:
{
"code": 200,
"data": {
"id": "2045369223068311553",
"title": "每一条线路,我们都亲自走过。",
"subtitle": null,
"description": "整个呼伦贝尔...",
"coverUrl": "https://cdn.example.com/home/team-photo.jpg",
"coverMaskType": "WHITE_TOP",
"version": 1,
"status": "ACTIVE",
"createTime": "2026-04-18 13:09:59",
"updateTime": "2026-04-18 13:09:59",
"createdBy": "1002",
"updatedBy": "1002",
"createdByName": null,
"updatedByName": null
},
"success": true
}
字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | String | 主键(雪花ID,JS 精度防护用 String) |
| title | String | 主标题,支持 \n 换行 |
| subtitle | String? | 副标题(预留,当前可为 null) |
| description | String | 描述文案 |
| coverUrl | String? | 主图 URL(草稿可空;发布时必填) |
| coverMaskType | String | 蒙层类型:WHITE_TOP=上白下透 / NONE=无 |
| version | Integer | 版本号,publish 每次 +1 |
| status | String | DRAFT=草稿 / ACTIVE=已发布 / ARCHIVED=已归档 |
| createdBy/updatedBy | String | 操作人 adminId(String 防 JS 精度) |
语义:
- 返回当前最新一条非删除记录(按 id 降序,包含 DRAFT)
- 管理端打开页面即可编辑当前状态,不管是草稿还是已发布
- 无数据时返回
data: null(首次部署时可能出现)
1.2 保存或发布品牌故事
PUT /admin/home-config/brand-story
Authorization: Bearer <admin token>
Content-Type: application/json
请求体 AdminBrandStorySaveReqVO:
{
"id": null,
"title": "每一条线路,我们都亲自走过。",
"subtitle": null,
"description": "整个呼伦贝尔...",
"coverUrl": "https://cdn.example.com/home/team-photo.jpg",
"coverMaskType": "WHITE_TOP",
"publish": true
}
字段校验:
| 字段 | 校验 | 说明 |
|---|---|---|
| id | 选填 | 有值=更新指定记录,null=新建 |
| title | @NotBlank, @Length(max=100) | 必填 |
| subtitle | @Length(max=200) | 选填 |
| description | @NotBlank, @Length(max=1000) | 必填 |
| coverUrl | @NotBlank, @URL(protocol="https") | 必填(只接受 https 链接) |
| coverMaskType | @NotBlank | 必填,值同上 |
| publish | Boolean | true=直接发布(归档旧 ACTIVE,新记录设为 ACTIVE);false=保存草稿 |
行为:
publish=false:saveDraft插入/更新status=DRAFT记录publish=true:publish先archiveAllActive()把当前 ACTIVE 改 ARCHIVED,再插入 ACTIVE 新记录;@Lock4j防并发;自动 evict Redis 缓存
响应 Result<Void>:{"code":200, "message":"成功", "data":null, "success":true}
幂等保护:@Idempotent(timeout=5, keyPrefix="admin-brand-story"),5 秒内重复请求返回缓存结果。
审计:@OperationLog("发布小程序首页第二屏品牌故事"),操作日志自动写入。
2. 小程序端接口(MP)
2.1 获取首页第二屏数据(聚合)
GET /mp/home-config/screen2
不需要 token(Gateway 设为 X-Auth-Level=SKIP,匿名可访问)。
响应 Result<MpHomeScreen2VO>:
{
"code": 200,
"data": {
"brandStory": {
"title": "每一条线路,我们都亲自走过。",
"subtitle": null,
"description": "整个呼伦贝尔...",
"coverUrl": "https://cdn.example.com/home/team-photo.jpg",
"coverMaskType": "WHITE_TOP"
},
"topics": [
{
"id": "XXXX",
"title": "季节之旅",
"subtitle": "夏/秋/冬三季体验",
"coverUrl": "https://cdn.example.com/topics/season.jpg",
"linkType": "PRODUCT_LIST",
"linkTarget": "theme=SEASON"
}
]
},
"success": true
}
字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
| brandStory | Object? | 品牌故事块,无数据时为 null |
| brandStory.title | String | 主标题,支持 \n 换行 |
| brandStory.subtitle | String? | 副标题 |
| brandStory.description | String | 描述 |
| brandStory.coverUrl | String? | 主图 URL |
| brandStory.coverMaskType | String | 蒙层类型 WHITE_TOP/NONE |
| topics | Array | 主题卡片列表(按 sort_order 升序) |
| topics[].id | String | 主题 ID |
| topics[].title | String | 卡片标题 |
| topics[].subtitle | String? | 卡片副标题 |
| topics[].coverUrl | String | 卡片封面图 |
| topics[].linkType | String | 跳转类型,如 PRODUCT_LIST / TOPIC_DETAIL |
| topics[].linkTarget | String | 跳转目标(路径/ID/参数) |
关键:响应不包含 fromFallback 字段(内部 Feign 协议字段,VO 层已过滤)。
3. 缓存策略
| 项 | 值 |
|---|---|
| Key | home:screen2:mp:{env}(env=dev/test/prod) |
| TTL | 30 分钟(1800s) |
| 空值 TTL | 60 秒(防穿透) |
| Evict 触发 | brand PUT / topic CUD 任一发生 |
前端渲染建议:小程序可本地缓存 5 分钟减少重复请求;发现数据变化再 pull。
4. 错误处理
所有错误走项目统一错误码,HTTP 始终 200,业务码在 code 字段:
| code | 含义 | 常见场景 |
|---|---|---|
| 200 | 成功 | — |
| 400 | 参数校验失败 | title 缺失 / coverUrl 非 https |
| 401 | 未登录 | admin token 过期 |
| 403 | 无权限 | 非 admin 访问 /admin/** |
| 409 | 状态冲突 | 禁止 ACTIVE → DRAFT 回退 |
| 500 | 服务器错误 | DB/Redis 异常 |
5. 管理端前端需要做的
- 页面:首页配置 / 第二屏
- 表单字段:title / subtitle / description / coverUrl(走素材库 system 分类上传) / coverMaskType(下拉 WHITE_TOP/NONE)
- 操作按钮:
保存草稿(publish=false) /立即发布(publish=true,需二次确认弹窗) - 首次打开:调 GET 接口,有数据则回填表单,无数据则空表单
- 发布后提示:调 GET 验证 status=ACTIVE,提示"发布成功,小程序缓存最多 30 分钟后生效"
6. 小程序前端需要做的
- 首页渲染:调
GET /mp/home-config/screen2 - 数据位置:
brandStory.coverUrl作为第二屏大图背景(375×497 区)brandStory.title+brandStory.description浮于大图之上(位置 x=20, y=50 参考原型)topics渲染为 2 个横向卡片(位置 x=20, y=520, 335×220 参考原型)
- 空态:
brandStory=null时第二屏隐藏;topics=[]时卡片区隐藏 - shimmer/skeleton:请求中显示骨架屏
7. 变更影响
- 现有
/admin/banner/*接口:不变(第一屏轮播独立) - 现有
/mp/banner/active接口:不变 - 现有
/admin/topic/*接口:不变(本期复用 sys_topic,不动表结构) - TopicService 新增方法:仅内部调用,不暴露接口