客户端 V3:账户、访问与机器人通知
除测试登录接口外,以下接口均使用 POST、Content-Type: application/json、Authorization: Bearer <用户端 access_token>。用户作用域由登录态确定,不接受 user_id 切换对象。成功 code=1,业务失败 code=0,无登录态 HTTP 401。示例中的邮箱与账号均为演示值。
1. 机器人通知
读取:/home/client/notifications
请求 {}。首次无设置记录时返回以下默认值,读取操作不创建记录:
{"code":1,"msg":"获取成功","data":{"afterSale":true,"platform":true,"orders":false,"funds":false}}保存:/home/client/saveNotifications
{"afterSale":true,"platform":true,"orders":false,"funds":false}四项均必填,必须是 JSON boolean,不能传字符串 "false" 或数字 0/1。成功 data 为服务端保存后的完整偏好。保存失败时前端保留原值。
| 字段 | 含义 | 默认 |
|---|---|---|
| afterSale | 售后进度、商家/平台回复 | true |
| platform | 用户端平台消息 | true |
| orders | 订单支付成功提醒 | false |
| funds | 用户端余额资金流水变动 | false |
消息发送使用现有用户端 BOT_APP_TOKEN,chat_id 为平台已有用户 ID(来自 Telegram 身份),不使用商户通知机器人的 telegram_bot_token,不改变商户通知设置。用户需启动该用户端机器人并允许发信;保存偏好不等于 Telegram 已收到消息。关闭机器人分类不关闭站内消息及 WebSocket。
发送入口:用户端 MessageService::send(..., user_type=2) 的投诉/平台消息;售后商家/管理员新回复、平台介入;订单支付成功;record_user_money_log(..., type=user_money, user_type=2) 的资金变动。资金站内消息不重复生成机器人消息。同一事件 key 去重。
client_bot_outbox 在业务事务中入库,client-bot-notify 独立进程每 5 秒读取已提交任务,发送前再次检查偏好和账户状态。失败最多尝试 3 次,间隔 60/120 秒;任务租约 60 秒,成功 sent、关闭 skipped、最终失败 failed。网络响应丢失可能导致重试重复,Telegram sendMessage 不提供端到端 exactly-once 保证。没有 Token 时不消费。不要把发送箱状态/Token 暴露给客户端。
2. 最近访问与快捷访问
读取:/home/client/access
请求 {},首次返回:
{"code":1,"msg":"获取成功","data":{"recent":[],"pinned":[]}}有记录时数组项为:
{"shopNo":"123456","name":"示例店铺","visitedAt":1790000000000,"avatarUrl":"/uploads/shop.png"}visitedAt 是毫秒时间戳;清空历史后仍保留的快捷记录可为 0。avatarUrl 为当前店铺头像,无头像时为空字符串,由客户端展示名称首字。最近访问最多 30 家,按最后访问倒序,按店铺号去重。快捷最多 7 家,按加入顺序展示,服务端跨设备同步。不导入演示稿数据,也不把收藏自动作为快捷。
通过店铺状态校验的 /home/shop 成功加载时自动记录最近访问;客户端无需再 POST 一个任意店名来写历史。无效店铺、被拒绝访问不记录。
设置快捷:/home/client/pinShop
{"shop_no":"123456","pinned":true}shop_no 必须为数字字符串(1–64位),pinned 必须为 boolean。只能操作本人已访问、已有快捷或本人收藏的店铺,不能读取/修改他人记录。收藏直接加入快捷不会伪造最近访问。true 添加,false 移除,重复操作幂等。成功 data 与读取接口一致。
达到 7 家时,客户端打开「选择一家替换」面板,并提交 {"shop_no":"123456","pinned":true,"replace_shop_no":"654321"}。replace_shop_no 为当前用户已设的另一家快捷,仅在添加时允许;移除旧快捷与添加新快捷在同一事务中完成,任何失败均保留原快捷。缺少替换目标且达到上限时仍返回业务错误;同一请求重试幂等。
清空历史:/home/client/clearRecent
请求 {}。只清空当前账户最近访问,保留手动快捷和已有店铺收藏。成功 data 与读取接口一致。
3. 恢复邮箱与换绑
状态:/home/client/recoveryEmail
请求 {}:
{"code":1,"msg":"获取成功","data":{"email":"","enabled":false,"code_length":6}}当前已绑定邮箱非空即 enabled=true,兼容已有绑定用户。此接口描述绑定状态;不提供匿名邮箱登录、跨 Telegram 身份合并或自动转移账户资产。
发送验证码:/home/user/sendEmailCode
{"email":"new@example.com"}{"code":1,"msg":"发送成功","data":{"retry_after":60,"expires_in":300,"code_length":6}}验证码为 6 位 ASCII 数字,使用安全随机数生成;接口不返回验证码。有效期 300 秒,同一账户 60 秒内不能再次发送(换邮箱也不能绕过)。邮件发送失败不生成新挑战。数据库只存散列,按登录用户和目标邮箱绑定,与商户验证码隔离。
启用/换绑:/home/user/bindEmail
{"email":"new@example.com","code":"123456"}{"code":1,"msg":"已启用邮箱恢复","data":{"email":"new@example.com","enabled":true}}code 推荐始终传字符串,严格 6 位 [0-9];5/7位、字母、空格、全角数字均不通过。首次绑定只验证新邮箱;已绑定用户必须先验证原邮箱。先调用 /home/user/sendEmailCode,传 { "purpose": "old" }(收件人由服务端读取,忽略请求邮箱),再调用 /home/user/verifyOldEmail,传 { "code": "123456" }。成功获得绑定当前账户和原邮箱的 600 秒授权,再发送新邮箱验证码并调用 bindEmail。授权过期、原邮箱被其他请求修改、未验证原邮箱时,发送新邮箱验证码和提交换绑都会被拒绝;原邮箱验证码不能用于 bindEmail,新邮箱验证码不能用于 verifyOldEmail。换绑成功同时消费验证码和授权。原邮箱不可用时走客服申诉。旧商户接口 /merchantapi/user/user/bindEmail 仅允许首次绑定,已绑定用户会被拒绝,避免绕过此流程覆盖共享邮箱字段。最多 5 次错误尝试,成功后即消费不可重用,失败不替换原邮箱。成功后刷新 /home/user/userDetail。上线前旧缓存验证码失效,用户需重新发送。
4. 分享店铺
POST /home/shop/share,请求 {"token":"123456"}。
{"code":1,"msg":"获取成功","data":{"shop_no":"123456","shop_name":"示例店铺","shop_logo":"","description":"店铺描述"}}description 取自已有 shop_intro 并去除 HTML 标签,不是店铺公告 shop_notice。V3 只在进入分享页后展示/复制描述,店铺主页不展示。旧 /home/shop 响应字段为兼容老客户端继续保留。
5. 开发环境测试账号
两个接口均无需登录,但服务端必须 DEVELOPMENT=1。关闭时返回 code=0,不发放 token。
POST /home/base/debugAccounts,请求{},返回data.list=[{"sub":1000,"username":"test1000"}, ...],范围 1000–1050 共 51 个。POST /home/base/debugLogin,请求{"sub":1000},仅允许上述固定范围,用户名由服务端产生。创建/复用普通用户并检查状态后,返回data.access_token,再使用/home/user/userDetail获取详情。
前端开发模式默认开放,.env.local 中 VITE_DEBUG_MODE=false 可关闭;生产构建无论该变量如何设置都禁用。入口 /login?debugLogin=1,支持搜索/选择及账户页切换。显式测试登录优先于 TG 自动登录,所选测试身份在开发刷新后保持,不被当前 TG 身份覆盖。生产构建会丢弃本地标记为 debug 的会话。
6. 数据库与部署
已有数据库发布本次邮箱安全更新前,先执行 sql/2026-10-07-client-email-verification.sql,新增验证码用途、原邮箱快照和授权有效期字段,再发布后端和前端并重启 Webman;旧版本客户端不能直接换绑。新数据库先执行 sql/2026-09-28-client-v3.sql(可重复执行),建立 client_preferences、client_shop_access、client_email_code、client_bot_outbox,再发布代码并重启 Webman HTTP 和 client-bot-notify 进程。现有邮箱账号池、BOT_APP_TOKEN 及用户机器人关系继续使用,不需要配置商户机器人。
Windows 完整服务可沿用 php windows.php;仅启动 HTTP 不能消费机器人待发送记录。部署生产环境必须关闭 DEVELOPMENT。客户端本地开发 API 代理配置 API_PROXY_TARGET=http://127.0.0.1:7979,前端域名不应直接暴露服务端凭据。
本轮迁移已在配置的开发 MySQL 连续执行两次。自动化用真实 ORM 验证 SQLite 和开发 MySQL,开发库测试以事务回滚;邮件及机器人网络发送使用替身。真实邮箱投递和 Telegram 送达仍需在目标环境验收。