From 504849c14a88c93d0d81b916d148d0a12133845e Mon Sep 17 00:00:00 2001 From: xiaomai Date: Mon, 4 May 2026 16:04:58 +0800 Subject: [PATCH] feat(search): include user profiles in global search results Add users group to global search API and frontend types Query users by display name and link to their public profiles Update system wordings for the new search group --- DESIGN.md | 4 ++-- backend/src/queries.ts | 25 +++++++++++++++++++++--- frontend/src/components/GlobalSearch.vue | 3 ++- frontend/src/services/api.ts | 3 ++- system-wordings.ts | 6 ++++-- 5 files changed, 32 insertions(+), 9 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index fa03d61..1aa5bcd 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -9,7 +9,7 @@ - Home 首页路径为 `/`,用于聚合公开 Wiki 入口;Logo 导航回到 Home,用户可从 Home 进入核心资料、每日 CheckList、Life 和正在准备中的分区。 - 桌面端使用侧边栏导航,侧边栏可折叠为图标栏;移动端继续使用抽屉式侧边栏。 - 全局顶部导航栏承载语言切换、通知、User Profile 和登录 / 退出等账号操作;除 User Profile 可展示用户名外,顶部操作以图标按钮呈现。 -- 全局顶部导航栏提供全站搜索。搜索结果按内容类型分组展示,覆盖 Pokemon、Habitats、Items、Ancient Artifacts、Recipes、Daily CheckList 和公开可见的 Life Post;结果跳转到对应公开详情页或页面锚点。 +- 全局顶部导航栏提供全站搜索。搜索结果按内容类型分组展示,覆盖 Pokemon、Habitats、Items、Ancient Artifacts、Recipes、Daily CheckList、公开可见的 Life Post 和公开用户 Profile;结果跳转到对应公开详情页、页面锚点或 `/profile/:id`。 - 管理入口用于维护全局配置、语言、系统文案、列表排序和每日 CheckList。 ## 技术栈 @@ -24,7 +24,7 @@ - `DESIGN.md` 是产品行为、数据结构和 API 暴露边界的单一事实来源。 - API 只返回业务需要的字段,不返回密码、token hash、验证 token、内部调试字段或不必要的元数据。 -- 全局搜索 API 只返回公开浏览所需的最小结果字段:结果类型、ID、展示标题、目标 URL、可选摘要和可选图片;不返回编辑审计、权限、审核原因、内部字段或调试信息。 +- 全局搜索 API 只返回公开浏览所需的最小结果字段:结果类型、ID、展示标题、目标 URL、可选摘要和可选图片;用户搜索结果只使用公开 Profile 所需的 `id`、`displayName` 和目标 URL,不返回邮箱、角色、权限、Referral、编辑审计、审核原因、token/hash、内部字段或调试信息。 - 用户界面只展示业务数据和设计内的文案,不展示提示词、计划、调试信息、字段内部名或修改说明。 - 可编辑 Wiki 内容必须记录创建者、最后编辑者、创建时间、最后编辑时间和编辑历史。 - 列表顺序由 `sort_order` 控制,默认按创建时间旧到新初始化,排序值按 10 递增以便后续插入和拖拽排序。 diff --git a/backend/src/queries.ts b/backend/src/queries.ts index 245fa38..f0148be 100644 --- a/backend/src/queries.ts +++ b/backend/src/queries.ts @@ -43,7 +43,8 @@ type GlobalSearchGroupType = | 'ancient-artifacts' | 'recipes' | 'daily-checklist' - | 'life'; + | 'life' + | 'users'; type GlobalSearchItem = { id: number; type: GlobalSearchGroupType; @@ -2466,7 +2467,7 @@ export async function globalSearch(paramsQuery: QueryParams = {}, locale = defau const checklistTitle = localizedField('daily-checklist-items', 'c.id', 'c.title', 'title', locale); const lifeCategoryName = localizedName('life-tags', 'lc', locale); - const [pokemon, habitats, items, artifacts, recipes, checklist, life] = await Promise.all([ + const [pokemon, habitats, items, artifacts, recipes, checklist, life, users] = await Promise.all([ query( ` SELECT @@ -2604,6 +2605,23 @@ export async function globalSearch(paramsQuery: QueryParams = {}, locale = defau LIMIT $2 `, [pattern, limit] + ), + query( + ` + SELECT + u.id, + 'users' AS type, + u.display_name AS title, + '/profile/' || u.id AS url, + NULL AS summary, + NULL AS meta, + NULL AS image + FROM users u + WHERE u.display_name ILIKE $1 + ORDER BY lower(u.display_name), u.id + LIMIT $2 + `, + [pattern, limit] ) ]); @@ -2614,7 +2632,8 @@ export async function globalSearch(paramsQuery: QueryParams = {}, locale = defau { type: 'ancient-artifacts', items: artifacts }, { type: 'recipes', items: recipes }, { type: 'daily-checklist', items: checklist }, - { type: 'life', items: life } + { type: 'life', items: life }, + { type: 'users', items: users } ]; return { query: search, groups: groups.filter((group) => group.items.length > 0) }; diff --git a/frontend/src/components/GlobalSearch.vue b/frontend/src/components/GlobalSearch.vue index f46056c..9afc6ec 100644 --- a/frontend/src/components/GlobalSearch.vue +++ b/frontend/src/components/GlobalSearch.vue @@ -36,7 +36,8 @@ const groupLabels: Record = { 'ancient-artifacts': 'search.groups.ancientArtifacts', recipes: 'search.groups.recipes', 'daily-checklist': 'search.groups.dailyChecklist', - life: 'search.groups.life' + life: 'search.groups.life', + users: 'search.groups.users' }; function clearSearchTimeout() { diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts index 7a5cebd..53d88ef 100644 --- a/frontend/src/services/api.ts +++ b/frontend/src/services/api.ts @@ -325,7 +325,8 @@ export type GlobalSearchGroupType = | 'ancient-artifacts' | 'recipes' | 'daily-checklist' - | 'life'; + | 'life' + | 'users'; export interface GlobalSearchItem { id: number; diff --git a/system-wordings.ts b/system-wordings.ts index 1976853..38b4102 100644 --- a/system-wordings.ts +++ b/system-wordings.ts @@ -90,7 +90,8 @@ export const systemWordingMessages = { ancientArtifacts: 'Ancient Artifacts', recipes: 'Recipes', dailyChecklist: 'Daily CheckList', - life: 'Life' + life: 'Life', + users: 'Users' } }, notifications: { @@ -1409,7 +1410,8 @@ export const systemWordingMessages = { ancientArtifacts: 'Ancient Artifacts', recipes: '材料单', dailyChecklist: '每日 CheckList', - life: 'Life' + life: 'Life', + users: '用户' } }, notifications: {