---
name: moeguri-payslip
description: 通过萌谷里排班系统（moeguri.com）的开放接口管理员工名单、生成工资条、发企微通知并跟踪员工确认情况。需要给员工发工资条、查谁确认了/谁有异议、更正工资条、新增或修改员工信息时使用。
---

# 萌谷里工资条与员工接口

员工名单以排班系统为准，不要再自己维护员工表格。工资条在系统里生成后，每个员工会收到一个专属链接（企微卡片消息），在网页上查看明细，点「确认无误」或「提出异议」。确认和异议都会自动推送给负责人，不需要员工回复消息。

## 一、基本信息

- 接口地址：`https://moeguri.com/api/v1/open`
- 鉴权：请求头 `Authorization: Bearer <API令牌>`。令牌由超级管理员在后台「🔑 API 令牌」创建，只显示一次
- 令牌保存在私有位置（环境变量 `MOEGURI_API_TOKEN` 或工作区外的私密文件），**不要写进共享文件、日志、聊天回复，也不要打印出来**
- 请求和响应都是 JSON（`Content-Type: application/json`），金额单位是元，最多两位小数
- 响应格式：`{ "code": 200, "message": "...", "data": ... }`，HTTP 状态码与 `code` 一致：
  - `400` 参数有误（看 `message`，批量生成时看 `errors` 里每条的 `index` 和 `message`）
  - `401` 令牌无效
  - `404` 不存在
  - `409` 状态冲突（版本号不对、已冻结等）

先调一次 `GET /ping` 确认令牌可用。

本说明的最新版随时可以从 `https://moeguri.com/skills/moeguri-payslip/SKILL.md` 下载（无需令牌）。接口有变动时以这个链接为准。

## 二、务必用脚本发请求

**不要在命令行里手写一长串 curl。** 推荐做法如下：

1. 把要提交的数据写成 JSON 文件，例如 `payslips_第96周.json`
2. 写一个小脚本，读取 JSON 文件、带上令牌发请求，再把完整响应存成文件（例如 `result_第96周.json`），同时打印摘要
3. 接口报错时只改 JSON 数据，然后重跑脚本，不用重写请求

批量生成是「全部通过才写入」，任何一条有错都会整批不生成，所以改完数据整批重跑就行，不会重复生成。

Python 示例（只用标准库）：

```python
# moeguri_api.py  用法: python moeguri_api.py POST /payslips payslips_第96周.json
import json, os, sys, urllib.request, urllib.error

BASE = "https://moeguri.com/api/v1/open"
TOKEN = os.environ["MOEGURI_API_TOKEN"]  # 不要把令牌写进脚本

def call(method, path, body=None):
    data = json.dumps(body, ensure_ascii=False).encode("utf-8") if body is not None else None
    req = urllib.request.Request(BASE + path, data=data, method=method, headers={
        "Authorization": f"Bearer {TOKEN}",
        "Content-Type": "application/json",
    })
    try:
        with urllib.request.urlopen(req, timeout=60) as r:
            return json.load(r)
    except urllib.error.HTTPError as e:
        return json.load(e)

if __name__ == "__main__":
    method, path = sys.argv[1], sys.argv[2]
    body = json.load(open(sys.argv[3], encoding="utf-8")) if len(sys.argv) > 3 else None
    res = call(method, path, body)
    out = f"result_{method}_{path.strip('/').replace('/', '_')}.json"
    json.dump(res, open(out, "w", encoding="utf-8"), ensure_ascii=False, indent=2)
    print(res.get("code"), res.get("message"))
    for e in res.get("errors", []):
        print(f"  第 {e['index']} 条（employee_id={e.get('employee_id')}）：{e['message']}")
    print("完整响应已保存到", out)
```

## 三、硬性规则

