Files
2026-08-01 23:09:49 +08:00

12 KiB
Raw Permalink Blame History

运行手册

本文档是项目运行的唯一事实来源。后续操作优先按本文档执行,若实际命令与文档不符,先验证真实行为,再更新本文档。

技术栈

层级 技术 版本
前端框架 React + TypeScript ^19
构建工具 Vite ^6
后端框架 Express.js ^4
数据库 SQLiteWAL 模式) 内置 node:sqlite
包管理器 npm >= 18
图表库 Recharts latest
图标库 Lucide React latest
进程管理 concurrently latest
测试框架 Jest + Supertest latest

本地环境

环境变量

变量 默认值 说明
API_PORT 8787 后端 API 监听端口

端口清单

服务 地址 说明
前端开发服务器 http://localhost:5177 Vite 热重载
后端 API http://localhost:8787 Express REST API
前端代理 /apihttp://localhost:8787 Vite proxy 配置

依赖服务

  • 无外部依赖服务(SQLite 为内置文件数据库)

安装

npm install

node_modules 已存在,跳过此步骤。

开发命令

全栈启动(推荐)

npm run dev

同时启动:

  • API 服务(node --watch server/index.js)监听 127.0.0.1:8787
  • 前端 Vite 开发服务器监听 localhost:5173API 请求代理至 8787

单独启动

# 仅前端(需 API 已在运行)
npm run web

# 仅后端(需前端已在运行)
npm run server

后端启动(独立,不热重载)

npm run server:start

构建

npm run build
  • 前端编译输出至 dist/
  • TypeScript 类型检查:tsc -b
  • 两者均通过才算构建成功

测试

npm test

运行 server/tests/integration.test.cjs,覆盖所有 API 端点的集成测试。测试使用独立临时数据库,测试完成后自动清理。

数据库

数据文件

  • 开发数据库:data/reporter-station.dbSQLite
  • WAL 文件:data/reporter-station.db-walWrite-Ahead Log
  • SHM 文件:data/reporter-station.db-shm

迁移管理

使用 migrations/_runner.js 管理所有 schema 变更:

# 查看迁移状态
node migrations/_runner.js --status

# 应用所有待执行迁移
node migrations/_runner.js migrate

# 执行指定迁移(幂等,CREATE TABLE IF NOT EXISTS
node migrations/_runner.js migrate --target 005

# 回滚最后一条迁移
node migrations/_runner.js rollback

# 重置数据库(删除并重建所有表,重新 seed)
node migrations/_runner.js reset

# 查看 seed 数据
node migrations/_runner.js --seed

迁移按文件名数字前缀顺序执行(001~009)。新增迁移时,使用下一个可用数字前缀。

数据库结构

work_recordsV0.1

