# PN 中台客诉 Skill

`@pn-data/customer-service` 使用文档。  
当前内容已按服务端 `CustomerSkill::skill()` 对齐，只保留已开放能力。

- 当前版本：`1.0.2`

- Skill 主文档：`customer-service/SKILL.md`
- CLI 脚本：`customer-service/customer_service.py`

## 龙虾必看

- 术语统一：文档里的“未处理工单/待处理工单”统一指运营处理中工单（`status=3`）。
- 术语统一：“工单草稿”指未交给运营处理的工单，取待草稿客诉使用 `getWaitingDraftTroubles`。
- 数量口径：用户泛问“工单数量/几条工单”时，默认看运营处理中数量，使用 `getOperationProcessingWorkOrderCount`（脚本 `--operation-processing-count`）。
- 禁止误用：不要用 `getWaitingDraftTroubles` 返回数组长度当“工单总数”。
- 入口统一：协助工单语境下，“未处理工单/待处理工单”走运营处理中统计；“工单草稿”走 `getWaitingDraftTroubles`。
- 参数规则：`work order` 相关 skill（`getWaitingDraftTroubles`、`getWaitingDraftTroubleById`、`updateWorkOrderDraft`、`updateDraft`、`markWorkOrderError`、`markWorkOrderOperationHandled`、`getOperationProcessingWorkOrderCount`）请求体不传 `pid`。
- 调用约束：只有在提供客诉 `subjectId` 时，才调用 `getTroubleDetailBySubjectId`。
- 列表特殊筛选：`aiReply`（AI 回复）和 `humanRequest`（玩家申请转人工）仅在启用筛选时传字符串 `"yes"`；不筛选时省略对应字段。
- 列表标签筛选：通过 `--data-json` 的 `tags` 传标签路径数组，可直接在服务端筛选；按一级、二级、三级顺序填写实际保存的 Tag 值。
- 用户时区：客诉详情 `data.list[].userTimezone` 为 IANA 时区（如 `Asia/Shanghai`）；空字符串表示无法从 IP 识别。回复或工单草稿涉及时间时，按该时区理解用户当地时间。
- AI 草稿回复：先根据客诉详情确认项目已开启 `aiReply`，再使用该客诉 `data.aiDrafts[]` 中实际存在的 `id` 调用 `replyByDraftId`；不要传入或改写回复正文。
- Token 处理：当没有 `PN_DATA_ANALYSIS_TOKEN` 时，先看本地变量里有没有；有则直接执行 `source ~/.zshrc` 或 `source ~/.bashrc`。若没有则提示获取密钥：登录 `pn_mid_v2` 中台，点击右上角头像/菜单 -> 生成数据分析密钥（或设置/密钥相关入口），复制生成的 token（仅展示一次，请妥善保存）。
- 版本检查：使用 skill 前先确认版本是否最新；不是最新则自动更新（`npm install @pn-data/customer-service@latest`），并同步更新 `.cursor/skills/customer-service/` 下内容。

## 客诉状态说明

与 `client/src/customer/Trouble.vue` 的查询条件对齐：

- `-1`：查询全部状态
- `-2`：加急（Urgent）
- `0`：未处理（Untreated）
- `1`：已回复（Reply）
- `2`：待处理（Wait）
- `3`：客服已处理（Completed）
- `10`：用户已关闭（Close）
- `11`：已删除（Delete）

用于 `getTroubleListByProject` / `getTroubleListForAllProjects` 时，在 `data.status` 中传单个值或数组（如 `[0, 1]`）；查询全部时显式传 `-1`，省略则服务端默认查未处理（`0`）。返回记录的 `status=-1` 或 `4` 在页面按未处理展示，与查询条件 `status=-1` 的“全部”含义不同。

## 当前可用能力（与 `CustomerSkill.php` 对齐）

`/service/skill_for_customer_service`：

- `getWaitingDraftTroubles`
- `getWaitingDraftTroubleById`
- `updateWorkOrderDraft`
- `updateDraft`
- `markWorkOrderError`
- `markWorkOrderOperationHandled`
- `updateReplyDraft`
- `replyByDraftId`
- `getTroubleDetailBySubjectId`
- `getTroubleListByProject`
- `getTroubleListForAllProjects`
- `updateCustomerServiceTags`
- `getOperationProcessingWorkOrderCount`

`/service/pn_data_analysis`：

- `listProject`
- `statisticCustomerServiceReplyForSkill`

`getWaitingDraftTroubles`（队首 1 条，无可用客诉时返回 `[]`）和 `getWaitingDraftTroubleById` 的返回均为数组，每项仅包含 `subjectId` 与 `mainTrouble`。`mainTrouble` 为当前客诉详情，包含 `subject`、消息 `list`、AI 草稿和当前工单等数据；只查询当前客诉。

## 配置

