Files
Codex Local bf1233d5df
ci / verify (push) Canceled after 0s
feat: expand companion platform, device controls, and memoir workflows
Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
2026-10-08 10:06:44 +08:00

208 lines
11 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
openapi: 3.1.0
info:
title: 小程序原生登录与自动蓝牙连接
version: 2.0.1
description: HTTPS;POST 必须携带 UUID v4 X-Request-ID。响应使用 ok/code/request_id/server_time_ms/data 信封。不发 Cookie,不使用 AppSecret 或云开发。现有网页 Origin/CSRF 规则不变。
paths:
/api/v1/mini/login/session:
post:
summary: 创建20分钟原生登录会话
security: []
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: false
required: [portal]
properties:
portal: {type: string, enum: [customer, operator]}
responses:
'200': {description: 'data: {login_token: realm.opaque, expires_in: 1200, sms_enabled: boolean}'}
default: {description: 参数错误、限流或服务异常}
/api/v1/mini/login/captcha:
post:
summary: 获取图形验证码
security: [{MiniLogin: []}]
requestBody: {$ref: '#/components/requestBodies/Empty'}
responses:
'200': {description: 'data: {captcha_id, image: PNG data URL, expires_at_ms}'}
default: {description: 登录会话失效或限流}
/api/v1/mini/login/password:
post:
summary: 使用已有手机号密码登录,不自动注册或提升权限
description: 设备未通过短信验证或验证已过期时返回 403 NEW_DEVICE_SMS_REQUIRED。先完成 LOGIN 短信验证,再将返回的 trusted_device_token 按服务地址、账号和 portal 保存在客户端,后续密码登录携带。设备验证最长 30 天,不代替密码或人机验证。
security: [{MiniLogin: []}]
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: false
required: [phone_e164, password, captcha_id]
anyOf:
- required: [captcha_code]
- required: [captcha_ticket]
properties:
phone_e164: {type: string}
password: {type: string, maxLength: 256}
trusted_device_token: {type: string, maxLength: 43, writeOnly: true}
captcha_id: {type: string, format: uuid}
captcha_code: {type: string, pattern: '^[0-9]{6}$'}
captcha_ticket: {type: string, maxLength: 4096, writeOnly: true}
responses:
'200': {description: 'data: {access_token: realm.opaque, portal: customer|operator, expires_in: 86400};会话只能成功消费一次'}
default: {description: 验证码或密码错误、账号不可用或限流}
/api/v1/mini/login/sms/send:
post:
summary: 小程序发送手机号登录验证码
description: 与 SaaS 网页、设备绑定入口共用腾讯云短信通道、手机号发送限额和 6 位验证码规则;需先调用 login/captcha。不会返回验证码。
security: [{MiniLogin: []}]
requestBody:
required: true
content:
application/json:
schema: {$ref: './onboarding.yaml#/components/schemas/SMSSendInput'}
responses:
'200': {description: 'data: {sms_challenge_id, expires_at_ms, resend_after_ms}'}
default: {description: 图片验证码错误、发送失败、登录会话失效或限流}
/api/v1/mini/login/sms/verify:
post:
summary: 小程序验证短信并获得原生登录令牌
description: 客户可按唯一自注册租户配置创建普通账号;管理账号必须预先绑定手机号。挑战与小程序登录会话绑定,成功后一次性消费,不发 Cookie。成功响应另含 trusted_device_token 和 trusted_device_expires_in(2592000 秒),供此设备后续密码登录使用。
security: [{MiniLogin: []}]
requestBody:
required: true
content:
application/json:
schema: {$ref: './onboarding.yaml#/components/schemas/SMSVerifyInput'}
responses:
'200': {description: 'data: {access_token: realm.opaque, portal: customer|operator, expires_in: 86400}'}
default: {description: 验证码错误、过期、账户不可用或登录会话失效}
/api/v1/mini/account:
get:
summary: 校验原生登录状态
security: [{Mini: []}]
responses:
'200': {description: 'data: {authenticated: true, portal, display_name}'}
default: {description: 登录已过期、撤销或账号被停用}
/api/v1/mini/logout:
post:
summary: 撤销当前原生会话
security: [{Mini: []}]
requestBody: {$ref: '#/components/requestBodies/Empty'}
responses:
'200': {description: 'data: {signed_out: true}'}
default: {description: 登录失效或服务异常}
/api/v1/mini/ble-session:
post:
deprecated: true
summary: 自动建立设备加密连接,无需手动输入设备编号或配网密码
security: [{Mini: []}]
description: 仅允许有设备租户内家庭管理权限的家庭账号和未绑定设备。验证设备随机连接证明,返回本次连接专用 SRP 密码;绝不返回工厂密钥或标签配网密码。
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: false
required: [device_id, model, key_version, client_nonce, server_nonce, proof]
properties:
device_id: {type: string, pattern: '^KP01-[0-9A-F]{12}$'}
model: {type: string}
key_version: {type: integer, minimum: 1}
client_nonce: {type: string, pattern: '^[A-Za-z0-9_-]{22}$'}
server_nonce: {type: string, pattern: '^[A-Za-z0-9_-]{22}$'}
proof: {type: string, pattern: '^[a-f0-9]{64}$'}
responses:
'200': {description: 'data: {device_id, model, session_password: 64字符小写十六进制};只在当前蓝牙连接内有效'}
default: {description: 设备或家庭权限不满足、重放证明或绑定暂停}
/api/v1/mini/ble-device-state:
post:
summary: 查询设备账号归属及管理权限;未添加的设备不要求家庭或老人(明文BLE v2)
description: 按实际失败条件返回独立 code 和中文 message,不再合并为 DEVICE_UNAVAILABLE。错误信封含 ok=false、code、message、request_id;不包含其他账号身份或老人资料。详见 docs/device-ble-errors-20260918.md。
security: [{Mini: []}]
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: false
required: [device_id]
properties:
device_id: {type: string, pattern: '^KP01-[0-9A-F]{12}$'}
responses:
'200': {description: 'data: {device_id, id: 平台设备UUID, model, bound: boolean};bound表示已有账号或老人归属,true时只更新Wi-Fi,不申请bind_token'}
'400': {description: 'DEVICE_ID_INVALID:设备编号格式错误'}
'403': {description: 'ACCOUNT_NOT_FOUND / ACCOUNT_DISABLED / ACCOUNT_DELETED / CUSTOMER_REQUIRED / DEVICE_TENANT_MISMATCH / TENANT_INACTIVE / DEVICE_REVOKED / DEVICE_LOST / DEVICE_QUARANTINED / DEVICE_OWNED_BY_OTHER_ACCOUNT / DEVICE_MANAGE_PERMISSION_REQUIRED;message 提供具体原因及处理方式'}
'404': {description: 'DEVICE_NOT_REGISTERED:平台没有该设备的注册记录'}
'409': {description: 'DEVICE_NOT_RELEASED:尚未出厂放行;DEVICE_ENROLLMENT_UNSUPPORTED:尚未启用蓝牙实体确认绑定;DEVICE_BINDING_STALE:归属记录与当前设备状态不一致'}
default: {description: 参数错误、登录失效、限流或服务异常}
/api/v1/device/ble-state:
post:
summary: 设备用工厂HMAC查询自身是否已绑定,只读,不改变归属
security: [{DeviceHMAC: []}]
requestBody: {$ref: '#/components/requestBodies/Empty'}
responses:
'200': {description: 'data: {bound: boolean};设备仅在true时保存空bind_token任务的新Wi-Fi'}
default: {description: 工厂鉴权失败、设备不可用、限流或服务异常}
/api/v1/mini/ble-bind-tokens:
post:
summary: 为当前客户账号签发5分钟设备绑定令牌,无需先创建家庭或老人
description: 设备和账号校验使用与 ble-device-state 相同的具体错误码及提示。另有 ELDER_ID_INVALID(400,显式老人编号格式错误)及 ALREADY_BOUND(409,已有归属,无需重复添加)。
security: [{Mini: []}]
requestBody:
$ref: './ble-binding.yaml#/paths/~1api~1v1~1web~1ble-bind-tokens/post/requestBody'
responses:
'200': {description: 'data: {bind_token};设备已有归属或使用者无管理权限时拒绝'}
default: {description: 请求无效、登录失效、权限不足或限流}
/api/v1/mini/ble-bind-status:
post:
summary: 查询当前账号签发的绑定令牌结果
security: [{Mini: []}]
requestBody: {$ref: './ble-binding.yaml#/components/requestBodies/Token'}
responses:
'200': {description: 'data: {status: pending|success|failed, message?};蓝牙断开后仍可查询'}
default: {description: 请求无效、登录失效或令牌不可访问}
/api/v1/mini/device-subject:
post:
summary: 将已添加到当前账号的设备绑定给有管理权限的老人
security: [{Mini: []}]
parameters:
- in: header
name: X-Request-ID
required: true
schema: {type: string, format: uuid}
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: false
required: [device_id, elder_id, expected_account_binding_id]
properties:
device_id: {type: string, format: uuid, description: 设备列表中的平台UUID,不是工厂编号}
elder_id: {type: string, format: uuid}
expected_account_binding_id: {type: string, format: uuid, description: 设备列表返回的account_binding_id}
responses:
'200': {description: 'data: {status: success, device_id, elder_id};同一有效归属和同一老人可安全重试,不重复注册火山'}
'403': {description: 非设备归属账号、无老人管理权限、跨组织或归属已失效}
'409': {description: 设备有待确认操作或升级}
default: {description: 参数错误、登录失效、限流或服务异常}
components:
securitySchemes:
DeviceHMAC: {type: apiKey, in: header, name: Authorization, description: 'Device-HMAC-SHA256 hex;还需X-Device-ID、X-Key-Version、X-Timestamp-Ms、X-Nonce、X-Request-ID,详见docs/device-mini-ble-v2.md'}
Mini: {type: apiKey, in: header, name: Authorization, description: 'Mini 后接 customer.或operator.开头的原生access_token。业务接口仍按原资源白名单和服务器权限验证。'}
MiniLogin: {type: apiKey, in: header, name: Authorization, description: 'MiniLogin 后接 login_token,仅允许验证码和密码登录接口。'}
requestBodies:
Empty:
required: true
content:
application/json:
schema: {type: object, additionalProperties: false}