5 Commits

Author SHA1 Message Date
2995840376
066d374673 fix(M): 修复品类中文乱码, 子设备在线状态按父网关判定
乱码: 服务器 mysql 客户端在 C locale 下 character_set_client=latin1, 迁移脚本经 Windows type|plink 管道执行时 UTF-8 字节被按 latin1 解读, 中文双重编码。修复: V25/V26 脚本首行加 SET NAMES utf8mb4, deploy-all.bat 的 mysql 调用补 --default-character-set=utf8mb4, 新增 V26 按 code 写回正确中文(幂等)。已验证 product_category.name 的 hex 为正确 UTF-8。

子设备在线: 涂鸦 Mesh/Zigbee 子设备无独立在线通道, SDK 与云端快照对其 online 恒 false, APP 上报后被写成离线。后端新增 resolveEffectiveOnlineStatus —— 上报在线即 online; 子设备上报离线时以父网关为准(父在线则 online); 设备列表输出再做一次父网关聚合, 历史数据立即生效。前端 DeviceInfo 增报 parentDeviceId, 并按品类推断 is_gateway。

APP: 网关下挂设备不再用涂鸦 SDK 快照覆盖后端结论; 列表状态改为在线/离线/状态未知三态; uniapp 端修复状态比大写 ONLINE 导致恒离线、且把状态字符串当 DP JSON 解析的问题。生产验证: JD2BTSA/JD6BTSA 由 offline 变为 online。
2026-10-28 01:25:46 +08:00
2995840376
903b44c84b feat(L): 新增品类管理, 收口涂鸦设备写入的归属校验
品类管理: 新建 product_category 品类字典表(V25, 灌入 8 个预设品类并回填存量 product.category), 后端提供 ProductCategory 增删改查/启停/字典接口, 后台新增「品类管理」页面(设备管理组, 与产品管理同级), 产品管理的品类下拉改为读字典(失败回退内置枚举)。

孤儿设备根治: TuyaController.bindDevices 收口三道校验 —— 绑定门禁(本平台未绑定涂鸦账号直接拒绝)、归属白名单(上报设备须在该用户涂鸦账号真实清单内, 拉取失败降级放行)、owner 校验(他人名下设备不改写; 恢复已删设备时归属改到当前用户)。

兜底自愈: 新增 OrphanTuyaDeviceCleanupTask, 每天 04:30 回收无绑定记录用户的涂鸦设备与 AI 档案(逻辑删除, 可恢复)。

Android: 查看设备时的补同步先确认后端绑定状态, 未绑定不再写库; 同步被拒时记录原因而非静默失败。单测 TuyaControllerBindTest 5 用例覆盖门禁/白名单/owner/恢复归属。
2026-10-28 01:10:59 +08:00
2995840376
4c2d92571e feat(K): 规则保存时校验动作参数必须用物模型 code, 禁止 dpId
背景: 规则动作 params 的 key 会被原样当作涂鸦 code 下发(dp_id 从不参与下发), 历史测试数据里出现过 switch_1 与 1 混用, 导致下发失败或指令相互矛盾, 且保存时无任何校验。

后端: 新增 RuleActionValidator, createRule/updateRule 前校验动作参数 key 必须命中 product_property.code; 命中 dp_id 时直接提示应改用的 code; 同步校验值域(bool 取值、数值 min/max 区间、enum 候选值); 目标设备不存在、无产品物模型等场景给出明确业务错误。

前端: 后台规则表单补齐执行动作编辑(JSON), 提交前本地解析校验, 原本表单不提交 actions 导致新建规则必然失败。

Android: 物模型不可用时的回退路径改为优先使用涂鸦 code(反射读取), 避免提交 dpId 被后端拒绝。

V24: scene_run 表不存在时容错跳过, 便于在未执行 V16 的旧结构库上整段执行。
2026-10-28 00:52:04 +08:00
2995840376
10344e0b05 feat(D): Android 端消费物模型语义, 开关通道以物模型为唯一权威
ControlSchema.ControlItem:
- 新增 semantic / grp / isPrimary / enumLabels 字段
- 拆分隐式 key() 为显式 dpKey()(涂鸦 publishDps 用数字dpId) 与
  codeKey()(平台无关稳定标识, 场景规则/Mesh/云端用)
  原实现两条链路口径不一致(一处dpId一处code), 极易用错

DeviceControlActivity:
- 4 处 it.key() -> it.dpKey()(publishDps 路径)
- 主开关选择新增物模型兜底: 后端 mainSwitchCode -> is_primary -> semantic=power
  (语义由后端 SpecSemanticResolver 落库, 取代客户端按名称猜测)

新增 SpecSwitchResolver:
- 物模型驱动的开关通道解析器
- 通道来源 = control-schema 中 grp=switch 的功能点(即物模型 switch 组)
- 物模型不可用(设备无产品/未同步/网络失败)返回 null -> 调用方回退本地打分
- 保证离线与新设备场景不降级

DeviceRowAdapter:
- 新增物模型通道缓存(setSpecChannels), 回填后按设备局部刷新(不整表重绘)
- 统一内部 SwChannel 视图, 屏蔽物模型/本地打分两种来源差异
- 通道解析优先级: 后端物模型 > 本地打分兜底

DevicesFragment:
- 首次加载后异步拉取各设备 control-schema, 解析开关通道回填 adapter
- 通道属静态数据, 只拉一次, 避免每次刷新发 N 个请求

SceneDetailActivity:
- 规则下发的动作 key 改用 codeKey(), 并注释说明与 publishDps 的区别

验证: javac 编译 ControlSchema + SpecSwitchResolver 通过(两者不依赖 Android SDK);
其余改动为结构性替换, 待 Android Studio 编译验证
2026-10-27 13:23:24 +08:00
2995840376
0ce191c448 chore: 初始化 iotsaas 版本库基线
纳入范围:
- backend/  Spring Boot 多模块后端(iot-common/device/rule/data/alarm/auth)
- frontend/admin/  Vue3 + Element Plus 管理后台
- Android/   涂鸦原生 APP (com.xhai.iot)
- backend/sql/  Flyway 风格迁移脚本(V2~V21)
- docs/       对接文档与设计文档

排除范围:
- 构建产物: node_modules/ target/ build/ dist/ .gradle/
- 签名密钥: *.jks *.keystore (严禁入库)
- 本机配置: Android/local.properties
- 运行日志: logs/ *.log
- AI 协作本地数据 .codebuddy/ 与第三方 demo tuya-demo/

.gitattributes 统一以 LF 存储, Windows 批处理保留 CRLF
2026-10-27 12:00:41 +08:00