# 评价系统创作教程

> 结论：一个零输入评价系统 = 前端一张只打勾的网页 + 后端一个小服务 + 一个 JSON 文件 + 一个通知通道。
> 本教程照「同人管家汽车美容 / AI 陪跑满意度调查」这套已上线系统写成。照做可复刻。

---

## 1. 系统长什么样

```mermaid
flowchart LR
  A[客户扫码/点专属链接] --> B[评价页：只打勾，不填字]
  B -->|提交，链接带令牌 cid| C[后端 /api/submit]
  C --> D{差评判定}
  D -->|评分<=2 或 勾了问题项| E[差评：挽回话术 + 回访链接]
  D -->|否则| F[好评：感谢话术]
  E --> G[存 JSON]
  F --> G
  G --> H[推企微群：管理版]
  H --> I[店长回访 → 后台登记 → 闭环]
```

一句话：**客户只打勾 → 后台令牌认人 → 自动分好评差评 → 推群 → 回访登记留痕。**

---

## 2. 三个部件

| 部件 | 做什么 | 技术 |
|---|---|---|
| 前端 | 评价页（客户用）+ 后台页（店长用） | 纯 HTML + 原生 JS，无框架 |
| 后端 | 收提交、存数据、分流、推群、回访登记 | Node.js 单文件，零依赖 |
| 数据 | 存评价和客户名单 | JSON 文件（几百条量级够用） |

---

## 3. 前端（评价页）

**三条底线**

1. 客户不填字。页面只有可点的选项。
2. 店长不增负担。系统自动推，不靠人转发。
3. 差评马上有人管。差评 1 分钟内推群。

**页面从上到下**

| 序号 | 区块 | 交互 |
|---|---|---|
| 1 | 开场问候（暖色框，放最顶） | 无 |
| 2 | 选服务类型 | 单选 |
| 3 | 整体满意度 | 点 5 星 |
| 4 | 做得好的地方 | 多选 |
| 5 | 需要改进的地方 | 多选，可不选 |
| 6 | 愿不愿意推荐 | 单选 |
| 7 | 提交按钮 | 点提交 |

**令牌认人（核心）**

- 后台给每个客户生成 10 位短码 `cid`，拼进链接：`/?c=<cid>`。
- 客户提交时，`cid` 随请求回传。
- 后台拿 `cid` 查客户名单，知道是谁。客户什么都没填。

**提交代码（关键行）**

```js
fetch('/api/submit', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ cid, service, score, good, bad, recommend })
})
```

---

## 4. 后端（API 表）

| 路由 | 方法 | 做什么 | 谁调用 |
|---|---|---|---|
| `/` | GET | 返回评价页 HTML | 客户 |
| `/admin` | GET | 返回后台页 HTML（要口令） | 店长 |
| `/api/submit` | POST | 存评价、判差评、生成话术、推群 | 评价页 |
| `/api/admin/list` | GET | 返回全部评价 + 客户名单 + 统计 | 后台页 |
| `/api/admin/client` | POST | 建客户，返回新 `cid` | 后台页 |
| `/api/admin/followup` | POST | 回访登记 | 后台页 |
| `/api/admin/unfollowup` | POST | 撤销回访（退回待处理） | 后台页 |

**差评规则（写死，三处一致：推送 / 列表 / 统计）**

```
评分 ≤ 2 分  或  勾选任一「需要改进」项  →  差评
```

---

## 5. 前后端怎么连（重点）

```mermaid
sequenceDiagram
  participant C as 客户页
  participant A as 后端
  participant D as JSON 文件
  participant W as 企微群
  C->>A: POST /api/submit (cid+分数+选项)
  A->>D: 查 cid → 客户名/电话
  A->>A: 判差评 + 生成话术
  A->>D: 追加这条评价
  A->>W: 差评推管理群（带话术+回访链接）
  A-->>C: 返回 {ok:true}
```

| 前端动作 | 请求 | 后端处理 | 返回给前端 |
|---|---|---|---|
| 打开评价页 | `GET /` | 读 index.html 返回 | 页面 |
| 点提交 | `POST /api/submit` | 存数据、判差评、推群 | `{ok:true}` |
| 后台打开 | `GET /admin?k=口令` | 校验口令，返回后台页 | 页面 |
| 后台刷新 | `GET /api/admin/list?k=` | 读 JSON，算统计 | 列表 JSON |
| 建客户 | `POST /api/admin/client` | 生成 cid，写名单 | `{cid}` |
| 标记处理 | `POST /api/admin/followup` | 写回访结果 | `{ok:true}` |

---

## 6. 部署（上公网）

| 步 | 动作 | 命令/文件 |
|---|---|---|
| 1 | 后端常驻 | systemd 单元，`ExecStart=node server.js` |
| 2 | 子域名反代 | Caddy 加 `peiban.屿.cn { reverse_proxy 127.0.0.1:5123 }` |
| 3 | HTTPS 证书 | Caddy 自动签发，不用手动 |
| 4 | DNS | 加 A 记录 `peiban → 服务器 IP` |

---

## 7. 五个坑（线上真实踩过）

| 现象 | 原因 | 修法 |
|---|---|---|
| 整页白屏，显示一段 `{"type":"Buffer"...}` | 后端把静态文件 Buffer 当对象 JSON 序列化 | 出口函数先判 `Buffer.isBuffer()`，原样写出 |
| 好评也发差评话术 | `if (rec.bad)`，`bad` 是数组，空数组在 JS 里是真值 | 判分支用布尔字段 `rec.is_bad` |
| 点提交永远卡「提交中」 | 删了输入框，但 JS 还在读它的值，报错 | 删任何元素，全文搜它的 id，删掉 JS 引用 |
| 提交成功但群里没消息 | 推送用「门店名」查地址，配置存的是「门店代码」 | 名称↔代码映射集中写一处 |
| 改过的页面客户还是旧的 | 微信内置浏览器缓存顽固 | 链接末尾加当天日期版本号 |

---

## 8. 验收清单

- [ ] 客户页零输入框，提交后有成功反馈
- [ ] 令牌能认出客户（抽查 3 条）
- [ ] 差评规则三处一致
- [ ] 差评 1 分钟内进群，消息带可点的回访链接
- [ ] 回访可登记、可修改、可撤销
- [ ] 定时/推送跑过一轮（看日志，不看配置）
- [ ] 微信内真机扫码走一遍
