From 89369fd45eef4d67cce737ac0621cf283ae55ded Mon Sep 17 00:00:00 2001 From: zty Date: Tue, 7 Jul 2026 03:02:25 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20=E8=AE=BA=E5=9D=9B+=E7=8E=AF=E4=BF=9D?= =?UTF-8?q?=E5=BB=BA=E6=9D=90+=E5=AF=BC=E8=88=AA=E6=94=B9=E7=89=88?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 2026-07-07 spec:landing 导航改 4 项、环保建材拉真实材料库、 健康装修大家谈进阶版论坛(板块/楼层回帖/点赞,公开看登录发)。 Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01D7EkGJYZMdQtMeDEt3z4vL --- ...26-07-07-forum-and-eco-materials-design.md | 222 ++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 空气质量预测/源码/docs/superpowers/specs/2026-07-07-forum-and-eco-materials-design.md diff --git a/空气质量预测/源码/docs/superpowers/specs/2026-07-07-forum-and-eco-materials-design.md b/空气质量预测/源码/docs/superpowers/specs/2026-07-07-forum-and-eco-materials-design.md new file mode 100644 index 0000000..c87c3b4 --- /dev/null +++ b/空气质量预测/源码/docs/superpowers/specs/2026-07-07-forum-and-eco-materials-design.md @@ -0,0 +1,222 @@ +# 设计文档:导航改版 + 环保建材 + 健康装修大家谈论坛 + +- 日期:2026-07-07 +- 目标项目:`C:\code\空气质量预测\源码`(用户端 iapip-web / 服务端 iapip-svr) +- 工作目录:前端 `源码/用户端/iapip-web`,后端 `源码/服务端/iapip-svr` +- 状态:已通过需求评审,待写实现计划 + +--- + +## 1. 背景与目标 + +用户端首页(landing,`用户端/iapip-web/src/pages/landing/index.tsx`)当前顶部导航为 5 项(资讯科普 / 治理案例 / 如何使用 / 污染源识别 / 专业看板),前三项为页面内锚点滚动、内容全为写死的静态数据。 + +本次改动: + +1. 顶部导航改为 4 项:**健康装修科普、预测案例、环保建材、健康装修大家谈**。 +2. 「环保建材」为新区块,从后端真实公共材料库拉取 E0 低释放材料展示。 +3. 「健康装修大家谈」为新的**真功能论坛**(进阶版:板块、发帖、楼层回帖、点赞、分页;公开浏览、手机登录后发帖)。 + +前两项(健康装修科普、预测案例)仅为现有区块的**标签改名**,内容不变。 + +--- + +## 2. 范围 + +### 2.1 纳入范围 +- 导航 4 项改版与两处区块标签改名(纯前端)。 +- 环保建材:新增 1 个免认证后端接口 + landing 新区块(真实数据)。 +- 论坛:新增 3 张 Prisma 表 + 1 个后端 controller + 2 个前端页面 + 1 个前端 API 封装。 + +### 2.2 不纳入范围(YAGNI) +- 论坛板块的后台增删(板块用代码常量固定)。 +- 帖子/回帖的富文本、@提及、通知、搜索。 +- 管理端对论坛的审核后台(作者可自删自己的帖/回帖已满足基本治理;管理端审核为后续可选项)。 +- 环保建材的收藏、跳转到材料详情等交互(仅展示)。 + +--- + +## 3. A. 导航栏与 landing 文案(前端) + +文件:`用户端/iapip-web/src/pages/landing/index.tsx` + +| 新导航项 | 行为 | 实现 | +|---|---|---| +| 健康装修科普 | 滚动到 `#news` | 现「资讯科普」区块改名;`nav-links` 文案改,`#news` 区块 `sec-tag` 由「资讯 · 科普」改为「健康装修科普」 | +| 预测案例 | 滚动到 `#cases` | 现「治理案例」区块改名;`sec-tag` 由「治理案例」改为「预测案例」,内容(before/after 卡片)不变 | +| 环保建材 | 滚动到新 `#eco` | 见第 4 节 | +| 健康装修大家谈 | `history.push('/forum')` | 见第 5–7 节 | + +其他: +- 「免费试算」主按钮(`openPredict`)保留不变。 +- 原「如何使用」`#how` 区块**保留在页面**,仅从导航移除。 +- 原「专业看板」入口从导航移除;CTA 区块的「查看专业看板」按钮(`goPro`)与登录入口仍可到达工作台。 + +--- + +## 4. B. 环保建材(后端免认证接口 + 前端区块) + +### 4.1 后端接口 +- 路由:`GET /api/mtrl/eco`,**免认证**(不挂 `authn`),加在 `material.ts` 中。 +- 逻辑:查询 `PublicMaterial` 中 `deleted=0 且 display=1`、`eco_level='E0'` 的材料;若不足 8 条,补 `E1`。按 `created_at` 倒序,最多返回 8 条。 +- 返回字段(精简,不返回释放参数三件套等敏感/无关字段): + `material_id, name, category, brand, factory, eco_level, methanal`(甲醛标称值,若有)。 +- 空结果返回 `[]`,前端优雅处理。 + +### 4.2 前端区块 +- landing 新增 `#eco` 区块,位置置于 `#cases` 与 `#how` 之间(顺序:news → cases → eco → how)。 +- 组件挂载时通过 `api.material.getEcoMaterials()` 拉取,`useState` 保存。 +- 展示:卡片网格(复用现有 `.cases-grid` / `.case` 或新增等价样式),每张卡显示材料名、品牌、类别、环保等级标签(E0/E1)。 +- 加载中不阻塞页面其他区块;返回空数组时该区块整体隐藏(`data.length === 0` 则不渲染 `
`)。 +- 前端 API:`src/services/api/material.ts` 新增 `getEcoMaterials`,走 `/api/mtrl/eco`。 + +--- + +## 5. C. 论坛数据模型(Prisma) + +文件:`服务端/iapip-svr/prisma/schema.prisma`。新增 3 张表。板块(board)**不建表**,用后端常量固定。 + +### 5.1 板块常量 +在 `服务端/iapip-svr/src/common/constants.ts` 定义: +``` +FORUM_BOARDS = ['甲醛治理', '异味TVOC', '环保材料', '装修避坑', '求助问答'] +``` +帖子的 `board` 字段值必须属于该列表(后端校验)。 + +### 5.2 表结构 +```prisma +model ForumThread { + id Int @id @default(autoincrement()) + board String + title String + content String @db.Text + author_id String + author_name String + like_count Int @default(0) + reply_count Int @default(0) + created_at DateTime @default(now()) + updated_at DateTime @updatedAt + deleted Int @default(0) + replies ForumReply[] + likes ForumThreadLike[] + @@map("forum_thread") +} + +model ForumReply { + id Int @id @default(autoincrement()) + thread_id Int + thread ForumThread @relation(fields: [thread_id], references: [id], onDelete: Cascade) + floor Int + content String @db.Text + author_id String + author_name String + created_at DateTime @default(now()) + deleted Int @default(0) + @@map("forum_reply") +} + +model ForumThreadLike { + thread_id Int + thread ForumThread @relation(fields: [thread_id], references: [id], onDelete: Cascade) + user_id String + created_at DateTime @default(now()) + @@id([thread_id, user_id]) + @@map("forum_thread_like") +} +``` +- `author_name` 冗余存 username,列表/详情展示不连表。 +- 软删 `deleted` 沿用项目惯例;`ForumThreadLike` 无软删(toggle 直接删行)。 +- 迁移:`npm run dbpush:dev`(prisma db push)。 + +--- + +## 6. D. 论坛后端 API + +新建 controller `服务端/iapip-svr/src/controllers/forum.ts`,`prefix('/api/forum')`,在 `src/index.ts` 中 `app.use(forumRoutes)` 注册。登录鉴权复用 `authn(CERT_TYPE.ACCOUNT)`,作者身份取 `ctx.state.acct_cert`(`id`、`username`)。 + +| 方法 路径 | 鉴权 | 功能 | 说明 | +|---|---|---|---| +| `GET /api/forum/boards` | 公开 | 板块列表 + 各板块帖数 | 返回 `[{board, count}]`,count 统计 `deleted=0` | +| `GET /api/forum/threads` | 公开 | 分页帖子列表 | query:`board?`(缺省全部)、`page`(默认1)、`size`(默认20)。按 `created_at desc`。返回列表项 + 总数 | +| `GET /api/forum/threads/:id` | 公开 | 帖子详情 + 分页回帖 | 回帖按 `floor asc`,跳过 `deleted=1` 的回帖。best-effort:若请求带有效 token 则解析出用户并附 `liked` 布尔,否则省略该字段 | +| `POST /api/forum/threads` | 登录 | 发帖 | body:`{board,title,content}`。board 必须在 `FORUM_BOARDS`,title/content 非空且限长 | +| `POST /api/forum/threads/:id/replies` | 登录 | 回帖 | body:`{content}`。事务内 `floor = 当前 reply_count + 1`,同时 `reply_count++` | +| `POST /api/forum/threads/:id/like` | 登录 | 点赞/取消 toggle | 有 like 行则删并 `like_count--`;无则插并 `like_count++`。返回最新 `{liked, like_count}` | +| `DELETE /api/forum/threads/:id` | 登录 | 软删自己的帖 | 仅 `author_id === cert.id`,否则 403 | +| `DELETE /api/forum/replies/:id` | 登录 | 软删自己的回帖 | 同上;不回退 floor(其余楼层号保持不变)。详情接口跳过 `deleted=1` 的回帖,前端不展示已删楼层 | + +- 校验:沿用项目 AJV `validate` 中间件风格(参照 `material.ts`)。 +- 错误码:非法 board / 空标题用 `1000 INVALID_REQ_DATA`;越权删除用 `3000 FORBIDDEN`;帖子不存在用 `4000`(沿用现有粗粒度约定)。 +- 并发:回帖 floor 与 reply_count、点赞计数均在 `db.$transaction` 内完成,避免竞态。 + +--- + +## 7. E. 论坛前端 + +前端 API 封装:新建 `用户端/iapip-web/src/services/api/forum.ts`,聚合进 `src/services/api/index.ts`(`api.forum.*`)。 + +新增两个**公开路由**(无需登录即可访问,路由风格参照 landing 在 `.umirc.ts` 中的注册方式): + +### 7.1 `/forum` — 论坛首页 +- 板块 Tab(含「全部」):点击切换 `board` 过滤。 +- 帖子列表:标题、作者名、时间、回帖数、点赞数;点击行 → `/forum/thread/:id`。 +- 分页控件(Antd `Pagination`)。 +- 右上「发帖」按钮:未登录 → 弹 `PhoneAuthModal`;已登录 → 打开发帖表单(Modal:板块选择 + 标题 + 正文)。发帖成功后刷新列表。 + +### 7.2 `/forum/thread/:id` — 帖子详情 +- 帖子正文(作者、时间、板块)。 +- 点赞按钮:显示 `like_count` 与当前用户 `liked` 状态;未登录点则弹登录框,登录后 toggle。 +- 楼层回帖列表(floor + 作者 + 时间 + 内容),分页。 +- 底部回帖框:未登录点则弹登录框;已登录提交后刷新回帖列表并楼层递增。 +- 若为作者本人,帖子/自己的回帖显示「删除」入口(软删)。 + +### 7.3 登录复用 +- 统一复用现有 `PhoneAuthModal`(`src/components/phone-auth-modal`)。登录态判断沿用 `localStorage[LOC_STOR_KEY.AUTH_TOKEN]`。 +- 已登录请求自动带 `Authorization`(沿用 `services/api` 现有请求拦截)。 + +--- + +## 8. 测试 + +### 8.1 后端 +- 发帖 → 该板块帖数 +1;非法 board 被拒(1000)。 +- 连续回帖 → floor 依次 1、2、3,`reply_count` 同步;未登录回帖被拒(401/2000)。 +- 点赞 toggle:首次 +1 且 `liked=true`,再次 -1 且 `liked=false`;同一用户不重复计数。 +- 越权删除他人帖 → 403;删除自己的 → `deleted=1`,列表不再出现。 +- 分页:`page/size` 边界正确,返回总数一致。 + +### 8.2 环保建材 +- 有 E0 材料时返回 ≤8 条且字段精简;无任何公共材料时返回 `[]`,前端 `#eco` 区块隐藏、不报错。 + +### 8.3 前端 +- 未登录浏览 `/forum` 与详情正常;触发发帖/回帖/点赞时弹登录框,登录后动作继续完成。 +- 导航 4 项滚动/跳转正确;`#news`/`#cases` 改名后内容不变。 + +--- + +## 9. 涉及文件清单 + +### 后端 `服务端/iapip-svr` +- `prisma/schema.prisma`(新增 3 表) +- `src/common/constants.ts`(新增 `FORUM_BOARDS`) +- `src/controllers/forum.ts`(新建) +- `src/controllers/material.ts`(新增 `GET /eco`) +- `src/index.ts`(注册 `forumRoutes`) + +### 前端 `用户端/iapip-web` +- `src/pages/landing/index.tsx`(导航 4 项、区块改名、新增 `#eco` 区块及取数) +- `src/pages/forum/index.tsx`(新建,论坛首页) +- `src/pages/forum/thread.tsx`(新建,帖子详情) +- `src/services/api/forum.ts`(新建) +- `src/services/api/material.ts`(新增 `getEcoMaterials`) +- `src/services/api/index.ts`(聚合 `api.forum`) +- `.umirc.ts`(新增 `/forum`、`/forum/thread/:id` 公开路由) + +--- + +## 10. 风险与注意 + +- **数据库迁移**:改 schema 需 `npm run dbpush:dev`;生产库需谨慎(本地开发用 Docker MySQL)。 +- **landing 首个真实接口**:环保建材使 landing 依赖后端,需保证后端未起/空数据时页面不崩。 +- **楼层稳定性**:回帖软删后不回退 floor,避免楼层错位。 +- **登录态一致性**:论坛与现有测算入口共用手机登录 token,行为需一致。