1. **发送前必须让小黑确认**。生成工资条后，先把汇总发给小黑：人数、每人姓名和实发金额、哪些人没有企微。等小黑明确回复「确认发送」后，才能调发送接口
2. **扣款填负数**，比如迟到扣 20 元就写 `"amount": -20`。`total` 必须等于所有 `amount` 之和，不相等会被拒绝
3. **周期**用起始日期、结束日期、天数三个字段（首尾都算），比如周一到周日是 `"start_date": "2026-10-26", "end_date": "2026-11-01", "days": 7`。同一个员工的工资条周期不能重叠，重叠会报错
4. **员工能看到的内容**（`title`、`items` 的名称和说明、`note`）里不要出现内部信息：不提「小黑」，不写表格单元格坐标，不写待定或内部讨论的内容
5. 项目名称写清楚，比如「班次」写成名称，「3 个班」写在说明里。不推荐把数量写进名称（如「班次 3」）
6. 员工已确认或已提出异议的工资条会冻结，不能修改或删除
7. 已经发出去但员工还没回应的工资条可以改、可以删，但员工可能正在看，所以操作前先告诉小黑。改完如果需要提醒员工，用 `resend: true` 重发
8. 没有企微的员工（发送结果里是 `no_wecom`）会拿到链接，你要把「姓名 + 链接」列给小黑，请他手动转发
9. 链接就是员工的查看凭证，只发给员工本人或小黑，不要发到群里

## 四、员工接口

员工字段：`id`、`name`、`position`、`store_id`、`store_name`、`phone`、`wecom_userid`、`remark`、`deleted_at`。门店可以用 `GET /stores` 查询（目前武汉 1、上海 2，以接口返回为准）。

| 方法 | 路径 | 说明 |
| --- | --- | --- |
| GET | `/employees` | 员工列表。参数 `store_id` 按门店筛选，`include_deleted=1` 包含已删除员工 |
| POST | `/employees` | 新增员工。`name` 必填，`store_id` 建议填；填了 `phone` 会自动匹配企微 |
| PATCH | `/employees/{id}` | 修改员工，只改传了的字段。改 `phone` 会重新匹配企微；也可以直接传 `wecom_userid` |
| DELETE | `/employees/{id}` | 删除员工（软删除，可恢复，历史工资条保留） |
| POST | `/employees/{id}/restore` | 恢复已删除的员工 |
| POST | `/employees/wecom-match` | 为有手机号、还没有企微 ID 的员工重新匹配；可传 `{"ids": [1,2]}` 只匹配指定员工 |

新增员工示例：

```json
{ "name": "小樱", "position": "接待", "store_id": 2, "phone": "13800000000" }
```

响应里的 `wecom_lookup` 是企微匹配结果：`userid` 有值表示匹配成功，`error` 表示失败原因。

**对应员工**：按门店和名字在 `/employees` 里找 `id`。找不到、或同名的不止一个，就停下来问小黑，**不要猜**。确认是新员工后再新增。

## 五、工资条接口

| 方法 | 路径 | 说明 |
| --- | --- | --- |
| POST | `/payslips` | 批量生成（最多 200 张），返回每张的链接，**不会自动发送** |
| POST | `/payslips/send` | 批量发企微卡片给员工 |
| GET | `/payslips` | 查询列表 |
| GET | `/payslips/{id}` | 查询单张 |
| PATCH | `/payslips/{id}` | 修改（必须带 `version`） |
| DELETE | `/payslips/{id}` | 删除或撤回（必须带 `version`） |

### 5.1 生成

```json
{
  "payslips": [
    {
      "employee_id": 12,
      "title": "第96周工资条",
      "start_date": "2026-10-26",
      "end_date": "2026-11-01",
      "days": 7,
      "items": [
        { "name": "班次", "amount": 360, "note": "3 个班 × 120" },
        { "name": "拍立得", "amount": 45, "note": "9 张" },
        { "name": "迟到", "amount": -20, "note": "10/28 迟到 15 分钟" }
      ],
      "total": 385,
      "note": "本周辛苦了！"
    }
  ]
}
```

- `title`、`note`、每项的 `note` 都可以不填。不填 `title` 时，页面显示「2026-10-26 至 2026-11-01 工资条」
- 金额为 0 的项目要不要列出来，按小黑的习惯决定
- 成功时 `data` 是生成的工资条列表（含 `id`、`link`、`version`、`has_wecom`），`no_wecom` 是没有企微的员工列表
- 失败时 `errors` 会逐条说明哪一条（`index` 从 0 开始）有什么问题，整批都不会生成

