/* ===== MumIot API 文档 · 共享脚本 ===== */ /* 👇 只需在这里改一次默认 Base URL(生产用反向代理域名) */ const DEFAULT_BASE="http://mum.tonzou.cn/api/v1"; /* 👇 产品名(用于标题/品牌/首页),只改这一处 */ const PRODUCT_NAME="MumIot"; const AUTH={req:"强制",opt:"可选",none:"无"}; function F(n,t,r,d){return [n,t,r,d];} const BASE_FIELDS=[ F("id","Long","-","主键(雪花ID, @TableId ASSIGN_ID)"), F("createTime","LocalDateTime","-","创建时间(自动填充)"), F("updateTime","LocalDateTime","-","更新时间(自动填充)"), F("deleted","Integer","-","逻辑删除(0未删/1已删, @TableLogic)"), F("tenantId","Long","-","租户ID(自动填充)"), F("createBy","Long","-","创建人ID(自动填充)"), F("updateBy","Long","-","更新人ID(自动填充)") ]; /* ===== 数据模型(字段 + JSON 示例) ===== */ const MODELS={ LoginRequest:{title:"LoginRequest 登录请求体",fields:[F("username","String","是","用户名"),F("password","String","是","密码")],example:{username:"admin",password:"admin123"}}, RefreshRequest:{title:"RefreshRequest 刷新Token",fields:[F("refreshToken","String","是","刷新令牌")],example:{refreshToken:"eyJhbGciOiJIUzI1Ni...refresh"}}, RegisterRequest:{title:"RegisterRequest 普通用户注册",fields:[F("username","String","是","用户名(3-32,字母数字下划线)"),F("password","String","是","密码(6-20)"),F("nickname","String","是","昵称"),F("phone","String","否","手机号(1[3-9]\\d{9})")],example:{username:"alice",password:"abc123",nickname:"Alice",phone:"13800138000"}}, MemberRegisterRequest:{title:"MemberRegisterRequest 会员注册",fields:[F("username","String","是","用户名(3-50)"),F("password","String","是","密码(6-32)"),F("nickname","String","否","昵称(≤50)"),F("phone","String","否","手机号(≤20)")],example:{username:"member01",password:"abc123",nickname:"小明",phone:"13800138000"}}, PasswordChange:{title:"PasswordChange 修改密码",fields:[F("oldPassword","String","是","原密码"),F("newPassword","String","是","新密码")],example:{oldPassword:"abc123",newPassword:"newPass456"}}, OauthCallback:{title:"OauthCallback 涂鸦OAuth回调",fields:[F("code","String","是","授权码code")],example:{code:"a1b2c3d4"}}, BindTokenRequest:{title:"BindTokenRequest Token绑定",fields:[F("accessToken","String","是","涂鸦access_token")],example:{accessToken:"tok_xxxx"}}, SmsSendRequest:{title:"SmsSendRequest 发送验证码",fields:[F("phone","String","是","手机号"),F("countryCode","String","否","国家码(默认86)")],example:{phone:"13800138000",countryCode:"86"}}, SmsBindRequest:{title:"SmsBindRequest 验证码绑定",fields:[F("phone","String","是","手机号"),F("code","String","是","验证码"),F("countryCode","String","否","国家码(默认86)")],example:{phone:"13800138000",code:"123456",countryCode:"86"}}, UserRequest:{title:"UserRequest 后台用户(增/改)",fields:[F("username","String","是","用户名(3-64)"),F("password","String","否","密码(6-64,创建时必填)"),F("realName","String","否","真实姓名"),F("phone","String","否","手机号"),F("email","String","否","邮箱"),F("status","Integer","否","状态(0禁用/1启用,默认1)"),F("roleIds","List","否","角色ID列表")],example:{username:"operator",password:"abc123",realName:"运维",phone:"13800138000",email:"op@example.com",status:1,roleIds:[2,3]}}, RoleRequest:{title:"RoleRequest 后台角色(增/改)",fields:[F("roleName","String","是","角色名称(≤64)"),F("roleCode","String","是","角色编码(≤64)"),F("description","String","否","描述"),F("status","Integer","否","状态(0禁用/1启用,默认1)"),F("sort","Integer","否","排序(默认0)"),F("permissions","Set","否","权限编码集合")],example:{roleName:"设备管理员",roleCode:"DEVICE_ADMIN",description:"管理设备",status:1,sort:0,permissions:["device:view","device:edit"]}}, MemberRequest:{title:"MemberRequest 会员(增/改)",fields:[F("username","String","是","用户名(3-64)"),F("password","String","否","密码(6-64,创建时必填)"),F("nickname","String","否","昵称"),F("phone","String","否","手机号"),F("email","String","否","邮箱"),F("gender","Integer","否","性别(0未知/1男/2女)"),F("status","Integer","否","状态(0禁用/1正常,默认1)")],example:{username:"member01",password:"abc123",nickname:"小明",phone:"13800138000",email:"m@example.com",gender:1,status:1}}, PlatformConfigRequest:{title:"PlatformConfigRequest 第三方平台配置",fields:[F("platformCode","String","是","平台编码(tuya/google/alexa/ali...)"),F("platformName","String","否","平台名称"),F("enabled","Integer","否","是否启用(0/1)"),F("accessId","String","否","涂鸦Access ID"),F("accessSecret","String","否","涂鸦Access Secret"),F("apiKey","String","否","大模型API Key"),F("modelName","String","否","大模型(qwen-turbo/plus/max)"),F("baseUrl","String","否","API基础地址(OpenAI兼容)"),F("timeoutMs","Integer","否","超时(毫秒)"),F("apiRegion","String","否","API区域(cn/us/eu/in)"),F("projectId","String","否","Google Home Project ID"),F("clientId","String","否","Client ID"),F("clientSecret","String","否","Client Secret"),F("appIdentifier","String","否","App授权签名标识符"),F("alexaClientId","String","否","Alexa Client ID"),F("alexaSecret","String","否","Alexa Secret"),F("region","String","否","服务区域"),F("remark","String","否","备注")],example:{platformCode:"tuya",platformName:"涂鸦开放平台",enabled:1,accessId:"your_access_id",accessSecret:"your_access_secret",apiKey:"sk-xxxx",modelName:"qwen-turbo",baseUrl:"https://dashscope.aliyuncs.com",timeoutMs:30000}}, Device:{title:"Device 设备实体",entity:true,fields:[F("deviceId","String","否","设备唯一标识"),F("deviceName","String","否","设备名称"),F("platform","String","否","平台(tuya/miot/dueros/local/matter)"),F("deviceType","String","否","设备类型"),F("platformProductId","String","否","平台原始产品ID(如涂鸦productId)"),F("firmwareVersion","String","否","固件版本"),F("status","String","否","状态(online/offline/error)"),F("capabilities","JsonNode","否","设备能力描述(JSON)"),F("location","String","否","设备位置"),F("groupId","Long","否","分组ID"),F("parentDeviceId","String","否","父网关设备ID"),F("isGateway","Integer","否","是否网关(1=网关)"),F("ownerId","Long","否","归属用户ID(member.id)"),F("productId","Long","否","所属产品ID"),F("homeId","Long","否","所属家庭ID"),F("roomId","Long","否","所属房间ID"),F("icon","String","否","图标URL"),F("remark","String","否","备注")],example:{deviceId:"DEV_001",deviceName:"客厅灯",platform:"tuya",deviceType:"light",platformProductId:"abc123",firmwareVersion:"1.0.0",status:"online",capabilities:{"switch":true,"brightness":80},location:"客厅",groupId:1,parentDeviceId:null,isGateway:0,ownerId:1001,productId:10,homeId:1,roomId:2,icon:"https://cdn/x/light.png",remark:""}}, DeviceCommand:{title:"DeviceCommand 控制指令",fields:[F("deviceId","String","否","设备ID"),F("commandType","String","否","指令类型(switch/set/query...)"),F("params","Map","否","指令参数(如{\"power\":\"on\"}/{\"bright_value\":80})"),F("timeout","Long","否","超时毫秒(默认30000)")],example:{deviceId:"DEV_001",commandType:"switch",params:{power:"on"},timeout:30000}}, DeviceConnectRequest:{title:"DeviceConnectRequest 设备连接",fields:[F("deviceId","String","否","设备ID"),F("platform","String","否","平台(tuya/miot/...)"),F("credentials","Credentials","否","认证凭证(见下)")],nested:[["credentials.accessToken","String","否","访问令牌(如Tuya accessToken)"],["credentials.username","String","否","用户名"],["credentials.password","String","否","密码"],["credentials.clientId","String","否","客户端ID"],["credentials.extra","Map","否","其它扩展属性"]],example:{deviceId:"DEV_001",platform:"tuya",credentials:{accessToken:"tok_xxxx",username:"",password:"",clientId:"",extra:{}}}}, DeviceActivityReport:{title:"DeviceActivityReport 控制动态上报",fields:[F("action","String","否","动作(switch_on/switch_off/adjust/status_change)"),F("actionDesc","String","否","人类可读描述(可空,后端生成)"),F("detail","Map","否","明细(下发的DP,如{\"1\":true})"),F("commandId","String","否","控制回执配对ID(可空)")],example:{action:"switch_on",actionDesc:"开启客厅灯",detail:{"1":true},commandId:"cmd_1700000000"}}, DeviceGroup:{title:"DeviceGroup 设备分组实体",entity:true,fields:[F("groupName","String","否","分组名称"),F("groupCode","String","否","分组编码"),F("parentId","Long","否","父分组ID(0=根节点)"),F("description","String","否","描述"),F("sort","Integer","否","排序"),F("status","Integer","否","状态(0停用/1正常)")],example:{groupName:"卧室",groupCode:"bedroom",parentId:0,description:"",sort:0,status:1}}, Product:{title:"Product 产品实体",entity:true,fields:[F("productKey","String","否","产品业务编码"),F("name","String","否","产品名称"),F("category","String","否","品类(light/switch/ac/security...)"),F("manufacturer","String","否","厂商"),F("model","String","否","型号"),F("platform","String","否","接入平台(tuya/local/matter)"),F("platformProductId","String","否","平台产品ID(涂鸦productId)"),F("connectType","String","否","联网方式(wifi/ble/zigbee/matter)"),F("netConfigType","String","否","配网方式"),F("icon","String","否","图标"),F("description","String","否","说明"),F("status","String","否","状态(draft/published)"),F("specVersion","Integer","否","物模型版本")],example:{productKey:"PK_LIGHT_001",name:"智能灯",category:"light",manufacturer:"Tuya",model:"L1",platform:"tuya",platformProductId:"abc123",connectType:"wifi",netConfigType:"ap",icon:"https://cdn/x/light.png",description:"支持开关/亮度/色温",status:"published",specVersion:1}}, ProductSpec:{title:"ProductSpec 物模型(TSL)",fields:[F("productId","Long","否","产品ID"),F("properties","List","否","属性列表"),F("events","List","否","事件列表"),F("actions","List","否","服务列表")],example:{productId:10,properties:[{code:"switch_1",name:"开关",access:"write",type:"bool",dpId:"1"}],events:[],actions:[]}}, ProductProperty:{title:"ProductProperty 物模型-属性",entity:true,fields:[F("productId","Long","否","产品ID"),F("code","String","否","功能点编码(如switch_1)"),F("name","String","否","名称"),F("access","String","否","访问权限(read/write/notify)"),F("type","String","否","数据类型(bool/int/enum/float/string/json)"),F("min","String","否","最小值"),F("max","String","否","最大值"),F("step","String","否","步长"),F("unit","String","否","单位"),F("enumList","String","否","枚举值(JSON数组)"),F("dpId","String","否","平台DP点映射(涂鸦DP)"),F("defaultValue","String","否","默认值"),F("sort","Integer","否","排序")],example:{productId:10,code:"switch_1",name:"开关",access:"write",type:"bool",min:"0",max:"1",step:"1",unit:"",enumList:"",dpId:"1",defaultValue:"false",sort:1}}, ProductEvent:{title:"ProductEvent 物模型-事件",entity:true,fields:[F("productId","Long","否","产品ID"),F("code","String","否","事件编码"),F("name","String","否","名称"),F("type","String","否","类型(info/alert/fault)"),F("params","String","否","事件参数(JSON)")],example:{productId:10,code:"power_report",name:"电量上报",type:"info",params:"{}"}}, ProductAction:{title:"ProductAction 物模型-服务",entity:true,fields:[F("productId","Long","否","产品ID"),F("code","String","否","服务编码"),F("name","String","否","名称"),F("input","String","否","输入参数(JSON)"),F("output","String","否","输出参数(JSON)")],example:{productId:10,code:"restart",name:"重启",input:"{}",output:"{}"}}, Firmware:{title:"Firmware 固件实体",entity:true,fields:[F("productId","Long","否","产品ID"),F("version","String","否","固件版本"),F("fileUrl","String","否","升级包地址"),F("fileMd5","String","否","校验MD5"),F("size","Long","否","大小(字节)"),F("releaseNote","String","否","更新说明"),F("isLatest","Integer","否","是否最新(0/1)"),F("status","String","否","状态(draft/published)")],example:{productId:10,version:"1.2.0",fileUrl:"https://cdn/x/fw_1.2.0.bin",fileMd5:"a1b2c3d4",size:123456,releaseNote:"修复已知问题",isLatest:0,status:"draft"}}, OtaTaskCreateRequest:{title:"OtaTaskCreateRequest 创建OTA任务",fields:[F("firmwareId","Long","是","目标固件ID"),F("scope","String","否","范围(all/designated/gray)"),F("deviceIds","List","否","指定设备ID(scope=designated必填)"),F("grayPercent","Integer","否","灰度百分比(scope=gray时1-99)")],example:{firmwareId:5,scope:"designated",deviceIds:[101,102],grayPercent:10}}, OtaReportRequest:{title:"OtaReportRequest OTA结果上报",fields:[F("deviceId","String","是","设备ID"),F("success","Boolean","是","是否升级成功"),F("version","String","否","升级后版本(成功时上报)")],example:{deviceId:"DEV_001",success:true,version:"1.2.0"}}, HomeRequest:{title:"HomeRequest 家庭(增/改)",fields:[F("name","String","是","家庭名称(≤30)"),F("coverImage","String","否","封面图URL"),F("address","String","否","地址(≤100)")],example:{name:"我的家",coverImage:"https://cdn/x/home.png",address:"上海市浦东新区"}}, JoinHomeRequest:{title:"JoinHomeRequest 加入家庭",fields:[F("shareCode","String","是","分享码(6-12)")],example:{shareCode:"ABC123"}}, MemberRoleRequest:{title:"MemberRoleRequest 修改成员角色",fields:[F("role","String","是","目标角色(admin/member)")],example:{role:"admin"}}, RoomRequest:{title:"RoomRequest 房间(增/改)",fields:[F("name","String","是","房间名称")],example:{name:"主卧"}}, TuyaBindRequest:{title:"TuyaBindRequest 绑定涂鸦设备",fields:[F("homeId","Long","是","我们的家庭ID"),F("roomId","Long","否","房间ID"),F("deviceIds","List","否","涂鸦设备ID列表(旧版兼容)"),F("devices","List","否","设备详情(推荐)"),F("fullSync","Boolean","否","全量同步(默认false)")],nested:[["devices.deviceId","String","否","涂鸦设备ID"],["devices.deviceName","String","否","设备名称"],["devices.category","String","否","设备分类"],["devices.online","Boolean","否","是否在线"],["devices.productId","String","否","产品ID"],["devices.icon","String","否","图标URL"]],example:{homeId:1,roomId:2,deviceIds:["tdev1"],devices:[{deviceId:"tdev1",deviceName:"灯",category:"light",online:true,productId:"pid",icon:""}],fullSync:false}}, TuyaNativeBindRequest:{title:"TuyaNativeBindRequest 原生SDK绑定",fields:[F("tuyaUid","String","是","涂鸦用户UID"),F("account","String","是","登录账号(手机号/邮箱)"),F("loginMethod","String","否","登录方式(phone/email,默认phone)"),F("platform","String","否","平台(android/ios/h5,默认android)")],example:{tuyaUid:"uid123",account:"13800138000",loginMethod:"phone",platform:"android"}}, TuyaRename:{title:"TuyaRename 重命名涂鸦设备",fields:[F("deviceName","String","是","新设备名称")],example:{deviceName:"客厅主灯"}}, TuyaCommand:{title:"TuyaCommand 涂鸦指令数组",fields:[F("code","String","是","指令码(如switch_1/bright_value)"),F("value","Object","是","指令值(true/80/\"white\"等)")],example:[{code:"switch_1",value:true},{code:"bright_value",value:80}]}, DeviceStatusSyncRequest:{title:"DeviceStatusSyncRequest 状态同步",fields:[F("deviceId","String","否","涂鸦设备ID"),F("online","Boolean","否","是否在线"),F("dps","Map","否","DP状态(key=dpId,value=dp值)"),F("source","String","否","上报来源(panel/app)")],example:{deviceId:"tdev1",online:true,dps:{"1":true,"2":80},source:"app"}}, DeviceDataPoint:{title:"DeviceDataPoint 数据点(数组元素)",fields:[F("key","String","是","数据点key"),F("type","String","否","类型(number/string/boolean/...)"),F("value","Object","是","数据值"),F("timestamp","Long","否","时间戳(毫秒)"),F("quality","String","否","数据质量(good/bad)")],example:{key:"temperature",type:"number",value:26.5,timestamp:1697000000000,quality:"good"}}, RuleEntity:{title:"RuleEntity 规则实体",entity:true,fields:[F("sceneId","Long","否","所属场景ID(空=独立规则)"),F("ruleName","String","否","规则名称"),F("ruleCode","String","否","规则编码"),F("description","String","否","规则描述"),F("triggerDeviceId","String","否","触发设备ID"),F("conditionExpression","String","否","条件表达式"),F("actions","JsonNode","否","执行动作(JSON)"),F("enabled","Boolean","否","是否启用"),F("priority","Integer","否","优先级"),F("ownerId","Long","否","归属用户ID(member.id)")],example:{sceneId:null,ruleName:"温度告警",ruleCode:"R001",description:"",triggerDeviceId:"DEV_001",conditionExpression:"temp>30",actions:{type:"notify",content:"高温"},enabled:true,priority:1,ownerId:1001}}, SceneEntity:{title:"SceneEntity 场景实体",entity:true,fields:[F("sceneName","String","否","场景名称"),F("sceneCode","String","否","场景编码"),F("sceneType","String","否","类型(one_click/automation)"),F("cronExpression","String","否","定时cron(六段式,可空)"),F("conditions","String","否","触发条件(JSON数组)"),F("actions","String","否","执行动作(JSON数组)"),F("conditionLogic","String","否","多条件逻辑(AND/OR)"),F("icon","String","否","图标URL"),F("description","String","否","描述"),F("enabled","Boolean","否","是否启用"),F("sortOrder","Integer","否","排序"),F("ownerId","Long","否","归属用户ID(member.id)")],example:{sceneName:"回家模式",sceneCode:"S001",sceneType:"one_click",cronExpression:"",conditions:"[]",actions:"[{\"type\":\"set_property\"}]",conditionLogic:"AND",icon:"",description:"",enabled:true,sortOrder:0,ownerId:1001}}, SceneCreateRequest:{title:"SceneCreateRequest 结构化场景(增/改)",fields:[F("sceneName","String","否","场景名称"),F("ownerId","Long","否","归属用户ID(网关注入)"),F("sceneType","String","否","类型(one_click/automation,可空)"),F("icon","String","否","图标"),F("description","String","否","描述"),F("conditionLogic","String","否","多条件逻辑(AND/OR,默认AND)"),F("conditions","List","否","触发条件列表"),F("actions","List","否","执行动作列表")],example:{sceneName:"回家模式",ownerId:1001,sceneType:"one_click",icon:"",description:"",conditionLogic:"AND",conditions:[{type:"device_state",deviceId:"DEV_001",propertyCode:"switch_1",operator:">",value:true}],actions:[{type:"set_property",deviceId:"DEV_002",propertyCode:"switch_1",value:true},{type:"delay",seconds:3},{type:"notify",channel:"push",content:"已开启"}]}}, SceneActionDTO:{title:"SceneActionDTO 场景动作",fields:[F("type","String","是","动作类型(set_property/delay/execute_scene/notify)"),F("deviceId","String","否","目标设备ID(set_property用)"),F("propertyCode","String","否","属性code(set_property用)"),F("value","Object","否","属性值(set_property用)"),F("seconds","Integer","否","延时秒数(delay用)"),F("sceneId","Long","否","目标场景ID(execute_scene用)"),F("channel","String","否","通知渠道(notify用:app/sms/push)"),F("content","String","否","通知内容(notify用)")],example:{type:"set_property",deviceId:"DEV_002",propertyCode:"switch_1",value:true,seconds:3,sceneId:1,channel:"push",content:"已开启"}}, SceneConditionDTO:{title:"SceneConditionDTO 场景条件",fields:[F("type","String","是","条件类型(device_state/timer/manual)"),F("deviceId","String","否","设备ID(device_state必填)"),F("propertyCode","String","否","属性code(平台无关)"),F("dpId","String","否","平台DP标识(兼容dp-id)"),F("operator","String","否","运算符(==,!=,>,<,>=,<=)"),F("value","Object","否","比较值(boolean/number/string)"),F("time","String","否","定时时间HH:mm(timer用)"),F("weekDays","List","否","定时重复星期1-7(timer用)")],example:{type:"device_state",deviceId:"DEV_001",propertyCode:"switch_1",dpId:"1",operator:">",value:true,time:"08:00",weekDays:[1,2,3]}}, AlarmRuleEntity:{title:"AlarmRuleEntity 告警规则实体",entity:true,fields:[F("ruleName","String","否","规则名称"),F("ruleCode","String","否","规则编码"),F("deviceId","String","否","设备ID(空=所有设备)"),F("dataPoint","String","否","数据点"),F("conditionExpression","String","否","条件表达式"),F("alarmLevel","String","否","告警级别"),F("alarmTemplate","String","否","告警消息模板"),F("enabled","Boolean","否","是否启用"),F("deduplicationWindow","Integer","否","去重窗口(分钟)"),F("notificationMethods","String","否","通知方式(email/sms/websocket/push)"),F("ownerId","Long","否","归属用户ID(member.id)")],example:{ruleName:"高温告警",ruleCode:"A001",deviceId:"DEV_001",dataPoint:"temperature",conditionExpression:"value>30",alarmLevel:"high",alarmTemplate:"设备{deviceName}温度过高",enabled:true,deduplicationWindow:30,notificationMethods:"push,email",ownerId:1001}}, AiCommandRequest:{title:"AiCommandRequest 自然语言指令",fields:[F("text","String","是","指令文本(如:打开客厅的灯)"),F("targetType","String","否","二次确认目标类型(device/scene)"),F("targetId","String","否","二次确认目标ID"),F("learn","Boolean","否","是否学习为别名(默认true)")],example:{text:"打开客厅的灯",targetType:"device",targetId:"DEV_001",learn:true}}, AiAliasRequest:{title:"AiAliasRequest 别名维护",fields:[F("alias","String","是","用户说法(如:客厅开关)"),F("targetType","String","是","目标类型(device/scene/room)"),F("targetId","String","是","目标ID(device.device_id/scene.id/room.id)"),F("homeId","Long","否","归属家庭ID(可选)")],example:{alias:"客厅灯",targetType:"device",targetId:"DEV_001",homeId:1}}, AiDeviceProfileRequest:{title:"AiDeviceProfileRequest 指令码校准",fields:[F("powerCode","String","否","开/关指令码(如switch_1/switch_led/control)"),F("powerOnValue","String","否","开启值(true/1)"),F("powerOffValue","String","否","关闭值(false/0)"),F("brightnessCode","String","否","亮度指令码(如bright_value)"),F("colorTempCode","String","否","色温指令码(如temp_value)"),F("temperatureCode","String","否","温度设定码(如temp_set)"),F("modeCode","String","否","模式指令码(如mode)")],example:{powerCode:"switch_1",powerOnValue:"true",powerOffValue:"false",brightnessCode:"bright_value",colorTempCode:"temp_value",temperatureCode:"temp_set",modeCode:"mode"}}, UserQuery:{title:"UserQuery 用户查询参数",query:true,fields:[F("username","String","否","用户名"),F("realName","String","否","真实姓名"),F("roleId","Long","否","角色ID"),F("status","Integer","否","状态(0/1)"),F("pageNum","Integer","否","页码(默认1)"),F("pageSize","Integer","否","每页条数(默认20)")],sample:"pageNum=1&pageSize=20&status=1"}, RoleQuery:{title:"RoleQuery 角色查询参数",query:true,fields:[F("roleName","String","否","角色名称(模糊)"),F("roleCode","String","否","角色编码"),F("status","Integer","否","状态(0/1)"),F("pageNum","Integer","否","页码(默认1)"),F("pageSize","Integer","否","每页条数(默认20)")],sample:"pageNum=1&pageSize=20"}, MemberQuery:{title:"MemberQuery 会员查询参数",query:true,fields:[F("keyword","String","否","用户名/昵称(模糊)"),F("phone","String","否","手机号"),F("status","Integer","否","状态(0/1)"),F("pageNum","Integer","否","页码(默认1)"),F("pageSize","Integer","否","每页条数(默认20)")],sample:"pageNum=1&pageSize=20&keyword=小"}, LogQuery:{title:"LogQuery 日志查询参数",query:true,fields:[F("username","String","否","操作人(模糊)"),F("module","String","否","模块"),F("operation","String","否","操作类型"),F("status","Integer","否","状态(0失败/1成功)"),F("startTime","LocalDateTime","否","开始时间"),F("endTime","LocalDateTime","否","结束时间"),F("pageNum","Integer","否","页码(默认1)"),F("pageSize","Integer","否","每页条数(默认20)")],sample:"pageNum=1&pageSize=20&module=device"}, DeviceQuery:{title:"DeviceQuery 设备查询参数",query:true,fields:[F("deviceName","String","否","设备名称(模糊)"),F("platform","String","否","平台(tuya/miot/...)"),F("deviceType","String","否","设备类型"),F("status","String","否","状态(online/offline/error)"),F("ownerId","Long","否","归属用户ID"),F("homeId","Long","否","家庭ID"),F("roomId","Long","否","房间ID"),F("groupId","Long","否","分组ID"),F("deviceId","String","否","设备ID(模糊)"),F("parentDeviceId","String","否","父网关ID"),F("isGateway","Integer","否","是否网关(1)"),F("pageNum","Integer","否","页码(默认1)"),F("pageSize","Integer","否","每页条数(默认20)")],sample:"pageNum=1&pageSize=20&platform=tuya"}, ProductQuery:{title:"ProductQuery 产品查询参数",query:true,fields:[F("page","Integer","否","页码(默认1)"),F("size","Integer","否","每页条数(默认20)"),F("keyword","String","否","名称/编码(模糊)"),F("category","String","否","品类"),F("platform","String","否","平台")],sample:"page=1&size=20&category=light"}, SceneQuery:{title:"SceneQuery 场景查询参数",query:true,fields:[F("pageNum","Integer","否","页码(默认1)"),F("pageSize","Integer","否","每页条数(默认20)")],sample:"pageNum=1&pageSize=20"}, GroupQuery:{title:"GroupQuery 分组查询参数",query:true,fields:[F("groupName","String","否","分组名称(模糊)"),F("status","Integer","否","状态(0/1)"),F("pageNum","Integer","否","页码(默认1)"),F("pageSize","Integer","否","每页条数(默认20)")],sample:"pageNum=1&pageSize=20"}, AlarmQuery:{title:"AlarmQuery 告警查询参数",query:true,fields:[F("deviceId","String","否","设备ID"),F("alarmLevel","String","否","告警级别"),F("status","String","否","状态(pending/confirmed/resolved)"),F("ownerId","Long","否","归属用户ID"),F("startTime","LocalDateTime","否","开始时间"),F("endTime","LocalDateTime","否","结束时间"),F("pageNum","Integer","否","页码(默认1)"),F("pageSize","Integer","否","每页条数(默认20)")],sample:"pageNum=1&pageSize=20&status=pending"}, RuleQuery:{title:"RuleQuery 规则查询参数",query:true,fields:[F("ruleName","String","否","规则名称(模糊)"),F("enabled","Boolean","否","是否启用"),F("triggerDeviceId","String","否","触发设备ID"),F("ownerId","Long","否","归属用户ID"),F("sceneId","Long","否","所属场景ID"),F("pageNum","Integer","否","页码(默认1)"),F("pageSize","Integer","否","每页条数(默认20)")],sample:"pageNum=1&pageSize=20"}, DataHistoryQuery:{title:"DataHistoryQuery 历史数据查询参数",query:true,fields:[F("pointKey","String","否","数据点key(不填查全部)"),F("startTime","Long","否","开始时间戳(毫秒)"),F("endTime","Long","否","结束时间戳(毫秒)"),F("pageNum","Integer","否","页码(默认1)"),F("pageSize","Integer","否","每页条数(默认20)")],sample:"pointKey=temperature&startTime=1696900000000&endTime=1697000000000"}, DataStatsQuery:{title:"DataStatsQuery 聚合统计参数",query:true,fields:[F("field","String","是","统计字段(数据点key)"),F("aggregation","String","是","聚合方式(mean/max/min/sum/count)"),F("startTime","Long","否","开始时间戳(毫秒)"),F("endTime","Long","否","结束时间(毫秒)")],sample:"field=temperature&aggregation=max&startTime=1696900000000&endTime=1697000000000"}, LoginResponse:{title:"LoginResponse 登录响应 data",resp:true,fields:[F("token","String","-","JWT令牌"),F("username","String","-","用户名"),F("roles","List","-","角色编码列表"),F("expiresIn","Long","-","有效期(秒)")],example:{token:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",username:"admin",roles:["ADMIN"],expiresIn:86400}}, PageResult:{title:"PageResult 分页响应 data",resp:true,fields:[F("records","List","-","当前页数据列表(元素为对应entity)"),F("total","Long","-","总条数"),F("pages","Long","-","总页数"),F("current","Long","-","当前页"),F("size","Long","-","每页条数")],example:{records:[{id:1,deviceId:"DEV_001",deviceName:"客厅灯",platform:"tuya",status:"online"}],total:9,pages:1,current:1,size:20}}, /* ===== 中控屏(Panel) 数据模型 ===== */ PanelSession:{title:"PanelSession 屏会话(响应)",resp:true,fields:[F("panelId","Long","-","屏ID"),F("homeId","Long","否","归属家庭(后台预置即绑定场景)"),F("role","String","-","角色(GATEWAY/DISPLAY)"),F("accessToken","String","-","屏访问令牌"),F("refreshToken","String","-","刷新令牌(轮换用)"),F("expiresIn","Long","-","有效期(秒)")],example:{panelId:1,homeId:1,role:"GATEWAY",accessToken:"eyJ...",refreshToken:"r-...",expiresIn:86400}}, PanelSessionReq:{title:"PanelSessionReq 建会话请求",fields:[F("bootstrapSecret","String","是","管理端预置的一次性初始化凭证")],example:{bootstrapSecret:"bs-xxxx"}}, PanelSessionRefreshReq:{title:"PanelSessionRefreshReq 刷新请求",fields:[F("refreshToken","String","是","刷新令牌")],example:{refreshToken:"r-xxxx"}}, PanelSessionRefreshResp:{title:"PanelSessionRefreshResp 刷新响应",resp:true,fields:[F("accessToken","String","-","新访问令牌"),F("refreshToken","String","-","新刷新令牌"),F("expiresIn","Long","-","有效期(秒)")],example:{accessToken:"eyJ...",refreshToken:"r-...",expiresIn:86400}}, PanelHeartbeat:{title:"PanelHeartbeat 心跳体",fields:[F("online","Boolean","否","是否在线(默认true)"),F("uptime","Long","否","运行时长(秒)"),F("freeMem","Long","否","空闲内存(字节)"),F("msg","String","否","附加信息")],example:{online:true,uptime:3600,freeMem:524288000}}, PanelHeartbeatResp:{title:"PanelHeartbeatResp 心跳响应",resp:true,fields:[F("ok","Boolean","-","续约成功")],example:{ok:true}}, PanelInfo:{title:"PanelInfo 屏信息",resp:true,fields:[F("panelId","Long","-","屏ID"),F("homeId","Long","否","归属家庭"),F("role","String","-","角色"),F("enabled","Boolean","-","是否启用"),F("online","Boolean","-","是否在线"),F("lastSeenAt","Long","否","最后心跳时间"),F("features","List","否","能力列表")],example:{panelId:1,homeId:1,role:"GATEWAY",enabled:true,online:true,lastSeenAt:1697000000000,features:["device-control","scene"]}}, PanelSnapshotQuery:{title:"PanelSnapshotQuery 快照分页",query:true,fields:[F("pageToken","String","否","游标(首屏为空)"),F("limit","Integer","否","每页条数(默认200)")],sample:"pageToken=&limit=200"}, PanelSnapshot:{title:"PanelSnapshot 家庭快照",resp:true,fields:[F("home","Object","-","家庭对象{homeId,name}"),F("rooms","List","-","房间列表"),F("devices","List","-","设备列表(首屏全量)"),F("stateEpoch","Long","-","一致性游标"),F("pageToken","String","否","下一页游标"),F("hasMore","Boolean","-","是否还有更多")],example:{home:{homeId:1,name:"我的家"},rooms:[{roomId:1,name:"客厅"}],devices:[{deviceId:"DEV_001",deviceName:"客厅灯"}],stateEpoch:1700000000000,pageToken:"",hasMore:false}}, PanelConfig:{title:"PanelConfig 屏配置",resp:true,fields:[F("configJson","String","-","运营配置(JSON)"),F("version","Integer","-","配置版本"),F("checksum","String","-","校验和(屏端比对是否变更)")],example:{configJson:"{...}",version:3,checksum:"a1b2c3"}}, PanelDeviceRegister:{title:"PanelDeviceRegister 设备登记",fields:[F("deviceId","String","是","设备唯一标识"),F("deviceName","String","否","设备名称"),F("platform","String","否","平台(tuya/miot/local/matter)"),F("productId","Long","否","产品ID"),F("category","String","否","品类"),F("schema","Object","否","标准控制模型")],example:{deviceId:"DEV_001",deviceName:"客厅灯",platform:"tuya",productId:10,category:"light"}}, PanelDeviceRegisterResult:{title:"PanelDeviceRegisterResult 登记响应",resp:true,fields:[F("deviceId","String","-","设备ID"),F("registered","Boolean","-","是否新登记")],example:{deviceId:"DEV_001",registered:true}}, PanelSchemaUpdate:{title:"PanelSchemaUpdate 控制模型更新",fields:[F("schema","Object","是","标准控制模型(属性/事件/动作)")],example:{schema:{switch_1:{type:"bool"}}}}, PanelSchemaUpdateResult:{title:"PanelSchemaUpdateResult",resp:true,fields:[F("accepted","Boolean","-","接受")],example:{accepted:true}}, PanelReportBatch:{title:"PanelReportBatch 状态上报",fields:[F("deviceId","String","是","设备ID"),F("items","List","是","状态项[{key,value,ts}]")],example:{deviceId:"DEV_001",items:[{key:"switch_1",value:true,ts:1697000000000}]}}, PanelReportResult:{title:"PanelReportResult 上报响应",resp:true,fields:[F("accepted","Integer","-","已接受条数"),F("rejected","Integer","-","被拒条数")],example:{accepted:1,rejected:0}}, PanelCmdPullReq:{title:"PanelCmdPullReq 命令拉取",fields:[F("cursor","Long","否","已处理到的最大命令ID"),F("limit","Integer","否","拉取条数(默认20)")],example:{cursor:100,limit:20}}, PanelCmdPullResp:{title:"PanelCmdPullResp 命令拉取响应",resp:true,fields:[F("commands","List","-","待执行命令[{commandId,type,params}]")],example:{commands:[{commandId:101,type:"set_property",params:{switch_1:true}}]}}, PanelCmdStartReq:{title:"PanelCmdStartReq 申请执行",fields:[F("executorRevision","Long","否","执行端版本号")],example:{executorRevision:5}}, PanelCmdStartResp:{title:"PanelCmdStartResp",resp:true,fields:[F("accepted","Boolean","-","接受")],example:{accepted:true}}, PanelCmdReportReq:{title:"PanelCmdReportReq 命令回报",fields:[F("status","String","是","执行状态(SUCCESS/FAIL)"),F("result","Object","否","结果明细")],example:{status:"SUCCESS",result:{switch_1:true}}}, PanelCmdResult:{title:"PanelCmdResult 命令回报响应",resp:true,fields:[F("recorded","Boolean","-","已记录")],example:{recorded:true}}, PanelDiagnostic:{title:"PanelDiagnostic 诊断上报",fields:[F("type","String","是","类型(network/mqtt/ota/command/config)"),F("detail","Object","否","明细"),F("ts","Long","否","时间戳")],example:{type:"network",detail:{rssi:-50},ts:1697000000000}}, PanelEventQuery:{title:"PanelEventQuery 事件查询",query:true,fields:[F("cursor","Long","否","上次事件ID(默认0)"),F("timeout","Integer","否","最长阻塞毫秒(默认20000)"),F("limit","Integer","否","返回条数(默认50)")],sample:"cursor=0&timeout=20000&limit=50"}, PanelEventsResp:{title:"PanelEventsResp 增量事件",resp:true,fields:[F("events","List","-","事件列表[{id,type,payload}]"),F("nextCursor","Long","-","下一游标")],example:{events:[{id:11,type:"device_state",payload:{deviceId:"DEV_001"}}],nextCursor:11}}, PanelOtaNext:{title:"PanelOtaNext 待升级任务",resp:true,fields:[F("taskId","Long","否","任务ID(无则为null)"),F("version","String","否","目标版本"),F("fileUrl","String","否","升级包地址"),F("size","Long","否","大小(字节)"),F("md5","String","否","校验MD5")],example:{taskId:5,version:"1.2.0",fileUrl:"https://cdn/x.bin",size:123456,md5:"a1b2"}}, PanelSceneRunReport:{title:"PanelSceneRunReport 场景执行结果",fields:[F("sceneId","Long","是","场景ID"),F("runId","String","否","执行批次ID"),F("status","String","是","状态(SUCCESS/FAIL)"),F("detail","Object","否","明细")],example:{sceneId:1,runId:"r-1",status:"SUCCESS"}}, PanelSceneRunView:{title:"PanelSceneRunView",resp:true,fields:[F("runId","String","-","执行批次ID"),F("status","String","-","状态")],example:{runId:"r-1",status:"SUCCESS"}}, PanelBindReq:{title:"PanelBindReq 会员绑定请求",fields:[F("serialNumber","String","是","中控屏序列号(SN)"),F("homeId","Long","是","绑定到的家庭ID"),F("pairingCode","String","否","配对码(可选)")],example:{serialNumber:"SN-2024-0001",homeId:1}}, PanelBindResult:{title:"PanelBindResult 绑定结果",resp:true,fields:[F("panelId","Long","-","屏ID"),F("serialNumber","String","-","序列号"),F("homeId","Long","-","家庭ID"),F("memberId","Long","-","会员ID"),F("role","String","-","角色"),F("boundAt","Long","-","绑定时间")],example:{panelId:1,serialNumber:"SN-2024-0001",homeId:1,memberId:1001,role:"GATEWAY",boundAt:1697000000000}}, PanelBindable:{title:"PanelBindable 可绑定性预检",resp:true,fields:[F("exists","Boolean","-","是否已预置"),F("enabled","Boolean","-","是否启用"),F("bound","Boolean","-","是否已绑定家庭"),F("panelId","Long","否","屏ID(已预置时)")],example:{exists:true,enabled:true,bound:false,panelId:1}}, PanelSummary:{title:"PanelSummary 我的屏",resp:true,fields:[F("panelId","Long","-","屏ID"),F("serialNumber","String","-","序列号"),F("homeId","Long","-","家庭ID"),F("role","String","-","角色"),F("enabled","Boolean","-","是否启用"),F("online","Boolean","-","是否在线"),F("lastSeenAt","Long","否","最后心跳")],example:{panelId:1,serialNumber:"SN-2024-0001",homeId:1,role:"GATEWAY",enabled:true,online:true,lastSeenAt:1697000000000}}, PanelAdminCreate:{title:"PanelAdminCreate 预置屏终端",fields:[F("serialNumber","String","是","序列号(SN)"),F("homeId","Long","否","家庭ID(留空=待绑定库存)"),F("memberId","Long","否","预绑会员(可选)"),F("role","String","否","角色(GATEWAY/DISPLAY,默认GATEWAY)"),F("model","String","否","型号"),F("features","List","否","能力列表")],example:{serialNumber:"SN-2024-0001",homeId:null,role:"GATEWAY"}}, PanelAdminCreateResult:{title:"PanelAdminCreateResult 预置响应",resp:true,fields:[F("panelId","Long","-","屏ID"),F("serialNumber","String","-","序列号"),F("bootstrapSecret","String","-","一次性初始化凭证(仅返回一次)")],example:{panelId:1,serialNumber:"SN-2024-0001",bootstrapSecret:"bs-xxxx"}}, PanelAdminItem:{title:"PanelAdminItem 屏终端列表项",resp:true,fields:[F("panelId","Long","-","屏ID"),F("serialNumber","String","-","序列号"),F("homeId","Long","否","家庭ID"),F("role","String","-","角色"),F("enabled","Boolean","-","是否启用"),F("bound","Boolean","-","是否已绑定"),F("createdAt","Long","-","创建时间")],example:{panelId:1,serialNumber:"SN-2024-0001",homeId:null,role:"GATEWAY",enabled:true,bound:false,createdAt:1697000000000}}, PanelAdminRotateResult:{title:"PanelAdminRotateResult 轮换响应",resp:true,fields:[F("panelId","Long","-","屏ID"),F("bootstrapSecret","String","-","新初始化凭证")],example:{panelId:1,bootstrapSecret:"bs-new"}}, PanelConfigSave:{title:"PanelConfigSave 保存屏配置",fields:[F("configJson","String","是","配置JSON"),F("version","Integer","是","版本(需等于当前版本)")],example:{configJson:"{...}",version:3}}, PanelReassignReq:{title:"PanelReassignReq 多屏归属交接",fields:[F("fromPanelId","Long","是","源屏ID"),F("toPanelId","Long","是","目标屏ID"),F("routeRevision","Long","是","路由版本(需一致)")],example:{fromPanelId:1,toPanelId:2,routeRevision:7}}, PanelReassignResult:{title:"PanelReassignResult 交接响应",resp:true,fields:[F("routeRevision","Long","-","新路由版本"),F("reassigned","Integer","-","交接设备数")],example:{routeRevision:8,reassigned:3}} }; /* ===== curl 示例模板 ===== */ const EXAMPLES={ login:(b)=>`# 1) 登录拿 Token curl -X POST ${b}/auth/login \\ -H 'Content-Type: application/json' \\ -d '{"username":"admin","password":"admin123"}' # 返回 data.token,后续请求带 Header: Authorization: Bearer <TOKEN>`, devlist:(b)=>`# 2) 设备列表(带 Token) curl ${b}/devices \\ -H 'Authorization: Bearer <TOKEN>'`, cmd:(b)=>`# 3) 发送设备控制指令(开关 / 设置) curl -X POST ${b}/devices/{deviceId}/commands \\ -H 'Authorization: Bearer <TOKEN>' \\ -H 'Content-Type: application/json' \\ -d '{"deviceId":"{deviceId}","commandType":"switch","params":{"power":"on"}}'`, ingest:(b)=>`# 4) 设备上报时序数据(iot-data :9082) curl -X POST ${b}/data/devices/{deviceId}/data \\ -H 'Content-Type: application/json' \\ -d '[{"key":"temperature","type":"number","value":26.5,"timestamp":1697000000000,"quality":"good"}]'`, scene:(b)=>`# 5) 创建结构化场景(一键执行 + 延时 + 通知) curl -X POST ${b}/scenes/v2 \\ -H 'Authorization: Bearer <TOKEN>' \\ -H 'Content-Type: application/json' \\ -d '{ "sceneName":"回家模式", "sceneType":"one_click", "conditionLogic":"AND", "conditions":[], "actions":[ {"type":"set_property","deviceId":"DEV001","propertyCode":"switch_1","value":true}, {"type":"delay","seconds":3}, {"type":"notify","channel":"push","content":"已开启回家模式"} ] }'`, rulelist:(b)=>`# 6) 规则列表(按会员隔离,传 Token 看自己的) curl ${b}/rules \\ -H 'Authorization: Bearer <TOKEN>'` }; /* ===== 接口 → 模型 映射 ===== */ const EPMAP={ "POST /api/v1/auth/login":{body:"LoginRequest",resp:"LoginResponse"}, "POST /api/v1/auth/register":{body:"RegisterRequest"}, "POST /api/v1/auth/member/login":{body:"LoginRequest"}, "POST /api/v1/auth/member/refresh":{body:"RefreshRequest"}, "POST /api/v1/auth/member/register":{body:"MemberRegisterRequest"}, "PUT /api/v1/auth/member/password":{body:"PasswordChange"}, "GET /api/v1/users":{query:"UserQuery"}, "POST /api/v1/users":{body:"UserRequest"}, "PUT /api/v1/users/{id}":{body:"UserRequest"}, "PUT /api/v1/users/{id}/password":{body:"PasswordChange"}, "GET /api/v1/roles":{query:"RoleQuery"}, "POST /api/v1/roles":{body:"RoleRequest"}, "PUT /api/v1/roles/{id}":{body:"RoleRequest"}, "GET /api/v1/members":{query:"MemberQuery"}, "POST /api/v1/members":{body:"MemberRequest"}, "PUT /api/v1/members/{id}":{body:"MemberRequest"}, "GET /api/v1/logs":{query:"LogQuery"}, "PUT /api/v1/admin/platforms/{platformCode}":{body:"PlatformConfigRequest"}, "GET /api/v1/devices":{query:"DeviceQuery"}, "POST /api/v1/devices":{body:"Device",entity:true}, "PUT /api/v1/devices/{id}":{body:"Device",entity:true}, "POST /api/v1/devices/{deviceId}/connect":{body:"DeviceConnectRequest"}, "POST /api/v1/devices/{deviceId}/commands":{body:"DeviceCommand"}, "POST /api/v1/devices/activities/{deviceId}/activity":{body:"DeviceActivityReport"}, "GET /api/v1/products":{query:"ProductQuery"}, "POST /api/v1/products":{body:"Product",entity:true}, "PUT /api/v1/products/{id}":{body:"Product",entity:true}, "PUT /api/v1/products/{id}/spec":{body:"ProductSpec"}, "POST /api/v1/products/{id}/properties":{body:"ProductProperty",entity:true}, "POST /api/v1/products/{id}/events":{body:"ProductEvent",entity:true}, "POST /api/v1/products/{id}/actions":{body:"ProductAction",entity:true}, "POST /api/v1/products/{id}/firmware":{body:"Firmware",entity:true}, "POST /api/v1/products/{id}/ota":{body:"OtaTaskCreateRequest"}, "POST /api/v1/ota-tasks/{id}/report":{body:"OtaReportRequest"}, "GET /api/v1/device-groups":{query:"GroupQuery"}, "POST /api/v1/device-groups":{body:"DeviceGroup",entity:true}, "PUT /api/v1/device-groups/{id}":{body:"DeviceGroup",entity:true}, "POST /api/v1/homes":{body:"HomeRequest"}, "PUT /api/v1/homes/{id}":{body:"HomeRequest"}, "POST /api/v1/homes/join":{body:"JoinHomeRequest"}, "PUT /api/v1/homes/{id}/members/{memberId}/role":{body:"MemberRoleRequest"}, "POST /api/v1/homes/{homeId}/rooms":{body:"RoomRequest"}, "PUT /api/v1/homes/{homeId}/rooms/{roomId}":{body:"RoomRequest"}, "POST /api/v1/tuya/devices/bind":{body:"TuyaBindRequest"}, "POST /api/v1/tuya/devices/status":{body:"DeviceStatusSyncRequest"}, "POST /api/v1/tuya/auth/bind-native":{body:"TuyaNativeBindRequest"}, "POST /api/v1/tuya/auth/sms/send":{body:"SmsSendRequest"}, "POST /api/v1/tuya/auth/sms/bind":{body:"SmsBindRequest"}, "POST /api/v1/tuya/auth/bind-token":{body:"BindTokenRequest"}, "POST /api/v1/tuya/auth/callback":{body:"OauthCallback"}, "POST /api/v1/tuya/devices/{deviceId}/commands":{body:"TuyaCommand"}, "POST /api/v1/tuya/devices/{deviceId}/rename":{body:"TuyaRename"}, "POST /api/v1/data/devices/{deviceId}/data":{body:"DeviceDataPoint",isArray:true}, "GET /api/v1/data/devices/{deviceId}/history":{query:"DataHistoryQuery"}, "GET /api/v1/data/devices/{deviceId}/stats":{query:"DataStatsQuery"}, "POST /api/v1/rules":{body:"RuleEntity",entity:true}, "PUT /api/v1/rules/{ruleId}":{body:"RuleEntity",entity:true}, "GET /api/v1/rules":{query:"RuleQuery"}, "GET /api/v1/scenes":{query:"SceneQuery"}, "POST /api/v1/scenes":{body:"SceneEntity",entity:true}, "PUT /api/v1/scenes/{sceneId}":{body:"SceneEntity",entity:true}, "POST /api/v1/scenes/v2":{body:"SceneCreateRequest"}, "PUT /api/v1/scenes/{sceneId}/v2":{body:"SceneCreateRequest"}, "POST /api/v1/alarm-rules":{body:"AlarmRuleEntity",entity:true}, "PUT /api/v1/alarm-rules/{ruleId}":{body:"AlarmRuleEntity",entity:true}, "GET /api/v1/alarms":{query:"AlarmQuery"}, "POST /api/v1/ai/command":{body:"AiCommandRequest"}, "POST /api/v1/ai/aliases":{body:"AiAliasRequest"}, "PUT /api/v1/ai/devices/{deviceId}/profile":{body:"AiDeviceProfileRequest"}, /* —— 中控屏(Panel) —— */ "POST /api/v1/panels/session":{body:"PanelSessionReq",resp:"PanelSession"}, "POST /api/v1/panels/session/refresh":{body:"PanelSessionRefreshReq",resp:"PanelSessionRefreshResp"}, "POST /api/v1/panels/{panelId}/heartbeat":{body:"PanelHeartbeat",resp:"PanelHeartbeatResp"}, "GET /api/v1/panels/{panelId}":{resp:"PanelInfo"}, "GET /api/v1/panels/{panelId}/snapshot":{query:"PanelSnapshotQuery",resp:"PanelSnapshot"}, "GET /api/v1/panels/{panelId}/configuration":{resp:"PanelConfig"}, "POST /api/v1/panels/{panelId}/devices/register":{body:"PanelDeviceRegister",resp:"PanelDeviceRegisterResult"}, "PUT /api/v1/panels/{panelId}/devices/{deviceId}/schema":{body:"PanelSchemaUpdate",resp:"PanelSchemaUpdateResult"}, "POST /api/v1/panels/{panelId}/reports":{body:"PanelReportBatch",resp:"PanelReportResult"}, "POST /api/v1/panels/{panelId}/commands/pull":{body:"PanelCmdPullReq",resp:"PanelCmdPullResp"}, "POST /api/v1/panels/{panelId}/commands/{commandId}/start":{body:"PanelCmdStartReq",resp:"PanelCmdStartResp"}, "POST /api/v1/panels/{panelId}/commands/{commandId}/report":{body:"PanelCmdReportReq",resp:"PanelCmdResult"}, "POST /api/v1/panels/{panelId}/diagnostics":{body:"PanelDiagnostic"}, "GET /api/v1/panels/{panelId}/events":{query:"PanelEventQuery",resp:"PanelEventsResp"}, "GET /api/v1/panels/{panelId}/ota-tasks/next":{resp:"PanelOtaNext"}, "POST /api/v1/panels/{panelId}/scene-runs/report":{body:"PanelSceneRunReport",resp:"PanelSceneRunView"}, "POST /api/v1/member/panels/bind":{body:"PanelBindReq",resp:"PanelBindResult"}, "GET /api/v1/member/panels":{resp:"PanelSummary"}, "GET /api/v1/member/panels/bindable":{resp:"PanelBindable"}, "POST /api/v1/admin/panels":{body:"PanelAdminCreate",resp:"PanelAdminCreateResult"}, "GET /api/v1/admin/panels":{resp:"PanelAdminItem"}, "PUT /api/v1/admin/panels/{panelId}/configuration":{body:"PanelConfigSave",resp:"PanelConfig"}, "GET /api/v1/admin/panels/{panelId}/configuration":{resp:"PanelConfig"}, "POST /api/v1/admin/panels/{panelId}/rotate":{resp:"PanelAdminRotateResult"}, "POST /api/v1/admin/panels/executor-reassign":{body:"PanelReassignReq",resp:"PanelReassignResult"} }; /* ===== 页面与导航 ===== */ const PAGES=[ {id:"index",label:"概览首页",group:"入门"}, {id:"auth",label:"认证与账号",group:"账号体系"}, {id:"member",label:"会员与家庭",group:"账号体系"}, {id:"device",label:"设备管理",group:"设备接入"}, {id:"product",label:"产品与物模型",group:"设备接入"}, {id:"tuya",label:"涂鸦云集成",group:"设备接入"}, {id:"data",label:"数据采集",group:"数据服务"}, {id:"rule",label:"规则与场景",group:"智能联动"}, {id:"ai",label:"AI 智能",group:"智能联动"}, {id:"alarm",label:"告警服务",group:"智能联动"}, {id:"models",label:"数据模型",group:"参考"}, {id:"partner",label:"授权对接",group:"参考"}, {id:"panel",label:"中控屏接口",group:"设备接入"} ]; /* ===== 接口分组(按页面) ===== */ const SECTIONS={}; let _cur=null; function sec(page,title,sub){_cur={title,sub,items:[]};(SECTIONS[page]=SECTIONS[page]||[]).push(_cur);} function ep(m,p,d,a,params,ex){_cur.items.push({m,p,d,a,params,ex:ex||""});} /* —— 认证与账号 auth —— */ sec("auth","认证与登录","后台 / H5 账号登录注册、获取当前用户信息"); ep("POST","/api/v1/auth/login","用户登录,返回 JWT",AUTH.none,"body: LoginRequest","login"); ep("POST","/api/v1/auth/register","注册普通(H5)用户",AUTH.none,"body: RegisterRequest"); ep("GET","/api/v1/auth/info","获取当前登录用户信息",AUTH.req,"Header: Authorization: Bearer "); sec("auth","后台用户管理","用户 CRUD、启用禁用、重置密码、角色分配"); ep("GET","/api/v1/users","后台用户列表(分页)",AUTH.none,"query: UserQuery"); ep("GET","/api/v1/users/{id}","用户详情",AUTH.none,"path: id"); ep("POST","/api/v1/users","创建用户",AUTH.none,"body: UserRequest"); ep("PUT","/api/v1/users/{id}","更新用户",AUTH.none,"path: id; body: UserRequest"); ep("DELETE","/api/v1/users/{id}","删除用户",AUTH.none,"path: id"); ep("PUT","/api/v1/users/{id}/status","启用/禁用用户",AUTH.none,"path: id; query: status(0/1)"); ep("PUT","/api/v1/users/{id}/password","重置用户密码",AUTH.none,"path: id; body: PasswordChange"); ep("GET","/api/v1/users/{id}/roles","用户角色ID列表",AUTH.none,"path: id"); sec("auth","角色与权限","角色 CRUD、角色下拉"); ep("GET","/api/v1/roles","角色列表(分页)",AUTH.none,"query: RoleQuery"); ep("GET","/api/v1/roles/all","全部角色(下拉)",AUTH.none,"-"); ep("GET","/api/v1/roles/{id}","角色详情",AUTH.none,"path: id"); ep("POST","/api/v1/roles","创建角色",AUTH.none,"body: RoleRequest"); ep("PUT","/api/v1/roles/{id}","更新角色",AUTH.none,"path: id; body: RoleRequest"); ep("DELETE","/api/v1/roles/{id}","删除角色",AUTH.none,"path: id"); sec("auth","操作日志","后台操作审计分页"); ep("GET","/api/v1/logs","操作日志分页",AUTH.none,"query: LogQuery"); sec("auth","第三方平台配置","涂鸦 / 大模型等平台凭证配置"); ep("GET","/api/v1/admin/platforms/{platformCode}","获取第三方平台配置",AUTH.none,"path: platformCode(tuya/...)"); ep("PUT","/api/v1/admin/platforms/{platformCode}","更新平台配置",AUTH.none,"path: platformCode; body: PlatformConfigRequest"); ep("POST","/api/v1/admin/platforms/{platformCode}/toggle","启用/禁用平台",AUTH.none,"path: platformCode; query: enabled(bool)"); ep("POST","/api/v1/admin/platforms/{platformCode}/test","测试平台连接",AUTH.none,"path: platformCode"); /* —— 会员与家庭 member —— */ sec("member","会员端账号","移动端会员登录、注册、资料、改密"); ep("POST","/api/v1/auth/member/login","会员(移动端)登录",AUTH.none,"body: LoginRequest"); ep("POST","/api/v1/auth/member/refresh","刷新会员 Token",AUTH.none,"body: RefreshRequest"); ep("POST","/api/v1/auth/member/register","会员注册",AUTH.none,"body: MemberRegisterRequest"); ep("GET","/api/v1/auth/member/info","当前会员信息",AUTH.none,"query: memberId"); ep("PUT","/api/v1/auth/member/profile","更新会员资料",AUTH.none,"query: memberId; form: nickname/avatar/email(可空)"); ep("PUT","/api/v1/auth/member/password","修改会员密码",AUTH.none,"query: memberId; body: PasswordChange"); sec("member","会员管理(后台)","会员 CRUD、启用禁用、重置密码"); ep("GET","/api/v1/members","会员列表(分页)",AUTH.none,"query: MemberQuery"); ep("GET","/api/v1/members/{id}","会员详情",AUTH.none,"path: id"); ep("POST","/api/v1/members","创建会员",AUTH.none,"body: MemberRequest"); ep("PUT","/api/v1/members/{id}","更新会员",AUTH.none,"path: id; body: MemberRequest"); ep("DELETE","/api/v1/members/{id}","删除会员",AUTH.none,"path: id"); ep("PUT","/api/v1/members/{id}/status","启用/禁用会员",AUTH.none,"path: id; query: status(0/1)"); ep("PUT","/api/v1/members/{id}/password","重置会员密码",AUTH.none,"path: id; body: PasswordChange"); sec("member","家庭管理","家庭的创建、成员、分享、加入、离开"); ep("GET","/api/v1/homes","我的家庭列表",AUTH.none,"query: memberId"); ep("GET","/api/v1/homes/{id}","家庭详情",AUTH.none,"path: id; query: memberId"); ep("POST","/api/v1/homes","创建家庭",AUTH.none,"query: memberId; body: HomeRequest"); ep("PUT","/api/v1/homes/{id}","更新家庭",AUTH.none,"path: id; query: memberId; body: HomeRequest"); ep("DELETE","/api/v1/homes/{id}","删除家庭",AUTH.none,"path: id; query: memberId"); ep("POST","/api/v1/homes/{id}/share","生成家庭分享码",AUTH.none,"path: id; query: memberId"); ep("POST","/api/v1/homes/join","加入家庭",AUTH.none,"query: memberId; body: JoinHomeRequest"); ep("GET","/api/v1/homes/{id}/members","家庭成员列表",AUTH.none,"path: id; query: memberId"); ep("PUT","/api/v1/homes/{id}/members/{memberId}/role","修改成员角色",AUTH.none,"path: id,memberId; query: operatorId; body: MemberRoleRequest"); ep("DELETE","/api/v1/homes/{id}/members/{memberId}","移除成员",AUTH.none,"path: id,memberId; query: operatorId"); ep("POST","/api/v1/homes/{id}/leave","离开家庭",AUTH.none,"path: id; query: memberId"); sec("member","房间管理","家庭下房间增删改"); ep("GET","/api/v1/homes/{homeId}/rooms","房间列表",AUTH.none,"path: homeId; query: memberId"); ep("POST","/api/v1/homes/{homeId}/rooms","添加房间",AUTH.none,"path: homeId; query: memberId; body: RoomRequest"); ep("DELETE","/api/v1/homes/{homeId}/rooms/{roomId}","删除房间",AUTH.none,"path: homeId,roomId; query: memberId"); ep("PUT","/api/v1/homes/{homeId}/rooms/{roomId}","重命名房间",AUTH.none,"path: homeId,roomId; query: memberId; body: RoomRequest"); /* —— 设备管理 device —— */ sec("device","设备管理","设备 CRUD、连接/断开、控制指令、控制模型、父网关、操控动态"); ep("GET","/api/v1/devices","获取设备列表",AUTH.opt,"query: DeviceQuery","devlist"); ep("GET","/api/v1/devices/{deviceId}","设备详情",AUTH.none,"path: deviceId"); ep("POST","/api/v1/devices","创建设备",AUTH.none,"body: Device"); ep("PUT","/api/v1/devices/{id}","更新设备",AUTH.none,"path: id; body: Device"); ep("DELETE","/api/v1/devices/{id}","删除设备",AUTH.none,"path: id"); ep("GET","/api/v1/devices/{deviceId}/control-schema","设备标准控制模型",AUTH.none,"path: deviceId"); ep("GET","/api/v1/devices/{deviceId}/data","读取设备数据",AUTH.none,"path: deviceId"); ep("POST","/api/v1/devices/{deviceId}/connect","连接设备",AUTH.none,"path: deviceId; body: DeviceConnectRequest"); ep("POST","/api/v1/devices/{deviceId}/disconnect","断开设备",AUTH.none,"path: deviceId"); ep("POST","/api/v1/devices/{deviceId}/commands","发送控制指令",AUTH.none,"path: deviceId; body: DeviceCommand","cmd"); ep("POST","/api/v1/devices/{deviceId}/parent","绑定/解绑父网关",AUTH.none,"path: deviceId; body: {parentDeviceId} (空=解绑)"); ep("GET","/api/v1/devices/activities/recent","近期操控动态(首页)",AUTH.opt,"query: homeId, limit(默认5)"); ep("GET","/api/v1/devices/activities","动态分页查询",AUTH.opt,"query: homeId, deviceId, pageNum, pageSize"); ep("POST","/api/v1/devices/activities/{deviceId}/activity","上报设备控制动态",AUTH.none,"path: deviceId; body: DeviceActivityReport"); sec("device","设备分组","分组树 / 分页 / CRUD / 设备计数"); ep("GET","/api/v1/device-groups/tree","分组树形列表",AUTH.none,"-"); ep("GET","/api/v1/device-groups","分组分页",AUTH.none,"query: GroupQuery"); ep("POST","/api/v1/device-groups","创建分组",AUTH.none,"body: DeviceGroup"); ep("PUT","/api/v1/device-groups/{id}","更新分组",AUTH.none,"path: id; body: DeviceGroup"); ep("DELETE","/api/v1/device-groups/{id}","删除分组",AUTH.none,"path: id"); ep("GET","/api/v1/device-groups/{id}/devices/count","分组下设备数量",AUTH.none,"path: id"); /* —— 产品与物模型 product —— */ sec("product","产品管理","产品 CRUD、按平台解析、存量归类"); ep("GET","/api/v1/products","产品列表",AUTH.none,"query: ProductQuery"); ep("GET","/api/v1/products/{id}","产品详情",AUTH.none,"path: id"); ep("POST","/api/v1/products","创建产品",AUTH.none,"body: Product"); ep("PUT","/api/v1/products/{id}","更新产品",AUTH.none,"path: id; body: Product"); ep("DELETE","/api/v1/products/{id}","删除产品",AUTH.none,"path: id"); ep("GET","/api/v1/products/resolve","按平台信息解析产品",AUTH.none,"query: platform,platformProductId,model,manufacturer,category,name"); ep("POST","/api/v1/products/reconcile","存量设备按型号归类",AUTH.none,"-"); ep("POST","/api/v1/products/reconcile-spec","补全省物模型",AUTH.none,"query: memberId"); sec("product","物模型(TSL)","产品标准控制模型、属性/事件/服务维护"); ep("GET","/api/v1/products/{id}/spec","物模型(属性/事件/服务)",AUTH.none,"path: id"); ep("GET","/api/v1/products/{id}/control-schema","产品标准控制模型",AUTH.none,"path: id"); ep("PUT","/api/v1/products/{id}/spec","整体保存物模型",AUTH.none,"path: id; body: ProductSpec"); ep("POST","/api/v1/products/{id}/properties","新增/更新属性(DP)",AUTH.none,"path: id; body: ProductProperty"); ep("POST","/api/v1/products/{id}/events","新增/更新事件",AUTH.none,"path: id; body: ProductEvent"); ep("POST","/api/v1/products/{id}/actions","新增/更新服务",AUTH.none,"path: id; body: ProductAction"); ep("DELETE","/api/v1/spec/{type}/{id}","删除物模型项",AUTH.none,"path: type(property/event/action), id"); sec("product","固件与 OTA","固件注册/发布、OTA 任务、回滚、统计"); ep("POST","/api/v1/products/{id}/firmware","注册固件(草稿)",AUTH.none,"path: id; body: Firmware"); ep("GET","/api/v1/products/{id}/firmwares","产品固件分页",AUTH.none,"path: id; query: page,size"); ep("GET","/api/v1/products/{id}/firmware/latest","最新已发布固件",AUTH.none,"path: id"); ep("PUT","/api/v1/firmwares/{id}/publish","发布固件",AUTH.none,"path: id"); ep("POST","/api/v1/products/{id}/ota","创建OTA任务",AUTH.none,"path: id; body: OtaTaskCreateRequest"); ep("GET","/api/v1/ota-tasks","OTA任务分页",AUTH.none,"query: page,size,productId"); ep("GET","/api/v1/ota-tasks/{id}","OTA任务详情",AUTH.none,"path: id"); ep("POST","/api/v1/ota-tasks/{id}/report","设备上报升级结果",AUTH.none,"path: id; body: OtaReportRequest"); ep("POST","/api/v1/ota-tasks/{id}/cancel","暂停/取消任务",AUTH.none,"path: id"); ep("POST","/api/v1/products/{id}/ota/rollback","版本回滚",AUTH.none,"path: id; query: targetVersion(可空)"); ep("GET","/api/v1/ota-tasks/stats","OTA统计看板",AUTH.none,"query: productId(可空)"); /* —— 涂鸦云集成 tuya —— */ sec("tuya","账号绑定","OAuth 授权、Token/原生/短信绑定、解绑"); ep("GET","/api/v1/tuya/auth/url","获取涂鸦 OAuth 授权URL",AUTH.req,"Header: Bearer "); ep("GET","/api/v1/tuya/auth/status","涂鸦绑定状态",AUTH.req,"Header: Bearer "); ep("POST","/api/v1/tuya/auth/callback","处理OAuth回调",AUTH.req,"body: OauthCallback"); ep("GET","/api/v1/tuya/auth/callback-page","OAuth回调页(返回HTML)",AUTH.none,"query: code, state"); ep("POST","/api/v1/tuya/auth/bind-token","Token绑定涂鸦账号",AUTH.req,"body: BindTokenRequest"); ep("POST","/api/v1/tuya/auth/auto-bind","自动绑定涂鸦账号",AUTH.req,"Header: Bearer "); ep("POST","/api/v1/tuya/auth/bind-native","APP原生SDK绑定",AUTH.req,"body: TuyaNativeBindRequest"); ep("POST","/api/v1/tuya/auth/unbind","解绑涂鸦账号",AUTH.req,"Header: Bearer "); ep("POST","/api/v1/tuya/auth/sms/send","发送绑定验证码",AUTH.none,"body: SmsSendRequest"); ep("POST","/api/v1/tuya/auth/sms/bind","验证码绑定涂鸦账号",AUTH.req,"body: SmsBindRequest"); sec("tuya","设备与场景","涂鸦家庭/设备/场景同步与控制"); ep("GET","/api/v1/tuya/homes","涂鸦家庭列表",AUTH.req,"Header: Bearer "); ep("GET","/api/v1/tuya/devices","涂鸦设备列表",AUTH.req,"query: homeId(可空)"); ep("POST","/api/v1/tuya/devices/bind","绑定涂鸦设备到平台",AUTH.req,"body: TuyaBindRequest"); ep("POST","/api/v1/tuya/devices/status","设备状态实时同步",AUTH.none,"body: DeviceStatusSyncRequest"); ep("POST","/api/v1/tuya/devices/{deviceId}/commands","发送涂鸦控制指令",AUTH.req,"path: deviceId; body: 涂鸦指令数组 List<{code,value}>"); ep("GET","/api/v1/tuya/scenes","涂鸦场景列表",AUTH.req,"query: homeId(可空)"); ep("POST","/api/v1/tuya/scenes/{sceneId}/trigger","执行涂鸦场景",AUTH.req,"path: sceneId"); ep("GET","/api/v1/tuya/devices/{deviceId}/status","涂鸦设备状态",AUTH.req,"path: deviceId"); ep("GET","/api/v1/tuya/devices/{deviceId}/cloud-status","涂鸦云端在线状态",AUTH.req,"path: deviceId"); ep("GET","/api/v1/tuya/subscribe/status","消息订阅状态",AUTH.none,"-"); sec("tuya","AI 指令码","设备 AI 指令码档案探测"); ep("GET","/api/v1/tuya/devices/{deviceId}/ai-profile","设备AI指令码档案",AUTH.req,"path: deviceId"); ep("POST","/api/v1/tuya/devices/{deviceId}/ai-profile/detect","探测AI指令码档案",AUTH.req,"path: deviceId; query: force"); ep("POST","/api/v1/tuya/ai-profile/detect-all","批量探测AI指令码",AUTH.req,"Header: Bearer "); sec("tuya","设备与配网","重命名、Wi-Fi/Matter/Zigbee 配网 Token"); ep("POST","/api/v1/tuya/devices/{deviceId}/rename","重命名涂鸦设备",AUTH.req,"path: deviceId; body: TuyaRename"); ep("POST","/api/v1/tuya/config/wifi-token","Wi-Fi配网Token",AUTH.req,"query: homeId(默认0)"); ep("POST","/api/v1/tuya/config/matter-token","Matter配网Token",AUTH.req,"query: homeId(默认0)"); ep("POST","/api/v1/tuya/config/zigbee-token","Zigbee配网Token",AUTH.req,"query: homeId(默认0)"); sec("tuya","天气","当前天气查询"); ep("GET","/api/v1/weather","获取当前天气",AUTH.none,"query: lat(默认31.23), lon(默认121.47)"); /* —— 数据采集 data —— */ sec("data","时序数据","设备数据点上报、历史查询、聚合统计、最新值"); ep("POST","/api/v1/data/devices/{deviceId}/data","接收设备上报数据",AUTH.none,"path: deviceId; body: List","ingest"); ep("GET","/api/v1/data/devices/{deviceId}/history","查询历史数据",AUTH.none,"path: deviceId; query: DataHistoryQuery"); ep("GET","/api/v1/data/devices/{deviceId}/stats","数据聚合统计",AUTH.none,"path: deviceId; query: DataStatsQuery"); ep("GET","/api/v1/data/devices/{deviceId}/latest","获取最新数据",AUTH.none,"path: deviceId"); /* —— 规则与场景 rule —— */ sec("rule","规则引擎","规则 CRUD、启停、手动执行、设备触发"); ep("GET","/api/v1/rules","规则列表",AUTH.opt,"query: RuleQuery","rulelist"); ep("GET","/api/v1/rules/{ruleId}","规则详情",AUTH.none,"path: ruleId"); ep("POST","/api/v1/rules","创建规则",AUTH.opt,"body: RuleEntity"); ep("PUT","/api/v1/rules/{ruleId}","更新规则",AUTH.none,"path: ruleId; body: RuleEntity"); ep("DELETE","/api/v1/rules/{ruleId}","删除规则",AUTH.none,"path: ruleId"); ep("POST","/api/v1/rules/{ruleId}/enable","启用规则",AUTH.none,"path: ruleId"); ep("POST","/api/v1/rules/{ruleId}/disable","禁用规则",AUTH.none,"path: ruleId"); ep("POST","/api/v1/rules/{ruleId}/execute","手动执行规则",AUTH.none,"path: ruleId; body: Object(触发数据)"); ep("POST","/api/v1/rules/trigger/{deviceId}","触发设备数据变化",AUTH.none,"path: deviceId; body: Object"); sec("rule","场景联动","场景 CRUD、启停、一键执行、定时(cron)、结构化场景"); ep("GET","/api/v1/scenes","场景分页列表",AUTH.opt,"query: SceneQuery"); ep("GET","/api/v1/scenes/{sceneId}","场景详情",AUTH.none,"path: sceneId"); ep("POST","/api/v1/scenes","创建场景",AUTH.opt,"body: SceneEntity"); ep("PUT","/api/v1/scenes/{sceneId}","更新场景",AUTH.none,"path: sceneId; body: SceneEntity"); ep("DELETE","/api/v1/scenes/{sceneId}","删除场景",AUTH.none,"path: sceneId"); ep("POST","/api/v1/scenes/{sceneId}/enable","启用场景",AUTH.none,"path: sceneId"); ep("POST","/api/v1/scenes/{sceneId}/disable","禁用场景",AUTH.none,"path: sceneId"); ep("POST","/api/v1/scenes/{sceneId}/execute","一键执行场景",AUTH.none,"path: sceneId"); ep("PUT","/api/v1/scenes/{sceneId}/cron","设置定时执行(cron)",AUTH.none,"path: sceneId; body: {cron} (六段式 0 0 * * * ?)"); ep("POST","/api/v1/scenes/v2","创建结构化场景",AUTH.opt,"body: SceneCreateRequest","scene"); ep("PUT","/api/v1/scenes/{sceneId}/v2","更新结构化场景",AUTH.opt,"path: sceneId; body: SceneCreateRequest"); ep("GET","/api/v1/scenes/{sceneId}/v2","结构化场景详情",AUTH.none,"path: sceneId"); /* —— AI 智能 ai —— */ sec("ai","语音控制","自然语言指令、设备指令码、AI 日志"); ep("POST","/api/v1/ai/command","执行自然语言控制指令",AUTH.req,"body: AiCommandRequest"); ep("GET","/api/v1/ai/devices","可AI控制设备清单",AUTH.req,"Header: Bearer "); ep("GET","/api/v1/ai/devices/{deviceId}/profile","查询设备控制指令码",AUTH.req,"path: deviceId"); ep("PUT","/api/v1/ai/devices/{deviceId}/profile","校准设备控制指令码",AUTH.req,"path: deviceId; body: AiDeviceProfileRequest"); ep("GET","/api/v1/ai/logs","最近AI指令日志",AUTH.req,"query: limit(默认20)"); sec("ai","别名管理","设备/场景/房间别名的增删查、语音鉴权"); ep("GET","/api/v1/ai/aliases","查询设备/场景/房间别名",AUTH.req,"query: targetType(device/scene/room)"); ep("POST","/api/v1/ai/aliases","新增/更新别名",AUTH.req,"body: AiAliasRequest"); ep("DELETE","/api/v1/ai/aliases/{aliasId}","删除别名",AUTH.req,"path: aliasId"); ep("GET","/api/v1/ai/speech/auth","讯飞语音听写鉴权地址",AUTH.req,"Header: Bearer "); /* —— 告警服务 alarm —— */ sec("alarm","告警","告警列表/详情/确认/解决/统计"); ep("GET","/api/v1/alarms","告警列表",AUTH.opt,"query: AlarmQuery"); ep("GET","/api/v1/alarms/{alarmId}","告警详情",AUTH.none,"path: alarmId"); ep("POST","/api/v1/alarms/{alarmId}/confirm","确认告警",AUTH.none,"path: alarmId; body: {userId}"); ep("POST","/api/v1/alarms/{alarmId}/resolve","解决告警",AUTH.none,"path: alarmId; body: {userId}"); ep("GET","/api/v1/alarms/stats","告警统计",AUTH.none,"-"); sec("alarm","告警规则","告警规则 CRUD"); ep("GET","/api/v1/alarm-rules","告警规则列表",AUTH.none,"query: pageNum,pageSize"); ep("POST","/api/v1/alarm-rules","创建告警规则",AUTH.none,"body: AlarmRuleEntity"); ep("PUT","/api/v1/alarm-rules/{ruleId}","更新告警规则",AUTH.none,"path: ruleId; body: AlarmRuleEntity"); ep("DELETE","/api/v1/alarm-rules/{ruleId}","删除告警规则",AUTH.none,"path: ruleId"); /* —— 中控屏接口 panel —— */ sec("panel","屏端 · 身份与会话","中控屏(面板)专用身份层,与会员/后台隔离;凭证建会话后持 panel JWT 调用其余接口"); ep("POST","/api/v1/panels/session","建立会话:终端初始化凭证换受限身份",AUTH.none,"Header: Authorization: PanelBootstrap ; body: PanelSessionReq"); ep("POST","/api/v1/panels/session/refresh","刷新会话:轮换刷新凭证",AUTH.none,"body: PanelSessionRefreshReq"); ep("POST","/api/v1/panels/{panelId}/heartbeat","屏心跳:续在线租期",AUTH.req,"body: PanelHeartbeat"); ep("GET","/api/v1/panels/{panelId}","屏信息",AUTH.req,"path: panelId"); sec("panel","屏端 · 家庭快照","首屏拉取家庭/房间/设备完整视图(设备支持游标分页)"); ep("GET","/api/v1/panels/{panelId}/snapshot","家庭快照(含 rooms/devices/stateEpoch)",AUTH.req,"query: PanelSnapshotQuery"); sec("panel","屏端 · 配置","运营配置拉取(带 checksum,屏端比对是否变更)"); ep("GET","/api/v1/panels/{panelId}/configuration","拉取屏运营配置",AUTH.req,"path: panelId"); sec("panel","屏端 · 设备登记与物模型","本地设备登记、上报标准控制模型"); ep("POST","/api/v1/panels/{panelId}/devices/register","登记本地设备",AUTH.req,"body: PanelDeviceRegister"); ep("PUT","/api/v1/panels/{panelId}/devices/{deviceId}/schema","更新设备标准控制模型",AUTH.req,"path: panelId,deviceId; body: PanelSchemaUpdate"); sec("panel","屏端 · 状态上报","批量设备状态上报(按 epoch/sequence 去旧)"); ep("POST","/api/v1/panels/{panelId}/reports","批量设备状态上报",AUTH.req,"body: PanelReportBatch"); sec("panel","屏端 · 命令执行","拉取 → 申请 → 回报 三段式命令流"); ep("POST","/api/v1/panels/{panelId}/commands/pull","拉取待执行命令(QUEUED→DELIVERED)",AUTH.req,"body: PanelCmdPullReq"); ep("POST","/api/v1/panels/{panelId}/commands/{commandId}/start","申请执行(DELIVERED→EXECUTING)",AUTH.req,"path: panelId,commandId; body: PanelCmdStartReq"); ep("POST","/api/v1/panels/{panelId}/commands/{commandId}/report","命令执行回报",AUTH.req,"path: panelId,commandId; body: PanelCmdReportReq"); sec("panel","屏端 · 诊断与 OTA","诊断上报、屏端 OTA 升级任务拉取"); ep("POST","/api/v1/panels/{panelId}/diagnostics","上报诊断事件(network/mqtt/ota/command/config)",AUTH.req,"body: PanelDiagnostic"); ep("GET","/api/v1/panels/{panelId}/ota-tasks/next","拉取下一个待升级任务",AUTH.req,"path: panelId"); ep("POST","/api/v1/panels/{panelId}/scene-runs/report","屏上报本地设备场景执行结果",AUTH.req,"body: PanelSceneRunReport"); sec("panel","屏端 · 实时事件推送","长轮询 + SSE 双通道(非 WebSocket);屏端二选一,~20s 心跳"); ep("GET","/api/v1/panels/{panelId}/events","长轮询家庭增量事件(阻塞~20s)",AUTH.req,"query: PanelEventQuery"); ep("GET","/api/v1/panels/{panelId}/events/stream","SSE 增量事件流(text/event-stream,非 Result 包装)",AUTH.req,"query: cursor"); sec("panel","会员侧 · 自助绑定(APP 扫码)","与屏身份层隔离;memberId 取自会员 JWT 不信任参数"); ep("GET","/api/v1/member/panels/bindable","可绑定性预检(是否已预置/启用/已绑)",AUTH.req,"query: serialNumber"); ep("POST","/api/v1/member/panels/bind","扫码绑定:预置屏绑到自己的家庭",AUTH.req,"body: PanelBindReq"); ep("GET","/api/v1/member/panels","我的屏列表",AUTH.req,"-"); ep("POST","/api/v1/member/panels/{panelId}/unbind","解绑(仅绑定者本人,释放家庭)",AUTH.req,"path: panelId"); sec("panel","后台 · 屏终端管理","ROLE_ADMIN;预置屏终端、启用禁用、轮换凭证、配置、归属交接"); ep("POST","/api/v1/admin/panels","预置屏终端(一次性初始化凭证)",AUTH.req,"body: PanelAdminCreate"); ep("GET","/api/v1/admin/panels","屏终端列表",AUTH.req,"-"); ep("PUT","/api/v1/admin/panels/{panelId}/status","启用/禁用屏终端",AUTH.req,"path: panelId; query: status(0/1)"); ep("POST","/api/v1/admin/panels/{panelId}/rotate","轮换终端初始化凭证",AUTH.req,"path: panelId"); ep("GET","/api/v1/admin/panels/{panelId}/configuration","查看屏配置",AUTH.req,"path: panelId"); ep("PUT","/api/v1/admin/panels/{panelId}/configuration","保存屏配置",AUTH.req,"path: panelId; body: PanelConfigSave"); ep("POST","/api/v1/admin/panels/executor-reassign","多屏归属交接(routeRevision 递增)",AUTH.req,"body: PanelReassignReq"); /* ===== 渲染工具 ===== */ function esc(s){return String(s).replace(/&/g,'&').replace(//g,'>');} function jsonHTML(obj){return esc(JSON.stringify(obj,null,2));} function fieldTable(fields){ let t=''; fields.forEach(f=>{ const rc=f[2]==='是'?'rq':(f[2]==='否'?'op':''); t+=``; }); return t+'
字段类型必填说明
${esc(f[0])}${esc(f[1])}${esc(f[2])}${esc(f[3])}
'; } function modelCard(key){ const m=MODELS[key]; if(!m) return ''; let h=`

${esc(m.title)}

`; const tags=[]; if(m.entity) tags.push('实体(entity)'); if(m.query) tags.push('查询参数'); if(m.resp) tags.push('响应结构'); h+=`
${tags.join(' ')}
`; h+=fieldTable(m.fields); if(m.nested&&m.nested.length){h+='
嵌套对象字段:
'+fieldTable(m.nested);} if(m.entity){h+=`
+ 公共基类字段(所有 entity 响应均含)${fieldTable(BASE_FIELDS)}
`;} if(m.example!==undefined){h+=`
JSON 示例
${jsonHTML(m.example)}
`;} if(m.sample){h+=`
示例查询串:?${esc(m.sample)}
`;} return h+'
'; } function epHTML(e){ const map=EPMAP[e.m+' '+e.p]||{}; let extra=''; if(map.body){ const mk=map.body; extra+=``; let j=MODELS[mk]?jsonHTML(MODELS[mk].example):''; if(map.isArray) j='[\n '+j.replace(/\n/g,'\n ')+'\n]'; extra+=`
查看请求体 JSON 示例${map.isArray?'(数组)':''}
${j}
`; } if(map.query){ const qk=map.query; extra+=``; extra+=`
查看查询参数表${fieldTable(MODELS[qk].fields)}${MODELS[qk].sample?('
示例:?'+esc(MODELS[qk].sample)+'
'):''}
`; } if(map.resp){extra+=``;} const exBlock=e.ex?`
查看请求示例(curl)
`:''; const st=(e.m+' '+e.p+' '+e.d+' '+e.params+' '+e.a+' '+(map.body||'')+' '+(map.query||'')).toLowerCase(); const tc=e.a===AUTH.req?'t-req':e.a===AUTH.opt?'t-opt':'t-none'; return `
${e.m}${esc(e.p)}${e.a}
${esc(e.d)}
参数:${esc(e.params)}
${extra}${exBlock}
`; } function renderSections(pageId){ const secs=SECTIONS[pageId]||[]; let h=''; if(secs.length>1){h+='
';secs.forEach((s,i)=>{h+=`${esc(s.title)}`;});h+='
';} secs.forEach((s,i)=>{ h+=`

${esc(s.title)}

${esc(s.sub)}
`; s.items.forEach(e=>{h+=epHTML(e);}); h+='
'; }); return h; } function renderModels(){ let h='
';Object.keys(MODELS).forEach(k=>{h+=`${esc(k)}`;});h+='
'; Object.keys(MODELS).forEach(k=>{h+=modelCard(k);}); h+=`
统一响应包装 Result<T>:{ "code": 0, "message": "success", "data": T }。分页接口 data 即 PageResult;单条增改查 data 为对应 entity(如 Device)。
`; return h; } function totalEndpoints(){let n=0;for(const k in SECTIONS)n+=SECTIONS[k].reduce((a,s)=>a+s.items.length,0);return n;} function renderOverview(){ let h=`

${PRODUCT_NAME} 平台 · REST API 文档

统一入口 /api/v1 · 前后端分离 · JWT Bearer 鉴权 · 含字段级类型与 JSON 示例
共收录 ${totalEndpoints()} 个接口 / ${Object.keys(MODELS).length} 个数据模型,按业务域拆分多页。

`; h+='
'; PAGES.forEach(p=>{ if(p.id==='index')return; const cnt=p.id==='models'?Object.keys(MODELS).length:(SECTIONS[p.id]?SECTIONS[p.id].reduce((a,s)=>a+s.items.length,0):0); const descMap={auth:'后台登录注册、用户/角色/日志/平台配置',member:'会员账号、家庭与房间',device:'设备 CRUD、连接、控制指令、分组',product:'产品、物模型 TSL、固件与 OTA',tuya:'涂鸦 OAuth 绑定、设备/场景控制、配网',data:'时序数据上报、历史、聚合',rule:'规则引擎与场景联动(含 cron)',ai:'自然语言控制与语音',alarm:'告警与告警规则',models:'全部请求/响应字段与 JSON 示例',partner:'开放平台授权对接:应用凭证 ak/sk 换取 JWT,后台应用管理、接入指南与接口说明',panel:'中控屏(面板)身份、会话、快照、命令、实时事件推送、OTA 与会员自助绑定'}; h+=`

${esc(p.label)}

${descMap[p.id]||''}

${cnt} 项 →
`; }); h+='
'; h+=`
鉴权说明:需登录的接口在请求头携带 Authorization: Bearer <token>。后台管理类代码层不强制校验,由网关/反代保证;标注 强制 的接口需会员 Token(无效即报错);可选 仅用于数据隔离(会员看自己的、admin 看全部);无 不校验。
统一响应:{"code":0,"message":"success","data":{...}};分页 data 为 {records,total,pages,current,size}。所有 entity 响应额外含公共基类字段(见「数据模型」章末)。
`; return h; } function getBase(){try{return localStorage.getItem('apiBase')||DEFAULT_BASE;}catch(e){return DEFAULT_BASE;}} function setBase(v){try{localStorage.setItem('apiBase',v);}catch(e){}renderExamples(v);} function renderExamples(b){document.querySelectorAll('#content .ex pre[data-ex]').forEach(pre=>{const k=pre.dataset.ex;if(EXAMPLES[k])pre.innerHTML=EXAMPLES[k](b);});} function filterPage(){const q=(document.getElementById('q').value||'').trim().toLowerCase();let vis=0;document.querySelectorAll('#content .ep').forEach(el=>{const ok=!q||el.dataset.text.indexOf(q)>=0;el.classList.toggle('hidden',!ok);if(ok)vis++;});const em=document.getElementById('empty');if(em)em.style.display=vis?'none':'block';} function renderSidebar(cur){ let h=`

${PRODUCT_NAME} · API

REST 接口文档 v1

`; let last=null; PAGES.forEach(p=>{if(p.group!==last){h+=`
${esc(p.group)}
`;last=p.group;}h+=`${esc(p.label)}`;}); h+='
由代码扫描自动汇总
若有出入以服务端代码为准
'; document.getElementById('sidebar').innerHTML=h; } function renderTopbar(pageId){ const p=PAGES.find(x=>x.id===pageId); let h=`
${esc(p.label)}
`; h+=``; h+=`
Base URL
`; document.getElementById('topbar').innerHTML=h; } function renderPartner(){ const B=getBase(); return `

开放平台 · 授权对接(接入指南)

本平台采用 应用凭证(ak/sk) + 换取 JWT 的授权模式,与会员 JWT 同源校验。合作伙伴先在后台创建应用拿到 AppKey / AppSecret,再用 Secret 换取短期 Access Token,最后以 Bearer 方式调用业务接口。

一、名词说明

  • AppKey:公开标识,形如 ak-xxxx,可安全出现在前端/配置中。
  • AppSecret:密钥,形如 sk-xxxx,必须保密;服务端仅存储其 BCrypt 哈希,创建/轮换时明文仅展示一次。
  • Access Token:换取得到的 JWT(默认有效期 7 天),用于调用业务接口。

二、对接流程

  1. 后台 系统管理 → 应用管理 → 新建应用,生成 ak/sk(Secret 仅弹窗展示一次,请立即保存)。
  2. 调用 POST /api/v1/auth/token,用 ak/sk 换取 Access Token。
  3. 业务接口请求头带上 Authorization: Bearer <accessToken>。
  4. 密钥泄露或人员变动时,在后台 轮换密钥(旧 Secret 立即失效)或 禁用 应用。

三、换取 Token

支持两种传参方式(JSON 或 OAuth2 client_credentials 表单):

查看 curl 示例(JSON)
curl -X POST '${B}/api/v1/auth/token' \\
  -H 'Content-Type: application/json' \\
  -d '{"appKey":"ak-xxxxxxxxxxxx","appSecret":"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}'
查看 curl 示例(client_credentials 表单)
curl -X POST '${B}/api/v1/auth/token' \\
  -H 'Content-Type: application/x-www-form-urlencoded' \\
  -d 'grant_type=client_credentials&client_id=ak-xxxxxxxxxxxx&client_secret=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

返回:{"accessToken":"eyJ...","tokenType":"Bearer","expiresIn":604800,"appKey":"ak-xxx"}

四、调用业务接口

查看 curl 示例(携带 Token)
curl '${B}/api/v1/devices?pageNum=1&pageSize=10' \\
  -H 'Authorization: Bearer <accessToken>'

应用 Token 拥有 ROLE_APP 权限,可访问业务接口;建议结合后台配置的 授权范围(scopes)、IP 白名单、限流 与 过期时间 做最小权限控制。

五、后台管理接口(管理员)

  • POST /api/v1/admin/apps 创建应用(返回 ak/sk)
  • GET /api/v1/admin/apps 应用列表
  • GET /api/v1/admin/apps/{id} 应用详情
  • PUT /api/v1/admin/apps/{id}/status?status=0|1 启用/禁用
  • POST /api/v1/admin/apps/{id}/rotate 轮换密钥(返回新 sk)

六、安全建议

  • 全链路 HTTPS:Token 与 Secret 均为明文承载,务必通过 HTTPS 传输。
  • Secret 不出服务端:服务端仅存 BCrypt 哈希,前端/日志不落明文。
  • 最小权限:为每个对接方配置 scopes / IP 白名单 / 限流 / 有效期。
  • 可吊销:发生泄露立即「禁用」或「轮换密钥」,无需改代码。
`; } function initDoc(pageId){ renderSidebar(pageId); renderTopbar(pageId); let c=''; if(pageId==='index')c=renderOverview(); else if(pageId==='models')c=renderModels(); else if(pageId==='partner')c=renderPartner(); else c=renderSections(pageId); c+='
本页没有匹配的接口
'; c+=``; document.getElementById('content').innerHTML=c; renderExamples(getBase()); }