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
|
91d50eda91
|
fix(J): 清理孤儿涂鸦设备与测试联动数据, 解绑时联动失效设备与AI档案
数据层(V24 迁移): 逻辑删除无涂鸦绑定记录的孤儿涂鸦设备及其AI档案; 物理删除测试阶段的联动规则、场景与场景执行记录(条件相同但动作矛盾的测试数据); 保留产品/物模型/家庭/成员/活动日志。幂等可重复执行。
代码层(根治): TuyaCloudService.unbindUser 解绑时联动逻辑删除该用户的涂鸦设备与AI档案, 避免解绑后设备仍在列表被联动规则反复命中而刷错误日志; 采用逻辑删除, 用户重新绑定同步时 restoreById 自动恢复。
|
2026-10-27 21:56:45 +08:00 |
|
2995840376
|
4d8d38909d
|
fix(I): HTTP 语义类异常返回真实 4xx 状态码, 不再兜底成 500
问题:GlobalExceptionHandler 只对业务异常/参数校验做了分类,其余异常一律被兜底的
Exception handler 处理成 500「系统繁忙」。例如以 application/octet-stream 发送
JSON 请求体本应返回 415 Unsupported Media Type,却得到 500,误导调用方与排障。
改动(iot-common,四个服务共用):
- ResultCode 新增:UNSUPPORTED_MEDIA_TYPE(415) / NOT_ACCEPTABLE(406) / PAYLOAD_TOO_LARGE(413)
- GlobalExceptionHandler 新增 HTTP 语义类异常处理:
HttpMediaTypeNotSupportedException -> 415
HttpMediaTypeNotAcceptableException -> 406
HttpRequestMethodNotSupportedException-> 405
MissingServletRequestParameterException / MethodArgumentTypeMismatchException
/ HttpMessageNotReadableException -> 400
MaxUploadSizeExceededException -> 413
- 响应约定:真实 HTTP 状态码 + body.code 与状态码保持一致(沿用 JwtAuthenticationEntryPoint 的
401 约定);业务/参数异常维持 HTTP 200 + body.code 的历史约定,前端无需改动
- 这类异常属客户端错误,日志级别由 error 降为 warn,避免污染错误日志
- 新增单测 iot-common/src/test/java/com/iot/common/exception/GlobalExceptionHandlerHttpStatusTest.java
(2 个用例全部通过)
生产验证:
错误 Content-Type -> HTTP 415 body.code=415
错误请求方法 -> HTTP 405
参数类型不匹配 -> HTTP 400
请求体 JSON 非法 -> HTTP 400
正常列表/物模型 -> HTTP 200
前端 axios 拦截器对非 401/403/500 的 4xx 走 data?.message 分支,提示更准确,无回归。
|
2026-10-27 19:31:56 +08:00 |
|
2995840376
|
0ff9103089
|
fix(H): 修复 product_property 驼峰遗留列导致后台产品页面 500
问题:后台「产品中心 - 产品列表/详情/物模型」报
Unknown column 'completion_mode' in 'field list'(BadSqlGrammarException)
根因:V19(面板 P2 迁移)建列时把 completionMode 直接当作列名写入 product_property(驼峰),
而 MyBatis-Plus 默认把实体字段 completionMode 映射为下划线形式 completion_mode,
实体查询/写入时找不到列。
注:V22 已把 completion_mode 的权威归属判定为 product_action 维度,
product_property 上的同名列属于历史兼容,保留但统一为下划线命名。
修复:新增 V23 迁移,将 product_property.completionMode 重命名为 completion_mode
- 幂等可重复执行,三种情形分别处理:
1) 仅有驼峰列 -> 直接 RENAME COLUMN
2) 两列都存在 -> 先回填旧列非空值到新列,再 DROP 旧列
3) 仅有下划线列 -> 跳过
- 无存储过程、无 DELIMITER(MySQL batch 管道模式下 DELIMITER 失效)
- 全库扫描(COLUMN_NAME <> LOWER(COLUMN_NAME))确认驼峰遗留列仅此一处
上线过程:全库备份 /opt/iotsaas/backup/iot_platform_before_v23.sql
-> 克隆库 iot_v23_test 演练两遍(重命名成功 + 幂等无副作用)-> 生产执行 -> 克隆库 DROP
验证:/api/v1/products、/api/v1/products/{id}、/api/v1/products/{id}/spec、
/api/v1/products/{id}/control-schema 全部返回 200,物模型属性正常返回。
|
2026-10-27 19:12:13 +08:00 |
|
2995840376
|
56a40edd8e
|
fix(G): 设备控制指令业务异常降噪, 避免定时场景刷爆错误日志
问题:定时场景(SceneCronScheduler, 如 bb 场景)每分钟执行 7 条规则并向涂鸦设备下发控制指令,
而设备归属用户尚未绑定涂鸦账号,每次下发都抛 BusinessException,并以 ERROR + 完整堆栈记录,
单日产生上千条错误日志,掩盖真实故障。
改动(iot-device DeviceCommandListener):
- 区分业务可预期异常与系统异常:
BusinessException(如"请先在个人中心绑定涂鸦账号")改为 WARN 单行输出,不再打印堆栈
- 新增 60 秒日志抑制窗口:同一 deviceId 在窗口内只详细记录一次,其余静默累计;
下一个窗口首次记录时汇总上一周期被抑制的次数,避免高频场景刷屏
- 系统异常保留 ERROR + 堆栈(窗口内首次),同样受窗口抑制保护
生产验证:重启后日志变为 WARN「设备控制指令业务异常: 请先在个人中心绑定涂鸦账号(上一周期同类异常已抑制 N 次)」,
不再输出堆栈;RabbitMQ 队列深度为 0;4 个服务 active。
|
2026-10-27 18:40:51 +08:00 |
|
2995840376
|
b2d758be33
|
fix(F): 日志轮转治理 - 统一 logback 配置 + journald 限额
问题根因:
- iot-device.out 涨到 613MB:systemd StandardOutput=append: 无任何轮转
- DEBUG 刷屏(单日 11521 条):logging.level.com.iot=DEBUG 使 MyBatis 每条 SQL 打印两遍
- 涂鸦 open-mq-sdk jar 自带 logback.xml 抢占日志配置(iot-data/rule/alarm 未声明 logging.config),
把日志写到 ./logs 相对目录且无总量上限
改动:
- 新增 backend/iot-common/src/main/resources/logback-spring.xml(4 服务共用唯一真相源):
RollingFileAppender 按日 + 50MB 双触发切分,保留 7 天,主日志总量上限 2GB,
错误日志单独一份(保留 30 天 / 512MB),并对涂鸦/MQTT/AMQP 等第三方 SDK 降噪
- 父 pom 增加 maven-resources-plugin,把该配置复制进各服务 BOOT-INF/classes
- 4 个服务 application.yml 统一声明 logging.config,并把 com.iot 级别收敛为 INFO
- 新增 backend/deploy/systemd/:4 个 unit 改为 StandardOutput=journal + 注入 LOG_DIR,
附 journald 限额(SystemMaxUse=1G / SystemMaxFileSize=50M / MaxRetentionSec=7day)
- deploy-all.bat 增加日志治理步骤(同步 unit + journald + 清理历史 .out)
生产验证:日志落到 /opt/iotsaas/logs/app/*.log,DEBUG 计数 0,历史 .out 已清理,
journal 776M 降到 552M,4 服务 active,9081/9083 文档返回 200
|
2026-10-27 18:28:19 +08:00 |
|
2995840376
|
6e3f3ea661
|
fix(E): 修复 iot-device 启动失败 + V22 迁移脚本重写为可执行版本
【生产故障与修复】
iot-device 部署后反复重启失败(status=1/FAILURE, restart counter 3+):
UnsatisfiedDependencyException: No qualifying bean of type
'com.iot.device.mapper.ProductActionMapper' available
根因: iot-device 模块【没有 @MapperScan】(仅 iot-rule 有), Mapper 靠
@Mapper 注解被 mybatis-spring-boot-starter 自动扫描;
新建的 ProductEventMapper / ProductActionMapper 漏写 @Mapper 注解,
导致扫描不到 -> ControlSchemaServiceImpl 注入失败 -> 启动失败。
修复: 两个 Mapper 补 @Mapper 注解; 已全量核查 iot-device 31 个 Mapper 均有该注解。
【V22 迁移脚本重写】(实测发现原写法在本环境根本执行不了)
- 原写法使用存储过程 + DELIMITER, 实测三种执行方式(管道/文件重定向/source)
全部失败: MySQL 客户端在 batch 模式会把 DELIMITER 当普通 SQL 发给服务器。
生产库现存存储过程数为 0, 佐证历史 V19/V21 等含 DELIMITER 的脚本
其存储过程部分从未生效(仅 ADD COLUMN 等语句生效)。
- 重写为零存储过程、零 DELIMITER 的普通语句序列:
加列幂等 -> PREPARE + information_schema 判定(ADD COLUMN 走 PREPARE 实测可用)
索引 -> 直接 DDL(ADD INDEX 走 PREPARE 实测报 1064, 直接执行可用)
- 库名统一用 DATABASE() 判定, 测试库克隆无需改写脚本
- 清理顺序调整: 唯一索引统一放在软删重复行清理之后, 避免索引创建失败
【生产验证】(R730 192.168.3.3)
- 全库备份: /opt/iotsaas/backup/iot_platform_before_v22.sql
- 克隆库演练: 建克隆库 -> 跑 V22 -> 校验 -> DROP, 生产数据零风险
- 正式执行: platform_type 8行 / product.protocol / product_property.grp,
is_primary, enum_label / product_action.completion_mode 全部就位
- 唯一索引: uk_platform_product / uk_product_code(x3) / idx_product_id 全部就位
- 数据完整性: product=11 device=13, 与迁移前一致
- 数据清洗: 脏 model/manufacturer=0, 空串 platform_product_id=0, device.miot=0
- tracking 表已标记 V22, 避免 deploy 脚本重复执行
【部署脚本加固】
deploy-all.bat 迁移步骤加 --force: 迁移脚本中'已存在'的 DDL(重复列/索引)
属正常情况, 不应中断整体部署
验证: 4 服务全部 active; 后台首页/SPA路由/9081文档/9083文档 均 200;
产品列表 API 返回 401 未认证(路由与过滤器正常)
|
2026-10-27 13:49:15 +08:00 |
|
2995840376
|
297206368e
|
feat(B11): AI 指令码从产品物模型派生, 消除第三套指令码档案
重构前: ai_device_profile 的指令码全靠品类猜(默认 switch_1/switch_led/control),
与 product_property 物模型、涂鸦 SDK schema 是三套独立来源, 必然漂移 ——
多开关位/带背光设备会拿到错误指令码。
重构后决策优先级:
1. 人工覆盖(source=manual) —— 用户显式指定, 最高优先
2. 产品物模型派生 —— semantic 标注的权威来源(后端 SpecSemanticResolver 落库)
3. 已存在的档案(探测写入/类型默认) —— 兼容历史数据
4. 品类推断兜底 —— 物模型缺失时
新增 ProductSpecMapper(只读投影): 查询产品物模型功能点(code/name/type/semantic/grp/access/dp_id)
fromProductSpec 映射:
- power -> powerCode (多开关位取 is_primary 通道)
- switch -> powerCode 兜底 (物模型未标 power 时取首个可写开关通道)
- brightness -> brightnessCode
- color_temp -> colorTempCode
- mode -> modeCode
- temperature -> temperatureCode
派生结果同时写回 product_id(产品级共享), 不再逐设备重复存储
验证: mvn -pl iot-common,iot-device,iot-rule -am compile BUILD SUCCESS
|
2026-10-27 13:25:32 +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
|
d6d9a94736
|
feat(C2-C4): 物模型编辑器支持语义编辑 + 修复全量替换撞唯一索引
SpecEditor.vue:
- 属性表新增 语义/分组/主开关 三列
- 属性编辑表单新增 semantic 下拉(17种标签, 与后端 SpecSemanticResolver 一一对应)、
grp 下拉(8种分组)、is_primary 开关
- 分组说明写明: switch 组的开关位支持整组联动(switch_1~4 一起开关)
- 语义说明写明: 决定客户端如何渲染与能否作为主开关, 取代按名称猜测
后端 saveSpec 修复:
- 全量替换由逻辑删除改为物理删除
原因: 逻辑删除留下同 (product_id, code) 的软删行, 与 V22 的
(product_id, code, deleted) 唯一索引冲突 —— 连续保存两次即报错
- 新增 ProductEventMapper/ProductActionMapper.physicallyDeleteByProductId
- 语义缺失时自动推导(与 SpecSemanticResolver 规则一致)
- 状态量被误标 write 时强制只读
- 保存后调用 applyPrimary 重算主开关
- action 缺省 completionMode=SYNC
applyPrimary 语义修正:
- 人工优先: 已存在人工标记(is_primary=1)则保留人工选择, 不自动改写
- 仅在无人工标记时按语义规则自动补齐
(reconcile 场景重建后无标记 -> 自动选; 后台编辑场景有人工标记 -> 尊重)
V22 迁移顺序修正:
- 唯一索引统一移到'软删重复行清理'之后执行, 避免索引创建即失败
- 新增软删重复行物理清理(同 (product_id, code) 只保留 id 最小的一条)
- 索引语句去重
验证: mvn BUILD SUCCESS; vite build 通过(ProductDetail chunk 已更新)
|
2026-10-27 13:19:08 +08:00 |
|
2995840376
|
3120af6464
|
feat(C2-C3): 后台产品/设备管理重构 - 产品中心 + 关系可视化
后端:
- Product 实体新增统计字段(deviceCount/propertyCount/eventCount/
mainSwitchCode/mainSwitchName, 非持久化)
- ProductServiceImpl.fillProductStats(): 列表批量回填
一次 selectBatchIds, 无 N+1; 让产品-设备-物模型的关系在 UI 层可见
(此前关系在数据层存在但 UI 层完全不可见, 这就是'看起来乱'的根因)
- Device 实体新增 productName/productKey 非持久化字段
- DeviceServiceImpl.fillProductInfo(): 设备列表回填所属产品
前端 api/product.ts:
- Product/ProductProperty/ControlItem 接口补齐语义与统计字段
- 新增 syncProductSpec(): 从涂鸦云同步物模型
- 新增 listProductDevices(): 复用设备列表按 productId 过滤
产品列表 (ProductList.vue):
- 新增'物模型'列: DP 数量 + 主开关名 (未同步显示告警标签)
- 新增'设备'列: 挂载台数, 点击跳转设备列表并带 productId 过滤
- 新增'同步'按钮: 从涂鸦云同步物模型
- 新增'详情'按钮 -> 产品详情页
- 删除提示改为动态显示设备数
新增产品详情页 (ProductDetail.vue):
- 概览统计卡: 设备数/DP数/事件数/主开关
- 5 Tab: 基本信息 / 物模型 / 设备 / 固件与OTA
- 物模型 Tab 展示 semantic/grp/isPrimary/dpId, 并可切换属性/事件/服务
设备列表 (DeviceList.vue):
- 新增'所属产品'列, 可点击跳产品详情, 未关联显示'未关联'
- 新增'所属产品'筛选下拉(含各产品设备数)
- 支持从产品页跳转带 productId 参数自动筛选
MainLayout.vue 修复:
- groupedRoutes 未过滤 meta.hidden, 详情页会混进侧边栏菜单
验证: mvn 编译通过; npx vite build 通过(ProductDetail chunk 已产出)
|
2026-10-27 12:45:54 +08:00 |
|
2995840376
|
fdf0e265fc
|
feat(B8-B9): 屏端物模型回流 + 设备级 schema 覆盖层(消除死数据)
问题: device.schema_json 是中控屏上报物模型的唯一落点, 但全仓只写不读,
属死数据; 同时屏端本地设备(zigbee/ble/matter)无平台产品, 产品级物模型里
也查不到东西, 导致这类设备拿不到 control-schema。
新增 PanelSpecAdoptService:
- 归一化: 屏端通用协议类型(BOOLEAN/INTEGER/NUMBER/ENUM/STRING) ->
产品物模型类型(bool/int/float/enum/string);
access(READ/WRITE/READ_WRITE) -> (read/write/read,write)
- 回流: 已存在的属性只补齐/修正屏端明确提供的字段(不做破坏性覆盖),
不存在的补建; 产品级已有而屏端未报的属性保留(产品级是超集)
- 语义: 屏端已上报 semantic/channel 直接落库, 缺失时由
SpecSemanticResolver 兜底推导
- toProperties(): 供无产品设备直接以设备级 schema 作为模型来源
PanelDeviceService: register / updateSchema 均触发回流
ControlSchemaServiceImpl.getDeviceSchema 改造:
- 无 product_id 的设备: 用设备级 schema 构建控制模型(不再直接返回空)
- 有 product_id: 产品基线 + 设备级覆盖层 merge
同 code 以设备级为准, 设备级独有的 code 追加
- 新增 mergeItem(): 仅覆盖屏端明确提供的字段
验证: mvn -pl iot-common,iot-device -am compile 通过
|
2026-10-27 12:39:23 +08:00 |
|
2995840376
|
a8c2d005a8
|
feat(B10): 产品<->设备反向查询 - 设备列表可按产品过滤并展示所属产品
后端:
- DeviceQuery 新增 productId(精确) 与 productKeyword(按产品名/编码模糊)
- DeviceServiceImpl.pageDevices 支持两种产品维度过滤
productKeyword 场景: 先解析匹配的 product_id 集合再约束 device,
集合为空时用 1=0 保证不误返回全部
- 新增 fillProductInfo(): 批量回填设备所属产品名称与业务编码
(一次 selectBatchIds, 避免 N+1)
- Device 实体新增非持久化展示字段 productName / productKey
验证: mvn -pl iot-common,iot-device -am compile 通过
|
2026-10-27 12:35:52 +08:00 |
|
2995840376
|
d7e34efb58
|
feat(B2-B4): 产品生命周期闭环 - 删除防孤儿 / reconcile 幂等全量 / 同步自愈
B2 deleteProduct 引用校验:
- 原实现直接 deleteById, 会留下 product_id 指向已删产品的孤儿设备,
且 reconcile 只补 NULL 的设备 => 孤儿永远无法回收
- 现校验 device / firmware / ota_task 三类引用, 任一存在即拒绝删除并提示数量
(此前前端却提示'已关联设备的产品不可删除', 后端实际毫无校验)
B3 reconcileDevices 幂等全量对齐:
- 原: 仅处理 product_id IS NULL 的设备
- 现: 对所有有 platform_product_id 的设备按 (platform, platform_product_id)
重新解析产品并校正 product_id, 已对齐的跳过(不产生无谓写)
- 无平台产品ID的纯本地手工设备跳过
B4 同步自愈 (新增 ProductService.ensureDeviceProduct):
- TuyaController 同步路径由「仅当 product_id IS NULL 才解析」改为每次校验
- 产品不存在(含已逻辑删除)/平台产品ID不匹配 => 自动重新解析或创建产品
- 配合 B2 引用校验, 产品删除不再产生不可回收的孤儿
验证: mvn -pl iot-common,iot-device -am compile 通过
|
2026-10-27 12:34:27 +08:00 |
|
2995840376
|
6461acecd1
|
feat(B5-B7): 引入 SpecSemanticResolver 作为物模型语义唯一权威
新增 SpecSemanticResolver (356行):
- 语义标签体系: power/switch/backlight/indicator/brightness/color_temp/
mode/scene/countdown/child_lock/temperature/humidity/battery/motion/
contact/fault/raw
- 功能分组 grp: switch/scene/mode/countdown/display/config/measure/status
- resolve(): 单功能点语义推导(非主控词优先 -> 功能语义 -> 开关兜底)
- pickPrimary(): 产品范围内选主开关(power优先 -> switch组首个 -> 唯一可写bool兜底)
- isReadOnly(): 只读语义修正, 修复涂鸦 fnCodes 为空时兜底'全部可写'的缺陷
实体/DTO 补齐语义字段(V19 建了列但代码里没有):
- ProductProperty: semantic/grp/isPrimary/channel/completionMode/enumLabel
- ProductAction: completionMode
- ControlItem: semantic/grp/isPrimary/enumLabels
ControlSchemaServiceImpl 改造:
- toItem(): 搬运语义字段; 物模型无语义时即时推导(旧数据兼容);
状态量被误标 write 时强制只读
- pickMainSwitch(): 优先 is_primary -> semantic=power -> 回退旧启发式
(原实现按名称含 switch/power 猜测, 是 JD2BTSA 背光误控的根因)
- fillValues(): code 优先、dp_id 兜底(修复 dps key 语义分裂导致回填失败)
ProductServiceImpl.reconcileSpecFromTuya:
- 同步物模型时落库 semantic/grp, 并用语义修正 access
- 新增 applyPrimary(): 选主开关并回填 is_primary
验证: mvn -pl iot-common,iot-device -am compile 通过
|
2026-10-27 12:32:06 +08:00 |
|
2995840376
|
9786286ad3
|
fix(B1): 修复 resolveProductId 参数错位污染产品字段 + product_key 确定性化
根因: TuyaController 两处调用把「涂鸦 productId」填进 model 位、把字面量
'tuya' 填进 manufacturer 位, 导致 product.model / manufacturer 被写入
平台产品ID (生产数据已出现 P_tuya_y1ouxiqr_42bff218 的脏记录)。
修复:
- TuyaController: 2 处调用改为 model=null, manufacturer=null
(涂鸦 OpenAPI 确实未提供真实型号/厂商, 此前是错位而非真实值)
- ProductServiceImpl.resolveProductId: 新增 sanitizeDimension() 防御,
对畸形入参(等于平台产品ID / 等于平台编码)统一置空, 杜绝同类错位复发
- resolveProductId 入口 platform 经 PlatformType.normalize() 归一
product_key 确定性化:
- 旧: P_<platform>_<model>_<随机UUID8> -> 不可读、无法反查
- 新: P_<platform>_<platformProductId|model> -> 恒定、可反查
(超 56 字符截断并追加短哈希防碰撞)
验证: mvn -pl iot-common,iot-device -am compile 通过
|
2026-10-27 12:22:39 +08:00 |
|
2995840376
|
a932633c50
|
refactor(A): 产品/物模型/设备三层架构重构 - 地基批次
新增:
- iot-common/PlatformType: 平台维度单一真相源(枚举)
统一 tuya/matter/local/xiaomi/huawei/dueros/alexa/google_home,
提供 legacy 别名归一(miot|mi -> xiaomi)与已知性判定
- backend/sql/V22__product_device_arch_refactor.sql: 幂等迁移(153行)
A. platform_type 平台字典表 + 种子数据(后台可维护)
B. product 加 protocol 列; platform_product_id 空串清洗为 NULL;
清洗 model/manufacturer 被涂鸦 productId 污染的历史脏数据;
加 (platform, platform_product_id, deleted) 唯一约束
C. product_property 加 grp/is_primary/enum_label + (product_id,code) 唯一约束
D. product_event/product_action 加 (product_id,code) 唯一约束;
product_action 加 completion_mode
E. device 加 product_id 索引(OTA 物化目标设备此前全表扫); platform 归一
改造:
- DeviceAdapter 新增 platform() 显式声明平台
- DeviceAdapterManager 改为按 platform() 精确匹配,
移除'类名是否包含平台字符串'的脆弱约定(类名与平台码不同名会静默降级)
- DevicePlatform 委托 PlatformType, 消除第四套平台取值域
验证: mvn -pl iot-common,iot-device -am compile 通过
|
2026-10-27 12:08:44 +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 |
|