1. 摘要
目标是在现有最简新闻站基础上,落地可运营的后台发布系统:管理员登录、新闻草稿/发布、分类管理(首版仅分类)、前台新闻列表与详情、统一 API 返回结构、完整验收与测试闭环。
实现方式固定为:PostgreSQL、JWT + HttpOnly Cookie、单管理员账号、富文本正文、不做封面上传、API 统一 {code,message,data}。
2. 里程碑与交付顺序
- 基础改造:目录迁移到 src/、公共工具层、环境变量与错误模型。
- 数据库:建表与索引、初始化管理员、初始化默认分类、JSON 数据迁移脚本。
- 鉴权:登录/登出 API、JWT 签发校验、middleware 路由保护。
- 前台 API + 页面:新闻列表、详情、仅展示 published。
- 后台 API + 页面:新闻管理列表、创建、编辑、发布状态切换。
- 联调与测试:接口契约测试、页面关键流程测试、验收清单逐项通过。
- 上线准备:生产环境变量、一次性迁移执行、回滚预案。
3. 目标目录结构(落地版)
news-system/ ├── src/ │ ├── app/ │ │ ├── api/ │ │ │ ├── auth/ │ │ │ │ ├── login/route.js │ │ │ │ └── logout/route.js │ │ │ ├── news/ │ │ │ │ ├── route.js │ │ │ │ └── [id]/route.js │ │ │ ├── admin/ │ │ │ │ └── news/ │ │ │ │ ├── route.js │ │ │ │ └── [id]/route.js │ │ ├── admin/ │ │ │ ├── layout.jsx │ │ │ ├── login/page.jsx │ │ │ ├── dashboard/page.jsx │ │ │ └── news/ │ │ │ ├── create/page.jsx │ │ │ └── edit/[id]/page.jsx │ │ ├── news/[id]/page.jsx │ │ ├── layout.jsx │ │ ├── page.jsx │ │ ├── globals.css │ │ └── middleware.js │ ├── lib/ │ │ ├── db.js │ │ ├── auth.js │ │ ├── response.js │ │ ├── validators.js │ │ └── newsService.js │ ├── components/ │ │ ├── Header.jsx │ │ ├── Footer.jsx │ │ ├── NewsCard.jsx │ │ └── RichTextEditor.jsx │ └── scripts/ │ ├── migrate-json-to-pg.js │ └── seed-admin.js ├── .env.local ├── package.json └── tailwind.config.js
4. 数据库表结构(PostgreSQL)
create table admins ( id bigserial primary key, username varchar(50) not null unique, password_hash varchar(255) not null, token_version int not null default 1, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create table news_categories ( id bigserial primary key, name varchar(50) not null unique, slug varchar(60) not null unique, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create table news ( id bigserial primary key, title varchar(200) not null, summary varchar(500) not null, content_html text not null, content_text text not null, status varchar(20) not null check (status in ('draft','published')), category_id bigint not null references news_categories(id), published_at timestamptz null, created_by bigint not null references admins(id), updated_by bigint not null references admins(id), created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create index idx_news_status_published_at on news(status, published_at desc); create index idx_news_category_status on news(category_id, status, published_at desc); create index idx_news_created_at on news(created_at desc);
初始化规则:
- news_categories 默认插入 未分类(slug=uncategorized)。
- seed-admin.js 创建单管理员(来自 .env.local:ADMIN_USERNAME、ADMIN_PASSWORD)。
- 旧 data/news.json 通过 migrate-json-to-pg.js 一次性导入为 published,published_at=created_at。
5. 环境变量与安全基线
必须项:
- DATABASE_URL
- JWT_SECRET
- ADMIN_USERNAME
- ADMIN_PASSWORD
- COOKIE_NAME=ns_admin_token
- JWT_EXPIRES_IN=7d
安全约束:
- 登录成功后仅写 HttpOnly + Secure(生产) + SameSite=Lax cookie。
- middleware 保护 /admin/:path* 与 /api/admin/:path*。
- 密码哈希 bcrypt。
- 富文本正文服务端净化(白名单标签),同时保留 content_text 供摘要与检索。
6. API 契约(统一响应)
统一响应:
- 成功:{ code: 0, message: “ok”, data: … }
- 失败:{ code: <业务码>, message: “<错误信息>”, data: null }
业务码约定:
- 0 成功
- 1001 参数错误
- 1002 未认证
- 1003 无权限
- 1004 资源不存在
- 1005 状态非法
- 1500 服务端错误
接口清单:
- POST /api/auth/login
- 入参:{ username, password }
- 成功:写入 cookie,返回管理员基础信息
- 失败:1002
- POST /api/auth/logout
- 行为:清空 cookie
- 成功:code=0
- GET /api/news?page=1&pageSize=10&categoryId=&q=
- 说明:仅返回 published
- 返回:分页列表 + total
- GET /api/news/:id
- 说明:仅可读取 published
- 不存在或未发布:1004
- GET /api/admin/news?page=1&pageSize=10&status=&categoryId=&q=
- 说明:后台列表,支持 draft/published 过滤
- POST /api/admin/news
- 入参:{ title, summary, contentHtml, categoryId, status }
- 规则:status=published 时写 published_at=now()(若首次发布)
- GET /api/admin/news/:id
- 说明:后台详情(可读草稿)
- PUT /api/admin/news/:id
- 入参同创建
- 规则:draft -> published 时补 published_at;published -> draft 时保留历史 published_at 但前台不展示
- DELETE /api/admin/news/:id
- 首版策略:硬删除
- 返回:删除结果
7. 页面交互规格
- / 首页
- 展示已发布新闻卡片流,按 published_at desc。
- 支持分类筛选与关键词搜索(标题/摘要)。
- 分页导航(默认 10 条)。
- /news/[id]
- 仅展示已发布新闻详情。
- 显示标题、分类、发布时间、富文本正文。
- /admin/login
- 用户名密码登录。
- 成功跳转 /admin/dashboard。
- 已登录访问此页直接跳转 dashboard。
- /admin/dashboard
- 显示统计卡片:总新闻、已发布、草稿。
- 最近新闻快捷入口。
- /admin/news/create
- 表单字段:标题、摘要、分类、富文本正文、状态(草稿/发布)。
- 保存后跳转编辑页并提示成功。
- /admin/news/edit/[id]
- 加载详情并可编辑全部字段。
- 支持“保存草稿”“发布更新”“删除”。
- admin/layout
- 统一侧栏、顶部登出、登录态校验。
- 所有后台页面复用该布局。
8. 服务层与模块职责
- lib/db.js:Postgres 连接池与事务封装。
- lib/auth.js:JWT 签发/验签、cookie 读写、管理员校验。
- lib/validators.js:zod 请求校验 schema。
- lib/newsService.js:新闻增删改查与状态流转规则。
- lib/response.js:统一响应与错误抛出。
约束:
- Route Handler 不直接写 SQL。
- 所有写操作走 service 层并统一事务边界。
9. 测试用例与场景
接口测试:
- 登录成功/失败、登出清 cookie。
- 未登录访问 /api/admin/news 返回 1002。
- 创建草稿成功,列表可见,前台不可见。
- 草稿发布后前台可见,排序正确。
- 已发布改回草稿后前台不可见。
- 分类筛选与关键词搜索结果正确。
- 非法参数触发 1001。
- 删除新闻后后台/前台均不可访问。
页面流程测试:
- 管理员登录 -> 进入 dashboard。
- 创建新闻(草稿)-> 编辑 -> 发布 -> 前台可浏览。
- 未登录访问 /admin/news/create 被重定向到 /admin/login。
- 详情页访问不存在 id 显示 404。
回归测试:
- 旧 JSON 导入后首页可见历史新闻。
- 样式与基础导航不回退。
10. 验收清单(Definition of Done)
- 数据库三张核心表已建、索引生效、默认分类与管理员已初始化。
- 旧 data/news.json 成功迁移,条数一致。
- 所有 API 均按 {code,message,data} 返回并覆盖错误码。
- 后台受 middleware + cookie 双层保护。
- 新闻支持草稿/发布,前台仅展示已发布。
- 分类功能在前后台均可使用(至少筛选)。
- 关键流程测试通过,无阻塞级缺陷。
- 生产环境变量齐全,可一键启动。
11. 假设与默认值(已锁定)
- 数据库:PostgreSQL。
- 鉴权:JWT + HttpOnly Cookie。
- 账号:单管理员。
- 正文:富文本编辑。
- 封面图:首版不做上传。
- 分类:首版做“仅分类”,不做标签。
- API 风格:统一 {code,message,data}。
- 删除策略:首版硬删除(后续可升级软删除)。
落实这套实施计划,不用额外创建 agent 文件、.md、.skill 文件也可以直接开工。
这些文件是给“流程规范/复用能力”用的,不是业务功能必需品。
只有两种情况才建议加:
- 你想把这套实施流程沉淀成团队规范(可加 IMPLEMENTATION_PLAN.md)。
- 你希望以后复用成自动化能力(再做 SKILL.md)。
就当前目标(把新闻系统做出来),直接按计划改代码、建表、写接口即可。