Commit 90d358a90e5df6060b1d3ce21ea51f80f0130ba4

Authored by YunaiV
1 parent 4843ff57

fix(iot): 仅同步指定产品的物模型表结构

yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.http
1 1 ### 请求 /iot/product/sync-property-table 接口 => 成功
2   -POST {{baseUrl}}/iot/product/sync-property-table
  2 +POST {{baseUrl}}/iot/product/sync-property-table?id=1
3 3 Content-Type: application/json
4 4 tenant-id: {{adminTenantId}}
5 5 Authorization: Bearer {{token}}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java
... ... @@ -144,8 +144,8 @@ public class IotProductController {
144 144 @PostMapping("/sync-property-table")
145 145 @Operation(summary = "同步产品属性表结构到 TDengine")
146 146 @PreAuthorize("@ss.hasPermission('iot:product:update')")
147   - public CommonResult<Boolean> syncProductPropertyTable() {
148   - productService.syncProductPropertyTable();
  147 + public CommonResult<Boolean> syncProductPropertyTable(@RequestParam("id") Long id) {
  148 + productService.syncProductPropertyTable(id);
149 149 return success(true);
150 150 }
151 151  
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java
... ... @@ -152,8 +152,8 @@ public interface IotProductService {
152 152 /**
153 153 * 同步产品的 TDengine 表结构
154 154 *
155   - * 目的:当 MySQL 和 TDengine 不同步时,强制将已发布产品的表结构同步到 TDengine 中
  155 + * 目的:当 MySQL 和 TDengine 不同步时,强制将指定产品的表结构同步到 TDengine 中
156 156 */
157   - void syncProductPropertyTable();
  157 + void syncProductPropertyTable(Long id);
158 158  
159 159 }
160 160 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
... ... @@ -181,24 +181,12 @@ public class IotProductServiceImpl implements IotProductService {
181 181 }
182 182  
183 183 @Override
184   - public void syncProductPropertyTable() {
185   - // 1. 获取所有已发布的产品
186   - List<IotProductDO> products = productMapper.selectListByStatus(
187   - IotProductStatusEnum.PUBLISHED.getStatus());
188   - log.info("[syncProductPropertyTable][开始同步,已发布产品数量({})]", products.size());
189   -
190   - // 2. 遍历同步 TDengine 表结构(创建产品超级表数据模型)
191   - int successCount = 0;
192   - for (IotProductDO product : products) {
193   - try {
194   - devicePropertyDataService.defineDevicePropertyData(product.getId());
195   - successCount++;
196   - log.info("[syncProductPropertyTable][产品({}/{}) 同步成功]", product.getId(), product.getName());
197   - } catch (Exception e) {
198   - log.error("[syncProductPropertyTable][产品({}/{}) 同步失败]", product.getId(), product.getName(), e);
199   - }
200   - }
201   - log.info("[syncProductPropertyTable][同步完成,成功({}/{})个]", successCount, products.size());
  184 + public void syncProductPropertyTable(Long id) {
  185 + // 1. 校验产品存在
  186 + validateProductExists(id);
  187 +
  188 + // 2. 同步指定产品的 TDengine 表结构
  189 + devicePropertyDataService.defineDevicePropertyData(id);
202 190 }
203 191  
204 192 @Override
... ... @@ -212,4 +200,4 @@ public class IotProductServiceImpl implements IotProductService {
212 200 }
213 201 }
214 202  
215   -}
216 203 \ No newline at end of file
  204 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/product/IotProductSyncTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.product;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  4 +import cn.iocoder.yudao.module.iot.controller.admin.product.IotProductController;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
  6 +import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper;
  7 +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
  8 +import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;
  9 +import jakarta.annotation.Resource;
  10 +import org.junit.jupiter.api.Test;
  11 +import org.springframework.context.annotation.Import;
  12 +import org.springframework.test.context.bean.override.mockito.MockitoBean;
  13 +import org.springframework.web.bind.annotation.RequestParam;
  14 +
  15 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  16 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
  17 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  18 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_NOT_EXISTS;
  19 +import static org.junit.jupiter.api.Assertions.*;
  20 +import static org.mockito.Mockito.*;
  21 +
  22 +/**
  23 + * {@link IotProductServiceImpl} 的产品表结构同步单元测试
  24 + *
  25 + * @author 芋道源码
  26 + */
  27 +@Import(IotProductServiceImpl.class)
  28 +public class IotProductSyncTest extends BaseDbUnitTest {
  29 +
  30 + @Resource
  31 + private IotProductServiceImpl productService;
  32 +
  33 + @Resource
  34 + private IotProductMapper productMapper;
  35 +
  36 + @MockitoBean
  37 + private IotDeviceService deviceService;
  38 + @MockitoBean
  39 + private IotDevicePropertyService devicePropertyDataService;
  40 +
  41 + @Test
  42 + public void testSyncProductPropertyTable_success() {
  43 + // mock 数据
  44 + IotProductDO dbProduct = randomPojo(IotProductDO.class);
  45 + productMapper.insert(dbProduct);
  46 + IotProductDO otherProduct = randomPojo(IotProductDO.class);
  47 + productMapper.insert(otherProduct);
  48 + // 准备参数
  49 + Long id = dbProduct.getId();
  50 +
  51 + // 调用
  52 + productService.syncProductPropertyTable(id);
  53 + // 断言:只同步指定产品
  54 + verify(devicePropertyDataService).defineDevicePropertyData(id);
  55 + verifyNoMoreInteractions(devicePropertyDataService);
  56 + }
  57 +
  58 + @Test
  59 + public void testSyncProductPropertyTable_productNotExists() {
  60 + // 准备参数
  61 + Long id = randomLongId();
  62 +
  63 + // 调用,并断言异常
  64 + assertServiceException(() -> productService.syncProductPropertyTable(id), PRODUCT_NOT_EXISTS);
  65 + verifyNoInteractions(devicePropertyDataService);
  66 + }
  67 +
  68 + @Test
  69 + public void testSyncProductPropertyTable_syncFailed() {
  70 + // mock 数据
  71 + IotProductDO dbProduct = randomPojo(IotProductDO.class);
  72 + productMapper.insert(dbProduct);
  73 + // 准备参数
  74 + Long id = dbProduct.getId();
  75 + // mock 同步失败
  76 + RuntimeException failure = new RuntimeException("TDengine sync failed");
  77 + doThrow(failure).when(devicePropertyDataService).defineDevicePropertyData(id);
  78 +
  79 + // 调用,并断言异常
  80 + assertSame(failure, assertThrows(RuntimeException.class, () -> productService.syncProductPropertyTable(id)));
  81 + }
  82 +
  83 + @Test
  84 + public void testController_syncProductPropertyTable_idRequired() throws Exception {
  85 + // 调用:读取请求参数定义
  86 + RequestParam parameter = IotProductController.class.getDeclaredMethod("syncProductPropertyTable", Long.class)
  87 + .getParameters()[0].getAnnotation(RequestParam.class);
  88 + // 断言
  89 + assertEquals("id", parameter.value());
  90 + assertTrue(parameter.required());
  91 + }
  92 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/test/resources/sql/create_tables.sql
... ... @@ -18,7 +18,12 @@ CREATE TABLE IF NOT EXISTS &quot;iot_product&quot; (
18 18 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
19 19 "name" varchar(255) NOT NULL DEFAULT '',
20 20 "product_key" varchar(100) NOT NULL DEFAULT '',
21   - "protocol_type" tinyint NOT NULL DEFAULT '0',
  21 + "product_secret" varchar(100) DEFAULT NULL,
  22 + "register_enabled" bit DEFAULT NULL,
  23 + "icon" varchar(255) DEFAULT NULL,
  24 + "pic_url" varchar(255) DEFAULT NULL,
  25 + "protocol_type" varchar(100) DEFAULT NULL,
  26 + "serialize_type" varchar(100) DEFAULT NULL,
22 27 "category_id" bigint DEFAULT NULL,
23 28 "description" varchar(500) DEFAULT NULL,
24 29 "data_format" tinyint NOT NULL DEFAULT '0',
... ...