You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6.3 KiB
6.3 KiB
发现页与「出现在发现中」设计
背景与目标
在站内提供 「发现」 入口:登录用户可浏览 自愿出现在发现列表中的其他用户,卡片展示有限公开信息(含用户配置的地址展示文案),点击进入其公开主页 /@publicSlug。
用户是否在发现中曝光、以及地址是否在卡片上展示,均在 控制台 → 个人资料 中配置,不在发现页内做复杂编辑。
需求结论(已确认)
- 发现页含义:全站用户展示名录(仅包含满足条件的用户),不是「仅看自己」的预览页。
- 设置位置:「是否出现在发现中」及地址相关展示设置在 个人资料 中维护。
- 访问控制:仅登录用户可打开发现页及列表接口;未登录访问
/discover由现有全局路由中间件重定向至/login?redirect=...;列表 API 走非公开/api/**,由服务端10.auth-guard保证未登录返回401。 - 路由:发现页路径
/discover;主导航(AppShell)在已登录时增加 「发现」 按钮/链接。
产品默认(评审未单列回复时采用的明确约定)
以下在讨论中作为 推荐默认 写入 spec,避免实现阶段歧义:
publicSlug为空:用户 不出现在 发现列表中(避免卡片无法链到合法公开主页)。若用户打开「出现在发现中」但尚未设置公开标识,在个人资料页 内联提示 并引导填写publicSlug。- 发现列表中的头像:遵守现有
avatarVisibility。当头像对公开策略为不可展示时,发现卡片使用占位(或不展示头像 URL),避免「发现」成为绕过资料隐私的渠道。
数据模型
在 packages/drizzle-pkg/database/sqlite/schema/auth.ts 的 users 表新增字段(命名实现时可与下列语义对齐):
| 字段(语义) | 类型建议 | 默认值 | 说明 |
|---|---|---|---|
| 出现在发现中 | integer(0/1)或等价 boolean |
0(false) | 默认不曝光,需用户主动打开 |
| 发现卡片地址文案 | text,可空 |
NULL |
用户自填展示用字符串(首版不做地图/结构化省市区) |
| 在发现卡片上显示地址 | integer(0/1) |
0(false) | 仅当「出现在发现中」为真时有意义;避免无文案仍出现「地址」标签 |
列表查询 必要条件(AND):
status = 'active'- 「出现在发现中」为真
publicSlug IS NOT NULL(且非空字符串)
可选:若未来需要排序(如按更新时间),可依赖 updatedAt,本 spec 不强制。
服务端 API
- 列表:
GET /api/discover/users(具体路径以实现为准,保持 REST 风格即可)。- 鉴权:必须登录;未登录
401。 - 查询参数:分页参数与项目内公开列表风格一致(如
page+ 固定pageSize常量,可新建DISCOVER_LIST_PAGE_SIZE或复用现有列表常量)。 - 响应字段(仅列展示所需,禁止返回
email、password等敏感列):publicSlugnickname(或展示名回退规则与公开页一致)avatar:仅当avatarVisibility允许对外展示时返回 URL,否则null或省略(与前端占位约定一致)discoverLocation:仅当「出现在发现中」且「在卡片上显示地址」为真且文案非空时返回;否则不返回或返回null
- 鉴权:必须登录;未登录
实现上可抽取「根据 avatarVisibility 判断是否返回头像 URL」的辅助函数,与公开资料 API 逻辑对齐,避免分叉规则。
个人资料(PATCH/PUT)
在现有 server/service/profile/updateProfile(及 server/api/me/profile.put.ts 请求体)中扩展:
discoverVisible(布尔)discoverLocation(可选字符串,长度上限与项目内其他 text 字段一致,如合理max)discoverShowLocation(布尔)
校验:
- 使用 zod 或现有校验风格与白名单更新字段。
- 当
discoverVisible === true且当前用户publicSlug为空:允许保存其他字段,但 发现列表仍不会出现该用户;前端应在个人资料页提示「需设置公开标识才会出现在发现中」。
前端
导航
app/components/AppShell.vue:已登录主导航中增加「发现」,指向/discover,高亮规则与「首页」等一致(navActive('/discover'))。
页面
- 新建
app/pages/discover/index.vue(或等价路径):- 调用列表 API,网格展示卡片(头像占位、昵称、
@publicSlug、可选地址行)。 - 卡片点击进入
/@publicSlug。 - 空状态:无任何符合条件的用户时的说明。
- 分页或「加载更多」,与项目现有列表 UX 一致。
- 调用列表 API,网格展示卡片(头像占位、昵称、
个人资料
app/pages/me/profile/index.vue(或当前资料编辑所在文件):新增分组 「发现与展示」:- 开关:出现在发现中
- 输入:地址/地区(展示文案)
- 开关:在发现卡片上显示上述地址
- 与
publicSlug的联动提示(见上文)
错误处理与边界
- 停用/非
active用户不出现在列表。 - 列表接口错误:与项目统一 API 错误展示(如 toast)一致。
- 首版 不强制 单独限流;若公开后需要,可后续对
/api/discover/users增加与公开只读接口类似的保护。
测试建议
- 中间件:未登录访问
/discover→ 重定向登录且带redirect。 - API:未登录
GET /api/discover/users→401;登录且库中无候选人 → 空列表;多名用户仅部分满足条件 → 仅返回满足者。 - 资料:切换
discoverVisible、地址开关与文案后列表字段变化符合 spec;avatarVisibility为 private 时响应中头像为空/占位。 publicSlug清空:该用户从发现列表消失。
与现有设计的关系
- 页面访问模型见
docs/superpowers/specs/2026-04-16-auth-access-control-design.md(默认受保护、/discover不在公开白名单)。 - 公开主页路径仍为
/@publicSlug,与docs/superpowers/specs/2026-04-18-person-panel-multitenant-hub-design.md一致。
实现后文档
若实现与 spec 不一致(例如产品改为「无 publicSlug 也展示但 CTA 禁用」),应 先改本 spec 再改代码,或在本文件追加修订段落并标注日期。