字段 类型 说明
id TEXT PK 主键,格式 WK-YYYYMMDD-XXXX
title TEXT 工作标题
type TEXT 工作类型(文字稿件/视频供稿/...
reporter TEXT 记者姓名
station TEXT 所属记者站
occurred_date TEXT 发生/刊发日期
platform TEXT 媒体/平台
status TEXT 状态(draft/station_review/headquarters_review/returned/archived
score INTEGER 考核得分(可为 null
description TEXT 工作说明
review_note TEXT 审核意见
updated_at TEXT 更新时间

audit_logsV0.1

字段 类型 说明
id INTEGER PK 自增主键
record_id TEXT FK 关联 work_records.id
actor_role TEXT 操作者角色
actor_name TEXT 操作者姓名
action TEXT 操作类型(submit/pass/return
from_status TEXT 原状态
to_status TEXT 新状态
score INTEGER 打分
note TEXT 意见
created_at TEXT 操作时间

peopleV0.1.1

字段 类型 说明
id INTEGER PK 自增主键
code TEXT UNIQUE 人员编码
name TEXT 姓名
role TEXT 角色(headquarters/station/reporter
title TEXT 职务
phone TEXT 联系电话
station TEXT 所属记者站
status TEXT 状态(active/inactive
joined_at TEXT 入职日期

stationsV0.1.1

字段 类型 说明
id INTEGER PK 自增主键
code TEXT UNIQUE 记者站编码
name TEXT 记者站名称
region TEXT 所属区域
established_at TEXT 成立日期

noticesV0.1.1

字段 类型 说明
id INTEGER PK 自增主键
title TEXT 通知标题
content TEXT 通知内容
type TEXT 类型(notice/circular/guide
priority TEXT 优先级(normal/important/urgent
created_by TEXT 创建人
created_at TEXT 创建时间
expire_at TEXT 过期时间

rulesV0.2

字段 类型 说明
id INTEGER PK 自增主键
code TEXT UNIQUE 规则编码
name TEXT 规则名称
description TEXT 规则说明
period_type TEXT 周期类型(quarterly/custom
period_start TEXT 周期开始日期
period_end TEXT 周期结束日期
status TEXT 状态(draft/active/archived
version INTEGER 版本号
parent_id INTEGER FK 父版本 ID
created_by TEXT 创建人
created_at TEXT 创建时间
updated_at TEXT 更新时间

rule_itemsV0.2

字段 类型 说明
id INTEGER PK 自增主键
rule_id INTEGER FK 关联 rules.id
category TEXT 指标类别(quantity/quality/efficiency/compliance
name TEXT 指标名称
metric_key TEXT 指标键名
weight REAL 权重
min_score REAL 最低分
max_score REAL 最高分
formula_type TEXT 计算公式类型
formula_params TEXT 公式参数(JSON
display_order INTEGER 排序
enabled INTEGER 是否启用

scoresV0.2

字段 类型 说明
id INTEGER PK 自增主键
rule_id INTEGER FK 关联 rules.id
station TEXT 站点名称
period TEXT 考核周期
period_type TEXT 周期类型
total_score REAL 综合得分
quality_score REAL 质量分
quantity_score REAL 数量分
efficiency_score REAL 时效分
compliance_score REAL 合规分
items TEXT 明细(JSON 数组)
computed_at TEXT 计算时间

API 接口文档

认证

所有 API 请求需携带 HTTP header

x-user-role: headquarters | station | reporter

核心接口

方法 路径 角色 说明
GET /api/health 公开 健康检查
GET /api/records 全部 查询工作记录(按角色隔离)
POST /api/records reporter/station 新建记录
PATCH /api/records/:id/review station/hq 审核(通过/退回)
GET /api/records/:id/audit 全部 流转记录(按角色隔离)

人员与站点(V0.1.1

方法 路径 角色 说明
GET /api/people 全部 查询人员列表
POST /api/people station 新建人员
PATCH /api/people/:id station/hq 编辑人员
GET /api/stations 全部 查询记者站列表
POST /api/stations hq 新建记者站

通知公告(V0.1.1

方法 路径 角色 说明
GET /api/notices 全部 查询通知列表
POST /api/notices hq 发布通知
POST /api/notices/:id/read 全部 标记已读
GET /api/notices/unread-count 全部 未读数量

统计(V0.1.1

方法 路径 角色 说明
GET /api/stats/overview 全部 总览统计(站点角色仅限本站)
GET /api/stats/records hq 记录统计(支持按周期/站点/类型筛选)
GET /api/stats/scores hq 评分统计(支持按周期/站点筛选)

考核规则(V0.2

方法 路径 角色 说明
GET /api/rules hq 查询规则列表(支持 status 筛选)
GET /api/rules/:id hq 查询规则详情(含指标项)
POST /api/rules hq 创建规则(含指标项)
PATCH /api/rules/:id hq 编辑规则(草稿状态)
POST /api/rules/:id/activate hq 激活规则

评分(V0.2

方法 路径 角色 说明
GET /api/scores hq 查询评分列表(支持 period/station 筛选)
GET /api/scores/:id hq 查询评分详情(含明细)
POST /api/scores/compute hq 触发评分计算

请求示例

# 以总部管理员身份查询全部记录
curl -H "x-user-role: headquarters" http://localhost:8787/api/records

# 以记者身份提交新记录
curl -X POST http://localhost:8787/api/records \
  -H "Content-Type: application/json" \
  -H "x-user-role: reporter" \
  -d '{"title":"测试稿件","type":"文字稿件","date":"2026-08-01","platform":"测试平台"}'

# 以分站负责人身份审核通过
curl -X PATCH http://localhost:8787/api/records/WK-202608-XXXX/review \
  -H "Content-Type: application/json" \
  -H "x-user-role: station" \
  -d '{"decision":"pass","score":8,"note":"审核通过"}'

# 总部创建考核规则
curl -X POST http://localhost:8787/api/rules \
  -H "Content-Type: application/json" \
  -H "x-user-role: headquarters" \
  -d '{"name":"2026年度Q3考核","period_type":"quarterly","items":[{"category":"quantity","name":"发稿数量","metric_key":"count_total","weight":0.4,"formula_type":"count","formula_params":{}},{"category":"quality","name":"审核得分","metric_key":"avg_score","weight":0.6,"formula_type":"avg_score","formula_params":{}}]}'

# 总部触发评分计算
curl -X POST http://localhost:8787/api/scores/compute \
  -H "Content-Type: application/json" \
  -H "x-user-role: headquarters" \
  -d '{"rule_id":1,"period":"2026-Q3","period_type":"quarterly"}'

常见问题

Q1:端口 8787 被占用

# 查找占用进程
lsof -i :8787
# 杀死进程(替换 PID
kill -9 <PID>
# 或换端口
API_PORT=8788 npm run dev

Q2:端口 5173 被占用

lsof -i :5173
kill -9 <PID>

Q3SQLite WAL 文件导致"database is locked"

  • 通常因多个进程同时写入
  • 解决:确保只有一个服务实例运行
  • WAL 文件异常时,删除 .db-wal.db-shm 后重启服务

Q4:前端热重载不生效

  • 确认 Vite dev server 正在运行(看终端输出 "ready in xxx ms"
  • 清除浏览器缓存或使用无痕模式

Q5:迁移执行失败

# 查看迁移状态
node migrations/_runner.js --status
# 检查是否有 pending 迁移
# 查看具体错误日志

Q6TypeScript 类型错误

npx tsc -b --force  # 强制重建类型缓存

Q7npm run build 失败

  1. 先确认 TypeScript 无误:npx tsc -b
  2. 查看 Vite 构建输出中的具体错误
  3. 检查是否有新增依赖未安装

Q8seed 数据消失

  • 原因:work_records 表已有数据时,seed 逻辑不执行
  • 解决:手动删除数据库文件后重启服务,或使用 node migrations/_runner.js reset

后续开发参考

  • 需求文档:pmdocs/0-req-RSMS.md
  • 产品文档:pmdocs/1-prd-RSMS.md
  • 任务文档:pmdocs/2-task-RSMS.md
  • 架构决策:pmdocs/adr/
  • 需求变更:pmdocs/changes/
  • 原始需求参考:req.md