package cn.iocoder.yudao.module.iot.service.product; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.hutool.core.util.IdUtil; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.time.LocalDateTime; import java.util.Collection; import java.util.List; import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; /** * IoT 产品 Service 实现类 * * @author ahh */ @Slf4j @Service @Validated public class IotProductServiceImpl implements IotProductService { @Resource private IotProductMapper productMapper; @Resource private IotDeviceService deviceService; @Resource @Lazy // 延迟加载,避免循环依赖 private IotDevicePropertyService devicePropertyDataService; @Override public Long createProduct(IotProductSaveReqVO createReqVO) { // 1. 校验 ProductKey if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) { throw exception(PRODUCT_KEY_EXISTS); } // 2. 插入 IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class) .setStatus(IotProductStatusEnum.UNPUBLISHED.getStatus()) .setProductSecret(generateProductSecret()); productMapper.insert(product); return product.getId(); } private String generateProductSecret() { return IdUtil.fastSimpleUUID(); } @Override @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#updateReqVO.id") public void updateProduct(IotProductSaveReqVO updateReqVO) { updateReqVO.setProductKey(null); // 不更新产品标识 // 1. 校验存在 validateProductExists(updateReqVO.getId()); // 2. 更新 IotProductDO updateObj = BeanUtils.toBean(updateReqVO, IotProductDO.class); productMapper.updateById(updateObj); } @Override @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#id") public void deleteProduct(Long id) { // 1.1 校验存在 IotProductDO product = validateProductExists(id); // 1.2 发布状态不可删除 validateProductStatus(product); // 1.3 校验是否有设备 if (deviceService.getDeviceCountByProductId(id) > 0) { throw exception(PRODUCT_DELETE_FAIL_HAS_DEVICE); } // 2. 删除 productMapper.deleteById(id); } @Override public IotProductDO validateProductExists(Long id) { IotProductDO product = productMapper.selectById(id); if (product == null) { throw exception(PRODUCT_NOT_EXISTS); } return product; } @Override public IotProductDO validateProductExists(String productKey) { IotProductDO product = productMapper.selectByProductKey(productKey); if (product == null) { throw exception(PRODUCT_NOT_EXISTS); } return product; } private void validateProductStatus(IotProductDO product) { if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) { throw exception(PRODUCT_STATUS_NOT_DELETE); } } @Override public IotProductDO getProduct(Long id) { return productMapper.selectById(id); } @Override @Cacheable(value = RedisKeyConstants.PRODUCT, key = "#id", unless = "#result == null") @TenantIgnore // 忽略租户信息 public IotProductDO getProductFromCache(Long id) { return productMapper.selectById(id); } @Override public IotProductDO getProductByProductKey(String productKey) { return productMapper.selectByProductKey(productKey); } @Override public PageResult getProductPage(IotProductPageReqVO pageReqVO) { return productMapper.selectPage(pageReqVO); } @Override @DSTransactional(rollbackFor = Exception.class) @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#id") public void updateProductStatus(Long id, Integer status) { // 1. 校验存在 validateProductExists(id); // 2. 更新为发布状态,需要创建产品超级表数据模型 // TODO @芋艿:【待定 001】1)是否需要操作后,在 redis 进行缓存,实现一个“快照”的情况,类似 tl; if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { devicePropertyDataService.defineDevicePropertyData(id); } // 3. 更新 IotProductDO updateObj = IotProductDO.builder().id(id).status(status).build(); productMapper.updateById(updateObj); } @Override public List getProductList() { return productMapper.selectList(); } @Override public List getProductList(Integer deviceType) { return productMapper.selectList(deviceType); } @Override public Long getProductCount(LocalDateTime createTime) { return productMapper.selectCountByCreateTime(createTime); } @Override public List getProductList(Collection ids) { return productMapper.selectByIds(ids); } @Override public void syncProductPropertyTable() { // 1. 获取所有已发布的产品 List products = productMapper.selectListByStatus( IotProductStatusEnum.PUBLISHED.getStatus()); log.info("[syncProductPropertyTable][开始同步,已发布产品数量({})]", products.size()); // 2. 遍历同步 TDengine 表结构(创建产品超级表数据模型) int successCount = 0; for (IotProductDO product : products) { try { devicePropertyDataService.defineDevicePropertyData(product.getId()); successCount++; log.info("[syncProductPropertyTable][产品({}/{}) 同步成功]", product.getId(), product.getName()); } catch (Exception e) { log.error("[syncProductPropertyTable][产品({}/{}) 同步失败]", product.getId(), product.getName(), e); } } log.info("[syncProductPropertyTable][同步完成,成功({}/{})个]", successCount, products.size()); } @Override public void validateProductsExist(Collection ids) { if (CollUtil.isEmpty(ids)) { return; } List products = productMapper.selectByIds(ids); if (products.size() != ids.size()) { throw exception(PRODUCT_NOT_EXISTS); } } }