# 隐蔽未实现接口与逻辑清单 本报告聚焦于"看起来已实现、实际未接通或存在断层"的功能点。这些问题在 mock 演示阶段不易察觉,切换真实后端后会表现为数据丢失、页面空态、交互无效等事故。 与第一份报告(《静态模拟数据残留审计报告》)互补,本报告不再重复 mock 数据源、store 初始化、伪 API 层等已知问题。 --- ## 一、登录态与持久化缺失 ### 1.1 记住登录状态 — 纯展示控件 登录页渲染了"记住登录状态"复选框,但该控件未被表单注册,无任何读取或写入逻辑。 | 位置 | 内容 | | ------------------------------ | -------------------------------------------------------------------------- | | `app/(auth)/login/page.tsx:86` | `` — 未绑定 `register("remember")` 或 `useState` | ### 1.2 登录态无持久化 — 刷新即丢 全仓库未发现 `localStorage`、`sessionStorage`、`cookie` 写入,也未使用 `zustand/middleware` 的 `persist`。`store/auth.ts` 的 `login()` 仅写内存态,页面刷新后回到未登录状态。 **影响**:切真实后端后,如果前端仍依赖 Zustand 内存态判断登录,刷新会导致用户被踢出。 --- ## 二、身份与实体模型错位 ### 2.1 当前用户与店铺/打手 ID 不匹配 — 后台大面积空态 登录固定为 `mockUsers[0]`(id=`u1`),但 mock 数据中的店铺 owner 是 `u10`/`u11`/`u12`,打手是 `u5`/`u6` 起。 | 位置 | 内容 | | -------------------------------------- | ---------------------------------------------- | | `lib/mock/users.ts:119` | `currentUser = mockUsers[0]` — 固定 `u1` | | `lib/mock/shops.ts:7` 起 | 店铺 owner 为 `u10`、`u11`、`u12` | | `lib/mock/players.ts:7` 起 | 打手 user 从 `u5` 开始 | | `lib/domain/resolve-current-shop.ts:5` | `shops.find(shop => shop.owner.id === userId)` | **结果**:用户切换到店主身份后,`resolveOwnerShop` 始终返回 `null`,以下页面全部显示"当前账号没有可管理的店铺": - `app/(dashboard)/dashboard/shop/employees/page.tsx:67` - `app/(dashboard)/dashboard/shop/rules/page.tsx:29` - `app/(dashboard)/dashboard/shop/income/page.tsx:28` - `app/(dashboard)/dashboard/shop/page.tsx` 同理 - `app/(dashboard)/dashboard/shop/templates/page.tsx` 同理 - `app/(dashboard)/dashboard/shop/orders/page.tsx` 同理 ### 2.2 打手主页链接指向不存在的打手 导航栏在 player 身份下跳转 `/player/${user.id}`(`components/header.tsx:88`),但 `user.id` 是 `u1`,`mockPlayers` 中没有 id 为 `u1` 的打手,会触发 `notFound()`(`app/(main)/player/[id]/page.tsx:25`)。 --- ## 三、用户动作无持久化 — 刷新即回退 以下交互在前端有即时反馈,但数据仅存在于 Zustand 内存态,刷新页面或换设备后全部丢失。 ### 3.1 点赞 | 位置 | 行为 | | --------------------------------------- | --------------------------------------------------------------------- | | `components/post-like-button.tsx:21-32` | 调用 `togglePostLike` → `store/posts.ts:50-61` 本地翻转 `liked` 并 ±1 | ### 3.2 评论 | 位置 | 行为 | | ------------------------------------- | ------------------------------------------------ | | `components/post-comments.tsx:29-52` | 调用 `addComment` → `store/comments.ts` 本地追加 | | `components/post-comment-count.tsx:3` | 读 `useCommentStore` 本地计数 | ### 3.3 收藏 | 位置 | 行为 | | -------------------------------------- | ----------------------------------------------------- | | `components/favorite-button.tsx:29-33` | 调用 `toggleFavorite` → `store/favorites.ts` 本地增删 | ### 3.4 通知已读 | 位置 | 行为 | | -------------------------------------- | ----------------------------------------------------------------------- | | `app/(account)/notifications/page.tsx` | 调用 `markAsRead` / `markAllAsRead` → `store/notifications.ts` 本地标记 | | `components/header.tsx:81-83` | 未读数通过 `.filter(n => !n.read).length` 本地计算 | ### 3.5 设置保存 | 位置 | 行为 | | --------------------------------- | ---------------------------------------------------- | | `app/(account)/settings/page.tsx` | 昵称、简介、通知偏好等修改仅写 `useAuthStore` 内存态 | --- ## 四、上传功能 — 占位或 Object URL 所有"上传"操作要么是纯占位 UI,要么使用 `URL.createObjectURL` 生成本地临时链接,刷新后失效。 ### 4.1 头像上传 | 位置 | 行为 | | ------------------------------------ | ------------------------------------------------------------- | | `app/(account)/settings/page.tsx:75` | `setAvatar(URL.createObjectURL(file))` — 本地预览,无上传请求 | ### 4.2 聊天图片发送 | 位置 | 行为 | | ------------------------------------ | --------------------------------------------------------------------------------- | | `app/(order)/chat/[id]/page.tsx:152` | `sendImageMessage(session.id, URL.createObjectURL(file))` — blob URL 作为消息内容 | ### 4.3 争议证据上传 | 位置 | 行为 | | --------------------------------------- | ------------------------------------------------------------------------------- | | `app/(order)/dispute/[id]/page.tsx:88` | `URL.createObjectURL(file)` 生成预览,提交时传入 store | | `app/(order)/dispute/[id]/page.tsx:102` | `URL.revokeObjectURL` 移除时释放 — 说明开发者意识到了临时性,但未替换为真实上传 | ### 4.4 身份认证证明材料 — 纯占位 | 位置 | 行为 | | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `app/(account)/verify/page.tsx:170-181` | 三个 `
` 占位块(身份证正面/反面/游戏截图),有 `cursor-pointer` 样式但无 ``、无 `onClick`、无状态绑定 | ### 4.5 发帖图片 — 假计数 + 固定路径 | 位置 | 行为 | | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | | `app/(main)/post/new/page.tsx:46` | `imageCount` 状态仅做数字加减 | | `app/(main)/post/new/page.tsx:84` | 提交时 `images: Array.from({ length: imageCount }).map(() => "/posts/p1-1.jpg")` — 无论"上传"几张,全部指向同一张固定图片 | --- ## 五、客户端与服务端数据隔离 部分页面是 Server Component(或在服务端执行的函数),通过 `lib/api/*` 读取数据;而写入操作发生在客户端 Zustand store。在 Next.js 的 SSR/RSC 模式下,服务端无法读取客户端 store 的最新状态。 ### 5.1 发帖 → 帖子详情 | 写入 | 读取 | | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | `app/(main)/post/new/page.tsx:79` → `store/posts.ts:44` (client) | `app/(main)/post/[id]/page.tsx:17` → `lib/api/posts.ts:10` → `usePostStore.getState()` (server?) | ### 5.2 店铺模板保存 → 店铺主页 | 写入 | 读取 | | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | | `app/(dashboard)/dashboard/shop/templates/page.tsx:120` → `store/shops.ts` (client) | `app/(main)/shop/[id]/page.tsx:29` → `shop.templateConfig.sections` (server) | ### 5.3 服务发布 → 打手详情页 | 写入 | 读取 | | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | | `app/(dashboard)/dashboard/services/new/page.tsx:132` → `store/services.ts` (client) | `app/(main)/player/[id]/page.tsx:29-32` — 优先读 `player.services`(mock 内嵌数据),仅当为空时才 fallback 到 `listServicesByPlayer` | **特别注意**:打手详情页的 `player.services && player.services.length > 0` 判断(`app/(main)/player/[id]/page.tsx:30`)意味着只要 mock 数据中打手自带了 services,新发布的服务就永远不会显示。这是一个数据遮蔽问题,不仅仅是隔离问题。 --- ## 六、纯前端筛选/排序/统计 以下逻辑在前端内存中对全量数据做 filter/sort/slice,mock 阶段数据量小时体验正常,接后端引入分页、权限、跨端一致性后会出现偏差。 ### 6.1 社区列表 — 内存排序与筛选 | 位置 | 行为 | | ------------------------------------- | ---------------------------------------------------------- | | `app/(main)/community/page.tsx:22-34` | 全量 `posts.filter().sort()` 实现"最新/最热"和游戏标签筛选 | ### 6.2 订单列表 — 内存角色过滤 + Tab 过滤 | 位置 | 行为 | | ------------------------------------ | ----------------------------------------------------------------- | | `app/(order)/orders/page.tsx:91-103` | 先按 `consumerId/playerId/shopId` 过滤角色视角,再按 tab 过滤状态 | ### 6.3 管理后台概览 — 硬取首项 + 截断 | 位置 | 行为 | | --------------------------------------- | --------------------------------------------------------- | | `app/(dashboard)/dashboard/page.tsx:17` | `listPlayers()[0]` — 始终取第一个打手的数据作为"我的"数据 | | `app/(dashboard)/dashboard/page.tsx:18` | `listShops()[0]` — 始终取第一个店铺 | | `app/(dashboard)/dashboard/page.tsx:19` | `listOrders().slice(0, 3)` — 最近订单截前 3 条 | ### 6.4 首页推荐 — 全量渲染 | 位置 | 行为 | | --------------------------- | ---------------------------------------------------------------------- | | `app/(main)/page.tsx:12-14` | `listPlayers()` / `listShops()` 全量获取后直接渲染,无推荐算法、无分页 | ### 6.5 收入统计 — 正则匹配交易描述关联订单 | 位置 | 行为 | | ------------------------------------------------------ | -------------------------------------------------------------------- | | `app/(dashboard)/dashboard/shop/income/page.tsx:51-53` | `transaction.description.match(/ord[-\d]+/)` 从描述文本中提取订单 ID | **风险**:后端描述格式变化时,统计数据会静默丢失,不报错。应由后端提供结构化的 `orderId` 字段。 --- ## 七、消息列表未按用户过滤 | 位置 | 行为 | | -------------------------------------- | ----------------------------------------------------------- | | `app/(order)/chat/page.tsx:12` | `sessions` 直接全量渲染,未过滤当前用户是否为参与者 | | `app/(order)/chat/page.tsx:21-23` | 仅用 `participant.id !== userId` 找"对方",但不排除无关会话 | | `app/(order)/chat/[id]/page.tsx:52-59` | 会话详情页才做参与者校验 | **结果**:列表页会展示当前用户不参与的会话,点进去才提示无权查看。 --- ## 八、店铺规则 — 可保存但不执行 ### 8.1 规则字段仅做展示 | 字段 | 保存位置 | 执行位置 | | ------------------------------------ | ----------------------------------------------------------------- | ----------------------------------------------------- | | `allowMultiShop` | `app/(dashboard)/dashboard/shop/rules/page.tsx:50` → `updateShop` | 无 — 未发现任何校验逻辑 | | `allowIndependentOrders` | 同上 | 无 — 未发现任何校验逻辑 | | `dispatchMode` | 同上 | `store/orders.ts:407` — 仅影响前端自动派单模拟 | | `commissionType` / `commissionValue` | 同上 | `lib/domain/income.ts:22-31` — 仅影响前端收入计算展示 | ### 8.2 员工邀请 — 无校验直接重绑定 | 位置 | 行为 | | --------------------------------------------------------- | --------------------------------------------------------- | | `app/(dashboard)/dashboard/shop/employees/page.tsx:74-79` | 点击"邀请打手"直接调用 `assignToShop` + `playerCount + 1` | | `store/players.ts:13-17` | `assignToShop` 仅修改 `shopId`/`shopName` 字段,无校验 | **缺失**:未检查打手是否已属于其他店铺、是否符合 `allowMultiShop` 规则、是否需要打手同意。 ### 8.3 公告编辑 — `window.prompt` 无审计 | 位置 | 行为 | | --------------------------------------------- | ------------------------------------------ | | `app/(dashboard)/dashboard/shop/page.tsx:157` | `window.prompt("", announcement)` 编辑公告 | | `app/(dashboard)/dashboard/shop/page.tsx:175` | `window.prompt("", "")` 新增公告 | --- ## 九、社区列表的点赞/评论入口缺失 | 位置 | 行为 | | --------------------------------------- | -------------------------------------------------------------- | | `app/(main)/community/page.tsx:145-154` | 点赞和评论图标是 `` 内的纯展示元素,无 `onClick`,无链接 | 帖子详情页(`components/post-like-button.tsx`、`components/post-comments.tsx`)有完整的点赞和评论交互,但社区列表页的卡片上这些图标仅做数字展示,用户无法在列表页直接操作。 --- ## 十、置顶/精选 — 有字段和展示,无操作入口 | 位置 | 内容 | | ----------------------------------- | ------------------------------------------------------------------------------------------- | | `lib/types.ts:177` | `Post` 类型定义 `pinned: boolean` | | `lib/mock/posts.ts:17` 等 | mock 帖子中有 `pinned: true` | | `app/(main)/community/page.tsx:104` | 展示 `` 图标 | | `store/posts.ts:40` | `createPost` 强制 `pinned: false` | | `store/posts.ts:17-22` | store 接口仅有 `createPost` / `togglePostLike` / `incrementCommentCount`,无 pin/unpin 方法 | `PLAN.md:141` 规划了"用户自己置顶,最多 N 条",但目前无任何操作路径可以改变 `pinned` 状态。 --- ## 十一、关注与推送 — 文案存在,实现缺席 ### 11.1 关注 | 位置 | 内容 | | ------------------------------------- | ------------------------------- | | `PLAN.md:9` | "消费者可以收藏/关注打手或店铺" | | `app/(account)/settings/page.tsx:234` | 通知偏好文案"点赞、评论、关注" | 全仓库未发现 `follow`/`关注` 相关的 store、api、页面动作。 ### 11.2 浏览器推送 | 位置 | 内容 | | ------------- | ------------------------------- | | `PLAN.md:216` | "站内通知 + 用户可选浏览器推送" | 全仓库未发现 `Notification.requestPermission`、`serviceWorker`、`PushSubscription`、`pushManager` 等 Web Push 相关代码。现有通知体系仅为本地生成 + 本地已读。 --- ## 十二、硬编码展示值 这些数值直接写在 JSX 中,不来自任何 store 或 API 计算。 | 位置 | 内容 | | --------------------------------------- | -------------------------------------------------------- | | `app/(account)/wallet/page.tsx:154` | `¥1,280.00`(本月收入) | | `app/(account)/wallet/page.tsx:158` | `¥320.00`(待结算) | | `app/(account)/wallet/page.tsx:162` | `¥5,400.00`(已提现) | | `app/(dashboard)/dashboard/page.tsx:79` | `¥12,800`(店主本月收入) | | `app/(auth)/layout.tsx:4-8` | `12,000+` 认证打手 / `98.6%` 好评率 / `50,000+` 完成订单 | | `app/(order)/dispute/[id]/page.tsx:377` | UI 文案含"模拟处理结果"字样 | --- ## 十三、未使用的基础设施 ### 13.1 `requestWithAuth` — 定义未调用 | 位置 | 内容 | | --------------------- | ----------------------------------------------- | | `lib/api/client.ts:9` | 定义了 `requestWithAuth(executor, options?)` | | 全仓库 | `rg "requestWithAuth("` 命中 0 处调用 | ### 13.2 `usePlayerStatusStore` — 定义未调用 | 位置 | 内容 | | --------------------------- | ----------------------------------------------------------- | | `store/player-status.ts:11` | 定义了 `usePlayerStatusStore`,含 `statuses` 和 `setStatus` | | 全仓库 | `rg "usePlayerStatusStore"` 仅命中定义处 | `PLAN.md:107` 规划了"打手有并发接单上限,搜索结果和打手详情页展示'可接单/忙碌'状态",该 store 疑似为此功能的未完成基础设施。 --- ## 迁移优先级建议 ### P0 — 上线前必须解决(数据安全/业务正确性) 1. 登录态持久化(刷新丢失 = 用户无法正常使用) 2. 身份与实体 ID 对齐(否则后台全部空态) 3. 所有用户写入动作接入后端持久化(点赞/评论/收藏/设置/通知已读) 4. 上传功能替换为真实文件上传(头像/聊天图片/争议证据/认证材料/发帖图片) 5. 移除 UI 中的"模拟"字样(`dispute/[id]/page.tsx:377`) ### P1 — 切后端时必须改造(数据一致性) 6. 消息列表按当前用户过滤会话 7. 筛选/排序/统计改为后端分页查询(社区/订单/首页推荐/收入统计) 8. 打手详情页移除 `player.services` 优先读取逻辑,统一从服务列表查询 9. 店铺规则执行逻辑落地(`allowMultiShop`/`allowIndependentOrders` 校验) 10. 员工邀请增加校验流程 11. 收入统计改用结构化字段关联订单 ### P2 — 功能补齐或明确下线 12. 记住登录状态功能实现或移除控件 13. 置顶/精选操作入口 14. 关注功能 15. 浏览器推送 16. 打手在线状态(`usePlayerStatusStore` 接入或移除) 17. 公告编辑替换 `window.prompt` 为正式表单