Easy!Appointments 1.6.1 API 使用详解
Easy!Appointments 1.6.1 REST API v1 实测详解:Basic/Bearer 认证、13 类资源 CRUD、分页过滤排序约定、错误码分支与排障实录,全部 curl 回显照抄。
Easy!Appointments 1.6.1 API 使用详解
目标:完整讲清 Easy!Appointments 1.6.1 REST API v1 的认证方式、资源清单、通用查询约定与各资源域的调用方法,全部请求/响应均以 curl 在 Easy!Appointments 1.6.1 实测环境(Ubuntu 22.04,应用地址 http://192.168.146.128:8128)实际执行并逐行照抄回显。
一、环境信息
| 项目 | 值 |
|---|---|
| 应用版本 | Easy!Appointments 1.6.1(官方 zip 安装 + 官方 Docker 镜像环境均实测) |
| API 版本 | REST API v1(路由前缀 api/v1,随系统版本演进) |
| 应用地址 | http://192.168.146.128:8128(下文示例统一用此基址) |
| API 入口 | /index.php/api/v1/...(短路径 /api/v1/... 默认不可用,见 FAQ 1) |
| 认证 | HTTP Basic(用户名+口令)或 Bearer Token(API Token) |
| 客户端工具 | curl 7.81.0(任何 HTTP 客户端均可) |
| 测试账号 | administrator(安装向导创建的管理员账号) |
二、前置条件
- 一套已安装完成的 Easy!Appointments 1.6.1(API 在安装向导完成后即可用);
- 一个可调用 API 的账号:API 与后台共用同一套账号口令(存在 ea_user_settings.username),管理员角色可访问全部资源;提供者/秘书角色仅能访问其权限范围内的资源;
- 客户端网络可达应用地址;
- 熟悉 JSON:所有请求体与响应体均为 JSON,
Content-Type: application/json必须显式携带(缺失时 POST 请求体会解析失败)。
三、API 总览
3.1 协议与风格
REST API v1 基于 HTTP/1.1 + JSON,动词语义标准:
| 方法 | 语义 | 实测状态码 |
|---|---|---|
| GET | 查询集合/单条 | 200 |
| POST | 新建 | 成功返回 201(实测响应体为新建对象) |
| PUT | 全量/局部更新 | 200(响应体为更新后对象) |
| DELETE | 删除 | 204(无响应体) |
失败分支实测:未认证 401、参数缺失 400(响应体为 JSON 错误说明)、资源不存在 404。
3.2 资源清单
依据应用路由配置(application/config/routes.php)实测整理,v1 共开放以下资源,全部支持标准 CRUD(availabilities 与 settings 例外,仅查询/更新指定项):
| 资源 | 路径 | 说明 |
|---|---|---|
| 预约 | /appointments | 核心对象:客户×提供者×服务的时段绑定 |
| 客户 | /customers | 预约下单方 |
| 服务 | /services | 可预约服务(时长/价格/时段粒度) |
| 服务分类 | /service_categories | 服务分组 |
| 提供者 | /providers | 服务提供者(技师/顾问) |
| 管理员 | /admins | 管理员账号 |
| 秘书 | /secretaries | 前台代管角色 |
| 不可用时段 | /unavailabilities | 提供者临时封锁时间 |
| 封锁时段 | /blocked_periods | 全局性日期封锁 |
| 工作计划例外 | /working_plan_exceptions | 节假日/特殊作息 |
| Webhooks | /webhooks | 事件推送配置 |
| 设置 | /settings | 运行时设置(读指定项/更新指定项) |
| 可用时段 | /availabilities | 查询提供者在某日期的空闲时段(仅 GET) |
3.3 认证方式
方式一:HTTP Basic(实测主路径)——用户名与口令即后台登录账号:
curl -u 'administrator:<口令>' https://<应用地址>/index.php/api/v1/services
方式二:Bearer Token——在后台 Settings 页面设置 API Token 后,用 Authorization: Bearer <token> 头调用。1.6.1 起该令牌属于受保护设置:不能通过 REST API 读取或写入(实测尝试经 API 写入返回 403,这是 1.6.1 CHANGELOG 中的安全修复「Protected settings, such as the API token, can no longer be read or changed through the REST API」),只能在后台界面设置。未配置令牌时 Bearer 请求按未认证处理(实测返回 401)。
限流约定:v1 无独立限流参数;后台登录接口有登录尝试限速(1.6.1 恢复了 throttle),API 调用频次应遵循常规 Web 服务礼貌(脚本轮询建议间隔 ≥1 秒)。
四、认证与首次调用
第一次调用:无凭据访问任意资源,观察 401 分支:
curl -s http://192.168.146.128:8128/index.php/api/v1/appointments
实测回显(HTTP 状态 401):
You are not authorized to use the API.
携带 Basic 凭据重试:
curl -s -u 'administrator:administrator' http://192.168.146.128:8128/index.php/api/v1/services
实测回显(HTTP 200,输出节选):
[{"id":1,"name":"Service","duration":30,"price":0,"currency":"","description":null,"location":null,"slotInterval":15,"attendantsNumber":1,"isPrivate":false,"serviceCategoryId":null}]
💡 说明:本环境演示口令为 administrator,仅测试环境如此;生产账号请使用强口令。系统安装后自带一条演示服务(Service,30 分钟、15 分钟时段粒度),上面回显即它。
错误口令实测(同 401 提示,不泄露账号是否存在):
curl -s -u 'administrator:wrongpass' http://192.168.146.128:8128/index.php/api/v1/services
实测回显:
You are not authorized to use the API.
五、各 API 域详解
5.1 预约(/appointments)
预约是最核心也最复杂的资源。新建预约(POST,字段为 camelCase 短名):
curl -s -u 'administrator:administrator' -X POST -H 'Content-Type: application/json' \
-d '{"book":"2026-10-23 10:00:00","start":"2026-10-26 10:00:00","end":"2026-10-26 10:30:00","serviceId":1,"providerId":2,"customerId":3,"notes":"API预约实测"}' \
http://192.168.146.128:8128/index.php/api/v1/appointments
实测回显(响应体,格式化展示):
{"id":1,"book":"2026-10-23 02:52:07","start":"2026-10-26 10:00:00","end":"2026-10-26 10:30:00","hash":"QgB8qDxMvA0k","color":"#7cbae8","status":"","location":null,"notes":"API预约实测","customerId":3,"providerId":2,"serviceId":1,"meetingLink":null,"googleCalendarId":null,"caldavCalendarId":null}
字段说明:book 为录入时间(服务端自动校正);hash 为公开取消/修改链接用的预约哈希;color 日历显示色;customerId/providerId/serviceId 三方外键必填;end 可省略(省略时按服务时长自动推算)。
⚠️ 排障实录:字段名用数据库蛇形命名(start_datetime/id_users_provider 等)会报字段缺失,用 camelCase 长名(startDateTime)则会被静默丢弃后报必填缺失——正确字段名是短驼峰 book/start/end/serviceId/providerId/customerId。缺字段时的 400 回显(实测,错误信息会列出已收到的字段,便于对照):
{"success":false,"message":"Not all required fields are provided: Array\n(\n [book_datetime] => 2026-10-26\n [notes] => API预约实测\n [id_users_customer] => 3\n [id_users_provider] => 2\n [id_services] => 1\n [is_unavailability] => \n [end_datetime] => 2026-10-23 03:19:40\n)\n"}
按日期过滤查询:
curl -s -u 'administrator:administrator' 'http://192.168.146.128:8128/index.php/api/v1/appointments?date=2026-10-26'
实测回显(返回上一步创建的预约,输出节选):
[{"id":1,"book":"2026-10-23 02:52:07","start":"2026-10-26 10:00:00","end":"2026-10-26 10:30:00","hash":"QgB8qDxMvA0k","color":"#7cbae8","status":"","location":null,"notes":"API预约实测","customerId":3,"providerId":2,"serviceId":1,"meetingLink":null,"googleCalendarId":null,"caldavCalendarId":null}]
日期域查询参数全集(实测依据控制器实现):date(按起始日)、from / till(区间)、serviceId / providerId(按外键过滤),可组合使用。
更新预约(PUT,只传要改的字段):
curl -s -u 'administrator:administrator' -X PUT -H 'Content-Type: application/json' \
-d '{"status":"confirmed"}' \
http://192.168.146.128:8128/index.php/api/v1/appointments/1
实测回显(输出节选,仅摘关键字段;响应体为更新后的完整预约对象):
{"id":1, "status":"confirmed", "start":"2026-10-26 10:00:00", ...}
删除预约(DELETE):
curl -s -u 'administrator:administrator' -X DELETE -o /dev/null -w '%{http_code}\n' \
http://192.168.146.128:8128/index.php/api/v1/appointments/1
实测回显:
204
5.2 客户(/customers)
客户是低门槛资源,常用于对接自有 CRM。搜索(q 参数全文匹配姓名/邮箱等):
curl -s -u 'administrator:administrator' \
'http://192.168.146.128:8128/index.php/api/v1/customers?q=James'
实测回显:
[{"id":3,"firstName":"James","lastName":"Doe","email":"james@example.org",...}]
新建客户:
curl -s -u 'administrator:administrator' -X POST -H 'Content-Type: application/json' \
-d '{"firstName":"重启","lastName":"恢复测试","email":"restart-recovery@example.org","phone":"13800000000"}' \
http://192.168.146.128:8128/index.php/api/v1/customers
实测回显:
{"id":5,"firstName":"重启","lastName":"恢复测试","email":"restart-recovery@example.org","phone":"13800000000","address":null,"city":null,"zip":null,"notes":null,"timezone":"UTC","language":"english","customField1":null,...,"ldapDn":null}
查询单条并裁剪返回字段(fields 参数,逗号分隔):
curl -s -u 'administrator:administrator' \
'http://192.168.146.128:8128/index.php/api/v1/customers/3?fields=firstName,lastName,email'
实测回显:
{"firstName":"James","lastName":"Doe","email":"james@example.org"}
5.3 服务(/services)
服务的建改删在《Ubuntu 22.04 安装 Easy!Appointments 1.6.1 指南》与配置篇已有零散示例,本节给完整生命周期实测。新建:
curl -s -u 'administrator:administrator' -X POST -H 'Content-Type: application/json' \
-d '{"name":"API实测服务","duration":45,"price":50,"currency":"CNY","description":"API创建","attendantsNumber":1,"slotInterval":15}' \
http://192.168.146.128:8128/index.php/api/v1/services
实测回显:
{"id":2,"name":"API实测服务","duration":45,"price":50,"currency":"CNY","description":"API创建","location":null,"slotInterval":15,"attendantsNumber":1,"isPrivate":false,"serviceCategoryId":null}
字段说明:duration 服务时长(分钟),slotInterval 日历上时段划分粒度(分钟),attendantsNumber 单时段可容纳人数(小组课场景 >1)。更新(PUT /services/2,实测把 name 改为「API实测服务-改」后回显新值);删除(DELETE /services/2,实测 204)。
参数校验实测(负价格/零时段粒度在服务端被拒绝,1.6.1 CHANGELOG:价格不可为负、时段粒度不可为 0;空必填字段返回 400,回显格式见 5.1 节)。
5.4 提供者、管理员与不可用时段
提供者清单:
curl -s -u 'administrator:administrator' http://192.168.146.128:8128/index.php/api/v1/providers
实测回显(节选):
[{"id":2,"firstName":"Jane","lastName":"Doe",...}]
管理员清单(实测返回邮箱而非用户名,口令永不返回):
[{"id":1,"email":"john@example.org",...}]
不可用时段(提供者临时封锁,如午休/外出):
curl -s -u 'administrator:administrator' -X POST -H 'Content-Type: application/json' \
-d '{"start":"2026-10-27 12:00:00","end":"2026-10-27 13:00:00","providerId":2,"notes":"午休封锁"}' \
http://192.168.146.128:8128/index.php/api/v1/unavailabilities
实测回显:
{"id":2,"book":"2026-10-23 02:52:07","start":"2026-10-27 12:00:00","end":"2026-10-27 13:00:00","hash":"aA6SeNyM5xk2","location":null,"notes":"午休封锁","providerId":2,...}
删除同样返回 204(实测 DELETE /unavailabilities/2 → 204)。
5.5 可用时段查询(/availabilities)
对接「选时段」前端时最常用的只读接口:
curl -s -u 'administrator:administrator' \
'http://192.168.146.128:8128/index.php/api/v1/availabilities?providerId=2&serviceId=2&date=2026-10-26'
实测回显(工作日 09:00–18:00、15 分钟粒度、扣除午休时段):
["09:00","09:15","09:30","09:45","10:00","10:15","10:30","10:45","11:00","11:15","11:30","11:45","12:00","12:15","12:30","12:45","13:00","13:15","13:30","15:00","15:15","15:30","15:45","16:00","16:15","16:30","16:45","17:00"]
💡 说明:回显中 13:30 后直接跳到 15:00——这正是默认工作计划中 14:30–15:00 午休与已占用时段共同作用的结果;该接口是判断「能不能下单」的权威来源,前端不要自行计算空闲。
5.6 设置(/settings)
设置域只提供「读指定项」与「更新指定项」:
curl -s -u 'administrator:administrator' http://192.168.146.128:8128/index.php/api/v1/settings
实测回显(节选):
[{"name":"company_working_plan","value":"{\"monday\":{\"start\":\"09:00\",\"end\":\"18:00\",\"breaks\":[{\"start\":\"14:30\",\"end\":\"15:00\"}]},..."}]
受保护设置(api_token 等)经 API 读取或写入均被 1.6.1 拒绝(实测写入返回 403),只能后台界面操作。
六、分页/过滤/排序通用约定
全部集合类资源共用以下查询参数(实测依据 API 库实现并用 /customers 验证):
| 参数 | 类型 | 说明 |
|---|---|---|
| page | 整数 ≥1 | 页码(1 起);offset = (page-1)×length |
| length | 整数 ≥1 | 每页条数(省略用系统默认值) |
| q | 字符串 | 关键字搜索(姓名/邮箱等文本字段) |
| sort | 字符串 | 排序字段,前缀 + 升序 / - 降序,多字段逗号分隔(如 sort=-id) |
| fields | 字符串 | 返回字段裁剪,逗号分隔(如 fields=firstName,lastName) |
| with | 字符串 | 关联对象展开(如预约带出客户/服务详情) |
组合实测:
curl -s -u 'administrator:administrator' \
'http://192.168.146.128:8128/index.php/api/v1/customers?sort=-id&length=2'
实测回显(按 id 倒序取前 2 条):
[{"id":5,...},{"id":4,...}]
错误码约定(实测汇总):401 未认证或凭据错误;400 请求体缺失必填字段或参数非法(message 字段直接说明缺什么);404 资源路径或 ID 不存在(含未安装/已停用的资源名);403 访问受保护设置或越权操作。删除成功无响应体(204),判断删除结果只看状态码。
七、常见问题(FAQ)
Q1:访问 /api/v1/services 返回 404,加了 /index.php 前缀就通?
正常现象。发行包不带根 .htaccess 重写规则,短路径默认不可用(实测 404)。所有 API 调用统一写全 index.php/api/v1/...;如必须用短路径,需自行在 Web 服务器层补重写规则。
Q2:POST 时提示 400「Not all required fields are provided」?
对照错误信息里列出的字段清单——它显示的是服务端已收到的字段,逐个核对是否用了正确的驼峰短名(预约用 book/start/end/serviceId/providerId/customerId,详见 5.1 节排障实录);同时确认请求头带了 Content-Type: application/json。
Q3:Bearer Token 怎么开启?为什么 API 设置不了?
在后台 Settings 界面设置 API Token 后即可用 Authorization: Bearer <token> 调用。1.6.1 起 api_token 属受保护设置,经 REST API 读写都会被拒(实测 403),这是安全修复(防止令牌被越权读取/篡改),不是故障。
Q4:创建预约成功但前台日历没显示?
先核对 providerId 与 serviceId 是否真实存在(GET 列表确认 id);再查该时段是否在工作计划内(工作计划外时段预约可建但不显示于预约页);最后查响应里的 googleCalendarId/caldavCalendarId——若配置了外部同步,同步失败会记日志但不影响站内数据。
Q5:能通过 API 建管理员/改角色吗? 管理员与秘书资源存在(/admins、/secretaries),但 1.6.1 收紧了角色越权:通过普通用户类型端点改写/冒用其他角色会被拒绝(CHANGELOG 安全修复),建议管理员管理操作走后台界面完成,API 仅用于业务数据(预约/客户/服务)。
Q6:Webhooks 怎么配?
GET /webhooks 清单当前为空(实测 []);POST /webhooks 创建推送目标(事件类型 + 目标 URL + 密钥头),1.6.1 起允许通过 API 设置 secret header(CHANGELOG),事件触发时系统向目标 URL 推送 JSON。
八、参考链接
- 官方 REST API 文档:https://easyappointments.org/docs.html#rest-api
- API 控制器源码(字段与参数的权威定义):https://github.com/alextselegidis/easyappointments/tree/master/application/controllers/api/v1
- GitHub 1.6.1 Release Notes(API 相关安全修复清单):https://github.com/alextselegidis/easyappointments/releases
- 站内延伸:《Easy!Appointments 1.6.1 介绍与选型指南》《Easy!Appointments 1.6.1 配置参数详解》《Easy!Appointments 1.6.1 Docker Compose 部署指南》