### 5.2 发送

```json
{ "ids": [101, 102, 103] }
```

结果里每张的 `result`：

- `sent`：已发送
- `no_wecom`：员工没有企微，需要小黑手动转发 `link`
- `failed`：发送失败，看 `reason`，可以稍后重试
- `skipped`：已跳过。已发送过的默认不会重发，已确认或有异议的也不发

需要重发（比如催确认、改过内容后提醒员工）时传 `{"ids": [...], "resend": true}`。

### 5.3 查询进度、催确认

`GET /payslips` 支持以下参数：

- `status`：`unsent` 未发送，`sent` 已发送待回应，`confirmed` 已确认，`disputed` 有异议；多个状态用逗号分隔
- `start_date`、`end_date`：查与该区间有重叠的工资条
- `store_id`、`employee_id`、`keyword`（姓名或标题）
- `include_deleted=1`：包含已删除的工资条

常用查询：

- 本周谁还没确认：`GET /payslips?start_date=2026-10-26&end_date=2026-11-01&status=sent`
- 谁提出了异议：`GET /payslips?status=disputed`，异议内容在 `dispute_text`，员工附的图片在 `dispute_files`（最多 9 张，每项有 `sha256`、`size`、`mime`、`width`、`height`）
- 取异议图片：`GET /payslips/{id}/files/{sha256}/{档位}`，档位 `thumb` 是缩略图，`preview` 是预览图（长边 1600），`original` 是员工上传的原图（作为凭证，原样保存）。一般看 `preview` 就够了，原图可能有好几 MB
- 返回字段 `first_viewed_at` 有值，说明员工打开看过

催确认：经小黑同意后，对 `status=sent` 的工资条调 `/payslips/send` 并传 `resend: true`。

### 5.4 修改与删除

先 `GET /payslips/{id}` 拿到当前的 `version`，再提交：

```json
{ "version": 1, "items": [ { "name": "班次", "amount": 480, "note": "4 个班" } ], "total": 480 }
```

- `items` 和 `total` 要一起传，`start_date`、`end_date`、`days` 也要三个一起传；`title`、`note` 可以单独改
- 返回 `409` 并提示「版本不一致」时，重新查询拿到最新的 `version`，确认内容没被别人改过后再提交
- 删除：`DELETE /payslips/{id}`，请求体 `{"version": 2}`。已发出的工资条删除后就是撤回，员工打开链接会看到「已撤回」
- 响应里有 `warning` 时要转告小黑

### 5.5 员工有异议时的更正流程

1. 把员工的 `dispute_text`、附图张数和工资条内容整理给小黑，由小黑核实。附图小黑可以直接打开工资条链接（`link`）查看
2. 有异议的工资条已经作废，员工页面会醒目显示「有异议，已作废」和员工写的异议内容
3. 小黑确定更正内容后，生成更正版：和普通生成一样，多传一个 `"corrects_id": <有异议的工资条 id>`。周期可以和原来一样，因为作废的那张不参与重叠检查
4. 按发送前确认的规则，经小黑确认后发送更正版。员工打开旧链接时，页面上会出现「查看更正后的工资条」

每张有异议的工资条只能有一张更正版。如果更正版也被提了异议，就再针对更正版生成新的更正版。

## 六、每周兼职工资条的标准流程

1. `GET /ping` 确认令牌可用
2. `GET /employees?store_id=…` 拉员工名单，把工资表里的每个人对应到员工 `id`。对不上的列出来问小黑
3. 生成 JSON 文件，跑脚本 `POST /payslips`。有错就改 JSON 重跑，直到全部通过
4. 把汇总发给小黑：人数、每人实发、合计、没有企微的人。等小黑回复「确认发送」
5. 跑脚本 `POST /payslips/send`，把结果汇报给小黑：成功几人、失败几人（附原因）、需要手动转发的名单和链接
6. 之后按小黑的要求查进度，汇报谁已确认、谁未确认、谁有异议及异议内容
