airpredict/空气质量预测/源码/docs/superpowers/specs/2026-07-07-forum-and-eco-ma...

11 KiB
Raw Permalink Blame History

设计文档:导航改版 + 环保建材 + 健康装修大家谈论坛

  • 日期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') 见第 57 节

其他:

  • 「免费试算」主按钮(openPredict)保留不变。
  • 原「如何使用」#how 区块保留在页面,仅从导航移除。
  • 原「专业看板」入口从导航移除CTA 区块的「查看专业看板」按钮(goPro)与登录入口仍可到达工作台。

4. B. 环保建材(后端免认证接口 + 前端区块)

4.1 后端接口

  • 路由:GET /api/mtrl/eco免认证(不挂 authn),加在 material.ts 中。
  • 逻辑:查询 PublicMaterialdeleted=0 且 display=1eco_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 则不渲染 <section>)。
  • 前端 APIsrc/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 表结构

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:devprisma db push

6. D. 论坛后端 API

新建 controller 服务端/iapip-svr/src/controllers/forum.tsprefix('/api/forum'),在 src/index.tsapp.use(forumRoutes) 注册。登录鉴权复用 authn(CERT_TYPE.ACCOUNT),作者身份取 ctx.state.acct_certidusername)。

方法 路径 鉴权 功能 说明
GET /api/forum/boards 公开 板块列表 + 各板块帖数 返回 [{board, count}]count 统计 deleted=0
GET /api/forum/threads 公开 分页帖子列表 queryboard?(缺省全部)、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_BOARDStitle/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.tsapi.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 登录复用

  • 统一复用现有 PhoneAuthModalsrc/components/phone-auth-modal)。登录态判断沿用 localStorage[LOC_STOR_KEY.AUTH_TOKEN]
  • 已登录请求自动带 Authorization(沿用 services/api 现有请求拦截)。

8. 测试

8.1 后端

  • 发帖 → 该板块帖数 +1非法 board 被拒1000
  • 连续回帖 → floor 依次 1、2、3reply_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行为需一致。