脚本和 Cursor Agent 调用 `customer_service.py` 时，必须能读到环境变量 `PN_DATA_ANALYSIS_TOKEN`，否则会报错并提示未配置。

### 获取密钥

1. 登录 `pn_mid_v2` 中台
2. 点击右上角头像/菜单 -> 生成数据分析密钥（或设置/密钥相关入口）
3. 复制生成的 token（仅展示一次，请妥善保存）

### 配置方式

方式一：写入 shell 配置文件（推荐，对本用户所有终端生效）

- macOS / Linux（zsh）：在 `~/.zshrc` 末尾添加  
  `export PN_DATA_ANALYSIS_TOKEN="粘贴你的 token"`
- bash：在 `~/.bashrc` 或 `~/.bash_profile` 末尾添加同一行

保存后执行一次：

```bash
source ~/.zshrc   # 或 source ~/.bashrc
```

方式二：仅当前终端会话

```bash
export PN_DATA_ANALYSIS_TOKEN="粘贴你的 token"
```

仅对当前 shell 有效，新开终端或 Cursor 新会话不会继承。

### 校验是否生效

```bash
echo $PN_DATA_ANALYSIS_TOKEN
# 或
printenv PN_DATA_ANALYSIS_TOKEN
```

有输出一长串 token 即表示已配置；为空则需检查配置文件是否写对、是否已 `source`。

### 其他可选环境变量

- `PN_DATA_ANALYSIS_BASE`（默认 `https://pnv2.17995api.net`）

脚本会自动尝试读取以下文件（已在 shell 导出的变量优先，不会覆盖）：

- `customer-service/.env.customer-service`
- `customer-service/.env`
- 项目根目录 `.env.customer-service`
- 项目根目录 `.env`

## 常用命令

```bash
# 项目列表
python3 customer-service/customer_service.py --list-projects

# 客服回复统计（时间范围）
python3 customer-service/customer_service.py --statistic-reply 1712505600 1712591999

# 待草稿客诉队列（队首 1 条）
python3 customer-service/customer_service.py --waiting-draft-troubles

# 按 subjectId 查询待草稿详情
python3 customer-service/customer_service.py --waiting-draft-trouble-by-id 123456

# 更新/新建工单草稿
python3 customer-service/customer_service.py --update-work-order-draft 123456 --title "工单标题" --content "工单内容"

# 同时更新工单草稿和回复草稿
python3 customer-service/customer_service.py --update-draft 123456 --work-order-title "工单标题" --work-order-content "工单内容" --reply-content "请先提供角色 ID，我们尽快核查"

# 按 subjectId 标记工单为错误
python3 customer-service/customer_service.py --mark-work-order-error 123456

# 按 subjectId 标记工单为运营已处理（title/content 必填）
python3 customer-service/customer_service.py --mark-work-order-operation-handled 123456 --title "处理结果标题" --content "处理结果正文"

# 更新 AI 回复草稿
python3 customer-service/customer_service.py --update-reply-draft 123456 --content "请先提供角色 ID，我们尽快核查"

# 采用 AI 草稿回复客诉
python3 customer-service/customer_service.py --reply-by-draft-id 98765 --subject-id 123456

# 客诉详情（data.list[].userTimezone 为用户 IANA 时区）
python3 customer-service/customer_service.py --trouble-detail 123456

# 项目内客诉列表
python3 customer-service/customer_service.py --trouble-list-by-project 2000 --data-json '{"status": -1, "limit": 10, "page": 1}'

# 仅看 AI 回复的客诉
python3 customer-service/customer_service.py --trouble-list-by-project 2000 --data-json '{"status": -1, "aiReply": "yes", "limit": 10, "page": 1}'

# 仅看玩家申请转人工的客诉
python3 customer-service/customer_service.py --trouble-list-by-project 2000 --data-json '{"status": -1, "humanRequest": "yes", "limit": 10, "page": 1}'

# 按标签路径查询（可以只传前一级或前两级）
python3 customer-service/customer_service.py --trouble-list-by-project 2000 --data-json '{"status": -1, "tags": ["充值问题", "礼包购买", "购买未到账"], "limit": 10, "page": 1}'

# 检测语言、运营工单状态和排序可以组合使用
python3 customer-service/customer_service.py --trouble-list-by-project 2000 --data-json '{"status": -1, "language_detect": ["tw", "jp"], "workstatus": ["3", "4"], "sort": "updated_at", "order": "DESC", "limit": 10, "page": 1}'

# 全项目客诉列表（eid=1 或 5）
python3 customer-service/customer_service.py --trouble-list-all-projects 1 --data-json '{"status": -1, "limit": 10, "page": 1}'

# 运营处理中工单数
python3 customer-service/customer_service.py --operation-processing-count

# 更新客诉标签（最多 3 个；传 [] 清空）
python3 customer-service/customer_service.py --update-customer-service-tags 123456 --tags-json '["vip","bug"]'

# 解析 token
python3 customer-service/customer_service.py --show-pn-token
```

追加 `--json` 可输出原始 JSON。

`--update-draft`、`--update-reply-draft` 还可附带 `--ai-source ai_v2`，对应请求体的可选 `aiSource`，用于标识草稿的 AI 来源。

### 客诉列表查询参数说明

`--trouble-list-by-project` 与 `--trouble-list-all-projects` 的 `--data-json` 原样传入请求体的 `data`，两者使用相同的有效查询参数：

| 参数 | 类型 | 含义 |
| --- | --- | --- |
| `status` | 整数或整数数组 | 客诉状态：`-1` 全部、`-2` 加急、`0` 未处理、`1` 已回复、`2` 待处理、`3` 已完成、`10` 已关闭、`11` 已删除；多选如 `[0, 1]`。省略默认 `0`。 |
| `workstatus` | 字符串或字符串数组 | 工单状态；`"3"` 运营处理中、`"4"` 运营已处理，多选如 `["3", "4"]`。与客诉 `status` 分开传；查询所有客诉状态下的工单时搭配 `status: -1`。 |
| `serverid` | 整数或字符串 | 服务器 ID；省略或 `""` 不筛选。 |
| `language` | 字符串或字符串数组 | 原始语言代码，如 `"tw"` 或 `["tw", "jp"]`；省略或 `""` 不筛选。 |
| `language_detect` | 字符串或字符串数组 | 检测语言代码，与页面语言筛选一致，如 `["tw", "jp"]`；省略或 `""` 不筛选。 |
| `typeid` | 整数或字符串 | 客诉分类 ID；省略或 `""` 不筛选。 |
| `tags` | 字符串数组 | 标签路径，按一级、二级、三级顺序传实际 Tag 值，最多 3 个。可只传前一级或前两级筛选对应路径；省略或 `[]` 不筛选。 |
| `gm_score` | 整数 | 客服评分上限，筛选已评分且分值不高于此值的客诉，页面使用 `1`、`2`、`3`、`4`；`0`、省略或 `""` 不筛选。 |
| `paystar` | 整数 | VIP 等级筛选，`4` 为 2 星、`6` 为 3 星、`8` 为 4 星；省略或 `""` 不筛选。 |
| `startTS` | 整数 | 创建时间范围的开始时间，Unix 秒级时间戳；必须与 `endTS` 成对传入，单独传入不生效。 |
| `endTS` | 整数 | 创建时间范围的结束时间，Unix 秒级时间戳；必须与 `startTS` 成对传入，不限制时间时同时省略。 |
| `passportid` | 字符串 | 用户 UID（通行证 ID）筛选。 |
| `subjectid` | 整数或数字字符串 | 客诉 ID 筛选；国内客诉使用中台展示的 ID，服务端自动移除国内 ID 偏移。 |
| `keywords` | 字符串 | 关键词搜索（标题/内容检索）。 |
| `onlyme` | 字符串 | 是否仅看当前操作者处理的客诉，`"yes"` 启用；`"no"` 或省略不筛选。 |
| `aiReply` | 字符串 | 仅看 AI 回复的客诉，启用传 `"yes"`，不筛选时省略。 |
| `humanRequest` | 字符串 | 仅看玩家申请转人工的客诉，启用传 `"yes"`，不筛选时省略。 |
| `gmName` | 字符串 | GM 昵称筛选，如 `"PicoClaw"`；省略或 `""` 不筛选。 |
| `sort` | 字符串 | 排序字段：`"created_at"` 创建时间或 `"updated_at"` 更新时间；与 `order` 一起传入。 |
| `order` | 字符串 | 排序方向：`"ASC"` 升序或 `"DESC"` 降序；与 `sort` 一起传入。 |
| `limit` | 正整数 | 每页条数，页面使用 `30`。 |
| `page` | 正整数 | 页码，从 `1` 开始。 |

项目范围由顶层 `pid` / `eid` 决定，`data.appid`、`data.gameid` 会被服务端删除或覆盖，不能改变这两个接口的项目范围。标签过滤使用 `tags`，单传 `tagPath` 不生效。前端内部字段 `idsOnly`、`filterStatus` 会被服务端删除，不作为 Skill 查询参数。

返回 `data.list` 为当前页客诉，`data.count` 为筛选后的总条数；获取全部结果时按 `limit`、`page` 分页，不能将当前页条数当作总数。

接口层参数补充：

- `getTroubleListByProject`：请求体顶层还需 `pid`（项目 ID）。
- `getTroubleListForAllProjects`：请求体顶层还需 `eid`（当前支持 `1` 或 `5`）。

## 安装

```bash
npm install @pn-data/customer-service
```

将 `node_modules/@pn-data/customer-service/customer-service/` 和 `README.md` 复制或链接到 `.cursor/skills/customer-service/`。

## License

MIT
