ck-chenkang 6 days ago
commit 221b257f8b

@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProduc
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductPackagingSchemeRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockStatisticsRespVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.pallet.ErpPalletDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO;
@ -93,6 +94,13 @@ public class ErpStockController {
return success(stockService.getStockCount(productId));
}
@GetMapping("/statistics-cards")
@Operation(summary = "获得库存统计卡片")
@PreAuthorize("@ss.hasPermission('erp:stock:query')")
public CommonResult<ErpStockStatisticsRespVO> getStockStatistics() {
return success(stockService.getStockStatistics());
}
@GetMapping("/page")
@Operation(summary = "获得产品库存分页")
@PreAuthorize("@ss.hasPermission('erp:stock:query')")
@ -348,8 +356,8 @@ public class ErpStockController {
if (stock.getUnitName() == null || stock.getPurchaseUnitName() == null || stock.getPurchaseUnitConvertQuantity() == null) {
return null;
}
return "1" + stock.getUnitName() + "=" + formatNumber(stock.getPurchaseUnitConvertQuantity())
+ stock.getPurchaseUnitName();
return "1" + stock.getPurchaseUnitName() + "=" + formatNumber(stock.getPurchaseUnitConvertQuantity())
+ stock.getUnitName();
}
private String buildStockDisplay(ErpStockRespVO stock) {
@ -375,12 +383,12 @@ public class ErpStockController {
private String buildPurchaseUnitDisplay(ErpStockRespVO stock, BigDecimal count) {
if (stock.getPurchaseUnitConvertQuantity() == null || stock.getPurchaseUnitConvertQuantity().compareTo(BigDecimal.ZERO) <= 0
|| stock.getUnitName() == null || stock.getPurchaseUnitName() == null) {
return formatNumber(count) + nullToEmpty(stock.getPurchaseUnitName());
return formatNumber(count) + nullToEmpty(stock.getUnitName());
}
BigDecimal unitCount = defaultZero(count).divideToIntegralValue(stock.getPurchaseUnitConvertQuantity());
BigDecimal purchaseUnitCount = defaultZero(count).remainder(stock.getPurchaseUnitConvertQuantity());
return formatNumber(unitCount) + stock.getUnitName()
+ formatNumber(purchaseUnitCount) + stock.getPurchaseUnitName();
BigDecimal purchaseUnitCount = defaultZero(count).divideToIntegralValue(stock.getPurchaseUnitConvertQuantity());
BigDecimal unitCount = defaultZero(count).remainder(stock.getPurchaseUnitConvertQuantity());
return formatNumber(purchaseUnitCount) + stock.getPurchaseUnitName()
+ formatNumber(unitCount) + stock.getUnitName();
}
private void appendQuantity(StringBuilder display, BigDecimal quantity, String unitName, boolean keepZero) {

@ -315,7 +315,9 @@ public class ErpStockInController {
item.setPallets(buildStockInPalletRespList(itemPalletMap.get(item.getId()), palletMap));
MapUtils.findAndThen(productMap, item.getProductId(), product -> {
item.setProductBarCode(product.getBarCode());
item.setProductUnitName(product.getUnitName());
if (StringUtils.isBlank(item.getProductUnitName())) {
item.setProductUnitName(product.getUnitName());
}
item.setCategoryType(product.getCategoryType());
if (item.getSupplierId() == null) {
item.setSupplierId(product.getDefaultSupplierId());

@ -329,7 +329,9 @@ public class ErpStockOutController {
item.setPallets(buildStockOutPalletRespList(itemPalletMap.get(item.getId()), palletMap));
MapUtils.findAndThen(productMap, item.getProductId(), product -> {
item.setProductBarCode(product.getBarCode());
item.setProductUnitName(product.getUnitName());
if (StringUtils.isBlank(item.getProductUnitName())) {
item.setProductUnitName(product.getUnitName());
}
item.setCategoryType(product.getCategoryType());
item.setSupplierId(product.getDefaultSupplierId());
if (product.getDefaultSupplierId() != null && CollUtil.isNotEmpty(product.getSuppliers())) {

@ -0,0 +1,146 @@
package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Schema(description = "管理后台 - ERP 库存统计卡片 Response VO")
@Data
public class ErpStockStatisticsRespVO {
@Schema(description = "产品品库存")
private ProductStockCard productStock;
@Schema(description = "原材料库存")
private UnitGroupedStockCard materialStock;
@Schema(description = "备件库存")
private UnitGroupedStockCard sparePartStock;
@Schema(description = "今日入库")
private TodayInCard todayIn;
@Schema(description = "今日出库")
private TodayOutCard todayOut;
@Schema(description = "产品品库存卡片")
@Data
public static class ProductStockCard {
@Schema(description = "SKU 数", example = "36")
private Long skuCount;
@Schema(description = "库存摘要", example = "128托42包560个")
private String stockDisplay;
@Schema(description = "今日完工入库", example = "12托8包")
private BigDecimal totalCount;
private String unitName;
private List<ProductPackagingStock> packagingStocks;
private String todayFinishedInDisplay;
}
@Schema(description = "产成品包装方案库存明细")
@Data
public static class ProductPackagingStock {
private Long productId;
private String productName;
private Long packagingSchemeId;
private String packagingSchemeName;
private BigDecimal totalCount;
private String unitName;
private String stockDisplay;
private String packagingDisplay;
private BigDecimal packageQuantity;
private BigDecimal palletPackageQuantity;
private BigDecimal palletTotalQuantity;
}
@Schema(description = "按单位分组库存卡片")
@Data
public static class UnitGroupedStockCard {
@Schema(description = "SKU/种类数", example = "24")
private Long skuCount;
@Schema(description = "库存摘要", example = "18.6吨 / 1250L / 320包")
private String stockDisplay;
@Schema(description = "按库存单位分组明细")
private List<UnitStock> unitStocks;
}
@Schema(description = "库存单位分组明细")
@Data
public static class UnitStock {
@Schema(description = "库存单位编号", example = "3")
private Long unitId;
@Schema(description = "库存单位名称", example = "kg")
private String unitName;
@Schema(description = "库存数量", example = "820")
private BigDecimal count;
@Schema(description = "库存展示", example = "820kg")
private String stockDisplay;
}
@Schema(description = "今日入库卡片")
@Data
public static class TodayInCard {
@Schema(description = "入库单数", example = "18")
private Long orderCount;
@Schema(description = "明细行数", example = "46")
private Long itemCount;
@Schema(description = "产品品入库")
private ProductStockCard productStock;
@Schema(description = "原材料入库")
private UnitGroupedStockCard materialStock;
@Schema(description = "备件入库")
private UnitGroupedStockCard sparePartStock;
}
@Schema(description = "今日出库卡片")
@Data
public static class TodayOutCard {
@Schema(description = "出库单数", example = "15")
private Long orderCount;
@Schema(description = "明细行数", example = "38")
private Long itemCount;
@Schema(description = "原材料领用")
private UnitGroupedStockCard materialStock;
@Schema(description = "备件领用")
private UnitGroupedStockCard sparePartStock;
@Schema(description = "产品品发货")
private ProductStockCard productStock;
}
}

@ -70,6 +70,10 @@ public class ErpStockInItemDO extends BaseDO {
* {@link ErpProductDO#getUnitId()}
*/
private Long productUnitId;
/**
*
*/
private String productUnitName;
/**
*
*/

@ -101,6 +101,10 @@ public class ErpStockOutItemDO extends BaseDO {
* {@link ErpProductDO#getUnitId()}
*/
private Long productUnitId;
/**
*
*/
private String productUnitName;
/**
*
*/

@ -556,6 +556,7 @@ public class ErpStockInServiceImpl implements ErpStockInService {
.setProductName(productMap.get(target.getProductId()).getName())
.setSupplierName(resolveStockInItemSupplierName(item, supplierMap, inType))
.setProductUnitId(productMap.get(target.getProductId()).getUnitId())
.setProductUnitName(productMap.get(target.getProductId()).getUnitName())
.setTotalPrice(MoneyUtils.priceMultiply(target.getProductPrice(), target.getCount()))));
}

@ -640,6 +640,7 @@ public class ErpStockOutServiceImpl implements ErpStockOutService {
.setAreaName(areaMap.containsKey(target.getAreaId()) ? areaMap.get(target.getAreaId()).getAreaName() : null)
.setProductName(productMap.get(target.getProductId()).getName())
.setProductUnitId(productMap.get(target.getProductId()).getUnitId())
.setProductUnitName(productMap.get(target.getProductId()).getUnitName())
.setTotalPrice(MoneyUtils.priceMultiply(target.getProductPrice(), target.getCount()))
.setOutUsageType(item.getOutUsageType())
.setOutUsageTypeName(ErpStockOutUsageTypeEnum.getNameByType(item.getOutUsageType()))

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.erp.service.stock;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockStatisticsRespVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;
import java.math.BigDecimal;
@ -61,6 +62,8 @@ public interface ErpStockService {
*/
PageResult<ErpStockDO> getStockPage(ErpStockPageReqVO pageReqVO);
ErpStockStatisticsRespVO getStockStatistics();
/**
*
*

@ -4,17 +4,37 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductPackagingSchemeRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockStatisticsRespVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInItemDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutItemDO;
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockInItemMapper;
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockInMapper;
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockMapper;
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockOutItemMapper;
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockOutMapper;
import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;
import cn.iocoder.yudao.module.erp.framework.bean.ProductTypeEnum;
import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_COUNT_NEGATIVE;
@ -42,6 +62,14 @@ public class ErpStockServiceImpl implements ErpStockService {
@Resource
private ErpStockMapper stockMapper;
@Resource
private ErpStockInMapper stockInMapper;
@Resource
private ErpStockInItemMapper stockInItemMapper;
@Resource
private ErpStockOutMapper stockOutMapper;
@Resource
private ErpStockOutItemMapper stockOutItemMapper;
@Override
public ErpStockDO getStock(Long id) {
@ -93,6 +121,21 @@ public class ErpStockServiceImpl implements ErpStockService {
return stockMapper.selectPage(pageReqVO);
}
@Override
public ErpStockStatisticsRespVO getStockStatistics() {
ErpStockStatisticsRespVO respVO = new ErpStockStatisticsRespVO();
List<ErpStockDO> stockList = stockMapper.selectList(new LambdaQueryWrapper<ErpStockDO>());
Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(stockList.stream()
.map(ErpStockDO::getProductId).collect(Collectors.toSet()));
fillStockCards(respVO, stockList, productMap);
LocalDateTime beginTime = LocalDate.now().atStartOfDay();
LocalDateTime endTime = beginTime.plusDays(1);
fillTodayInCard(respVO, beginTime, endTime, productMap);
fillTodayOutCard(respVO, beginTime, endTime, productMap);
return respVO;
}
@Override
public BigDecimal updateStockCountIncrement(Long categoryId, Long productId, Long warehouseId, Long areaId,
String areaName, BigDecimal count) {
@ -140,6 +183,480 @@ public class ErpStockServiceImpl implements ErpStockService {
return stock.getCount().add(count);
}
private void fillStockCards(ErpStockStatisticsRespVO respVO, List<ErpStockDO> stockList,
Map<Long, ErpProductRespVO> productMap) {
List<ErpStockDO> productStockList = filterStockByCategory(stockList, productMap, ProductTypeEnum.ITEM.getTypeId());
List<ErpStockDO> materialStockList = filterStockByCategory(stockList, productMap, ProductTypeEnum.PRODUCT.getTypeId());
List<ErpStockDO> sparePartStockList = filterStockByCategory(stockList, productMap, ProductTypeEnum.BEIJIAN.getTypeId());
respVO.setProductStock(buildProductStockCard(productStockList, productMap));
respVO.setMaterialStock(buildUnitGroupedStockCard(materialStockList));
respVO.setSparePartStock(buildUnitGroupedStockCard(sparePartStockList));
}
private void fillTodayInCard(ErpStockStatisticsRespVO respVO, LocalDateTime beginTime, LocalDateTime endTime,
Map<Long, ErpProductRespVO> stockProductMap) {
ErpStockStatisticsRespVO.TodayInCard todayInCard = new ErpStockStatisticsRespVO.TodayInCard();
List<ErpStockInDO> stockInList = stockInMapper.selectList(new LambdaQueryWrapper<ErpStockInDO>()
.eq(ErpStockInDO::getStatus, ErpAuditStatus.APPROVE.getStatus())
.between(ErpStockInDO::getInTime, beginTime, endTime));
List<Long> stockInIds = stockInList.stream().map(ErpStockInDO::getId).collect(Collectors.toList());
List<ErpStockInItemDO> itemList = stockInIds.isEmpty() ? Collections.emptyList()
: stockInItemMapper.selectListByInIds(stockInIds);
Map<Long, ErpProductRespVO> productMap = buildProductMap(itemList.stream()
.map(ErpStockInItemDO::getProductId).collect(Collectors.toSet()), stockProductMap);
todayInCard.setOrderCount((long) stockInIds.size());
todayInCard.setItemCount((long) itemList.size());
todayInCard.setProductStock(buildInItemProductStockCard(filterInItemByCategory(itemList, productMap, ProductTypeEnum.ITEM.getTypeId()), productMap));
todayInCard.setMaterialStock(buildInItemUnitGroupedStockCard(filterInItemByCategory(itemList, productMap, ProductTypeEnum.PRODUCT.getTypeId()), productMap));
todayInCard.setSparePartStock(buildInItemUnitGroupedStockCard(filterInItemByCategory(itemList, productMap, ProductTypeEnum.BEIJIAN.getTypeId()), productMap));
respVO.setTodayIn(todayInCard);
respVO.getProductStock().setTodayFinishedInDisplay(todayInCard.getProductStock().getStockDisplay());
}
private void fillTodayOutCard(ErpStockStatisticsRespVO respVO, LocalDateTime beginTime, LocalDateTime endTime,
Map<Long, ErpProductRespVO> stockProductMap) {
ErpStockStatisticsRespVO.TodayOutCard todayOutCard = new ErpStockStatisticsRespVO.TodayOutCard();
List<ErpStockOutDO> stockOutList = stockOutMapper.selectList(new LambdaQueryWrapper<ErpStockOutDO>()
.eq(ErpStockOutDO::getStatus, ErpAuditStatus.APPROVE.getStatus())
.between(ErpStockOutDO::getOutTime, beginTime, endTime));
List<Long> stockOutIds = stockOutList.stream().map(ErpStockOutDO::getId).collect(Collectors.toList());
List<ErpStockOutItemDO> itemList = stockOutIds.isEmpty() ? Collections.emptyList()
: stockOutItemMapper.selectListByOutIds(stockOutIds);
Map<Long, ErpProductRespVO> productMap = buildProductMap(itemList.stream()
.map(ErpStockOutItemDO::getProductId).collect(Collectors.toSet()), stockProductMap);
todayOutCard.setOrderCount((long) stockOutIds.size());
todayOutCard.setItemCount((long) itemList.size());
todayOutCard.setProductStock(buildOutItemProductStockCard(filterOutItemByCategory(itemList, productMap, ProductTypeEnum.ITEM.getTypeId()), productMap));
todayOutCard.setMaterialStock(buildOutItemUnitGroupedStockCard(filterOutItemByCategory(itemList, productMap, ProductTypeEnum.PRODUCT.getTypeId()), productMap));
todayOutCard.setSparePartStock(buildOutItemUnitGroupedStockCard(filterOutItemByCategory(itemList, productMap, ProductTypeEnum.BEIJIAN.getTypeId()), productMap));
respVO.setTodayOut(todayOutCard);
}
private Map<Long, ErpProductRespVO> buildProductMap(Set<Long> productIds, Map<Long, ErpProductRespVO> stockProductMap) {
Set<Long> missingProductIds = new HashSet<>(productIds);
missingProductIds.removeAll(stockProductMap.keySet());
if (missingProductIds.isEmpty()) {
return stockProductMap;
}
Map<Long, ErpProductRespVO> resultMap = productService.getProductVOMap(productIds);
resultMap.putAll(stockProductMap);
return resultMap;
}
private List<ErpStockDO> filterStockByCategory(List<ErpStockDO> stockList, Map<Long, ErpProductRespVO> productMap,
Integer categoryType) {
return stockList.stream()
.filter(stock -> categoryType.equals(resolveCategoryType(stock, productMap)))
.collect(Collectors.toList());
}
private List<ErpStockInItemDO> filterInItemByCategory(List<ErpStockInItemDO> itemList,
Map<Long, ErpProductRespVO> productMap,
Integer categoryType) {
return itemList.stream()
.filter(item -> categoryType.equals(resolveCategoryType(item.getProductId(), productMap)))
.collect(Collectors.toList());
}
private List<ErpStockOutItemDO> filterOutItemByCategory(List<ErpStockOutItemDO> itemList,
Map<Long, ErpProductRespVO> productMap,
Integer categoryType) {
return itemList.stream()
.filter(item -> categoryType.equals(resolveCategoryType(item.getProductId(), productMap)))
.collect(Collectors.toList());
}
private ErpStockStatisticsRespVO.UnitGroupedStockCard buildUnitGroupedStockCard(List<ErpStockDO> stockList) {
ErpStockStatisticsRespVO.UnitGroupedStockCard card = new ErpStockStatisticsRespVO.UnitGroupedStockCard();
card.setSkuCount(countDistinctProduct(stockList));
List<ErpStockStatisticsRespVO.UnitStock> unitStocks = stockList.stream()
.collect(Collectors.groupingBy(stock -> buildUnitKey(stock.getUnitId(), stock.getUnitName())))
.values().stream()
.map(this::buildUnitStock)
.collect(Collectors.toList());
card.setUnitStocks(unitStocks);
card.setStockDisplay(unitStocks.stream()
.map(ErpStockStatisticsRespVO.UnitStock::getStockDisplay)
.collect(Collectors.joining(" / ")));
return card;
}
private ErpStockStatisticsRespVO.UnitStock buildUnitStock(List<ErpStockDO> stockList) {
ErpStockDO firstStock = stockList.get(0);
BigDecimal count = stockList.stream().map(stock -> defaultZero(stock.getCount()))
.reduce(BigDecimal.ZERO, BigDecimal::add);
return buildUnitStock(firstStock.getUnitId(), firstStock.getUnitName(), count);
}
private ErpStockStatisticsRespVO.ProductStockCard buildProductStockCard(List<ErpStockDO> stockList,
Map<Long, ErpProductRespVO> productMap) {
ErpStockStatisticsRespVO.ProductStockCard card = new ErpStockStatisticsRespVO.ProductStockCard();
BigDecimal totalCount = sumStockCount(stockList);
String unitName = resolveStockUnitName(stockList, productMap);
card.setSkuCount(countDistinctProduct(stockList));
card.setTotalCount(totalCount);
card.setUnitName(unitName);
card.setStockDisplay(formatNumber(totalCount) + nullToEmpty(unitName));
card.setPackagingStocks(buildStockPackagingStocks(stockList, productMap));
card.setTodayFinishedInDisplay(formatNumber(BigDecimal.ZERO) + nullToEmpty(unitName));
return card;
}
private ErpStockStatisticsRespVO.ProductStockCard buildInItemProductStockCard(List<ErpStockInItemDO> itemList,
Map<Long, ErpProductRespVO> productMap) {
ErpStockStatisticsRespVO.ProductStockCard card = new ErpStockStatisticsRespVO.ProductStockCard();
BigDecimal totalCount = sumInItemCount(itemList);
String unitName = resolveInItemUnitName(itemList, productMap);
card.setSkuCount(itemList.stream().map(ErpStockInItemDO::getProductId).distinct().count());
card.setTotalCount(totalCount);
card.setUnitName(unitName);
card.setStockDisplay(formatNumber(totalCount) + nullToEmpty(unitName));
card.setPackagingStocks(buildInItemPackagingStocks(itemList, productMap));
return card;
}
private ErpStockStatisticsRespVO.ProductStockCard buildOutItemProductStockCard(List<ErpStockOutItemDO> itemList,
Map<Long, ErpProductRespVO> productMap) {
ErpStockStatisticsRespVO.ProductStockCard card = new ErpStockStatisticsRespVO.ProductStockCard();
BigDecimal totalCount = sumOutItemCount(itemList);
String unitName = resolveOutItemUnitName(itemList, productMap);
card.setSkuCount(itemList.stream().map(ErpStockOutItemDO::getProductId).distinct().count());
card.setTotalCount(totalCount);
card.setUnitName(unitName);
card.setStockDisplay(formatNumber(totalCount) + nullToEmpty(unitName));
card.setPackagingStocks(buildOutItemPackagingStocks(itemList, productMap));
return card;
}
private String buildUnitKey(Long unitId, String unitName) {
return String.valueOf(unitId) + "_" + String.valueOf(unitName);
}
private ErpStockStatisticsRespVO.UnitGroupedStockCard buildInItemUnitGroupedStockCard(List<ErpStockInItemDO> itemList,
Map<Long, ErpProductRespVO> productMap) {
ErpStockStatisticsRespVO.UnitGroupedStockCard card = new ErpStockStatisticsRespVO.UnitGroupedStockCard();
card.setSkuCount(itemList.stream().map(ErpStockInItemDO::getProductId).distinct().count());
List<ErpStockStatisticsRespVO.UnitStock> unitStocks = itemList.stream()
.collect(Collectors.groupingBy(item -> buildInItemUnitKey(item, productMap)))
.values().stream()
.map(items -> buildInItemUnitStock(items, productMap))
.collect(Collectors.toList());
card.setUnitStocks(unitStocks);
card.setStockDisplay(unitStocks.stream()
.map(ErpStockStatisticsRespVO.UnitStock::getStockDisplay)
.collect(Collectors.joining(" / ")));
return card;
}
private ErpStockStatisticsRespVO.UnitGroupedStockCard buildOutItemUnitGroupedStockCard(List<ErpStockOutItemDO> itemList,
Map<Long, ErpProductRespVO> productMap) {
ErpStockStatisticsRespVO.UnitGroupedStockCard card = new ErpStockStatisticsRespVO.UnitGroupedStockCard();
card.setSkuCount(itemList.stream().map(ErpStockOutItemDO::getProductId).distinct().count());
List<ErpStockStatisticsRespVO.UnitStock> unitStocks = itemList.stream()
.collect(Collectors.groupingBy(item -> buildOutItemUnitKey(item, productMap)))
.values().stream()
.map(items -> buildOutItemUnitStock(items, productMap))
.collect(Collectors.toList());
card.setUnitStocks(unitStocks);
card.setStockDisplay(unitStocks.stream()
.map(ErpStockStatisticsRespVO.UnitStock::getStockDisplay)
.collect(Collectors.joining(" / ")));
return card;
}
private ErpStockStatisticsRespVO.UnitStock buildInItemUnitStock(List<ErpStockInItemDO> itemList,
Map<Long, ErpProductRespVO> productMap) {
ErpStockInItemDO firstItem = itemList.get(0);
BigDecimal count = itemList.stream().map(item -> defaultZero(item.getCount()))
.reduce(BigDecimal.ZERO, BigDecimal::add);
return buildUnitStock(resolveInItemUnitId(firstItem, productMap), resolveInItemUnitName(firstItem, productMap), count);
}
private ErpStockStatisticsRespVO.UnitStock buildOutItemUnitStock(List<ErpStockOutItemDO> itemList,
Map<Long, ErpProductRespVO> productMap) {
ErpStockOutItemDO firstItem = itemList.get(0);
BigDecimal count = itemList.stream().map(item -> defaultZero(item.getCount()))
.reduce(BigDecimal.ZERO, BigDecimal::add);
return buildUnitStock(resolveOutItemUnitId(firstItem, productMap), resolveOutItemUnitName(firstItem, productMap), count);
}
private ErpStockStatisticsRespVO.UnitStock buildUnitStock(Long unitId, String unitName, BigDecimal count) {
ErpStockStatisticsRespVO.UnitStock unitStock = new ErpStockStatisticsRespVO.UnitStock();
unitStock.setUnitId(unitId);
unitStock.setUnitName(unitName);
unitStock.setCount(count);
unitStock.setStockDisplay(formatNumber(count) + nullToEmpty(unitName));
return unitStock;
}
private String buildInItemUnitKey(ErpStockInItemDO item, Map<Long, ErpProductRespVO> productMap) {
return buildUnitKey(resolveInItemUnitId(item, productMap), resolveInItemUnitName(item, productMap));
}
private String buildOutItemUnitKey(ErpStockOutItemDO item, Map<Long, ErpProductRespVO> productMap) {
return buildUnitKey(resolveOutItemUnitId(item, productMap), resolveOutItemUnitName(item, productMap));
}
private Long resolveInItemUnitId(ErpStockInItemDO item, Map<Long, ErpProductRespVO> productMap) {
if (item.getProductUnitId() != null) {
return item.getProductUnitId();
}
ErpProductRespVO product = productMap.get(item.getProductId());
return product != null ? product.getUnitId() : null;
}
private String resolveInItemUnitName(ErpStockInItemDO item, Map<Long, ErpProductRespVO> productMap) {
if (item.getProductUnitName() != null) {
return item.getProductUnitName();
}
return resolveCurrentProductUnitName(item.getProductId(), item.getProductUnitId(), productMap);
}
private Long resolveOutItemUnitId(ErpStockOutItemDO item, Map<Long, ErpProductRespVO> productMap) {
if (item.getProductUnitId() != null) {
return item.getProductUnitId();
}
ErpProductRespVO product = productMap.get(item.getProductId());
return product != null ? product.getUnitId() : null;
}
private String resolveOutItemUnitName(ErpStockOutItemDO item, Map<Long, ErpProductRespVO> productMap) {
if (item.getProductUnitName() != null) {
return item.getProductUnitName();
}
return resolveCurrentProductUnitName(item.getProductId(), item.getProductUnitId(), productMap);
}
private String resolveCurrentProductUnitName(Long productId, Long unitId, Map<Long, ErpProductRespVO> productMap) {
ErpProductRespVO product = productMap.get(productId);
if (product == null || (unitId != null && !Objects.equals(unitId, product.getUnitId()))) {
return null;
}
return product.getUnitName();
}
private List<ErpStockStatisticsRespVO.ProductPackagingStock> buildStockPackagingStocks(List<ErpStockDO> stockList,
Map<Long, ErpProductRespVO> productMap) {
return stockList.stream()
.collect(Collectors.groupingBy(this::buildStockPackagingKey))
.values().stream()
.map(items -> buildStockPackagingStock(items, productMap))
.collect(Collectors.toList());
}
private ErpStockStatisticsRespVO.ProductPackagingStock buildStockPackagingStock(List<ErpStockDO> stockList,
Map<Long, ErpProductRespVO> productMap) {
ErpStockDO firstStock = stockList.get(0);
BigDecimal totalCount = sumStockCount(stockList);
String unitName = firstStock.getUnitName();
ErpStockStatisticsRespVO.ProductPackagingStock detail = buildProductPackagingStock(
firstStock.getProductId(), resolveProductName(firstStock.getProductId(), productMap),
firstStock.getDefaultPackagingSchemeId(), firstStock.getDefaultPackagingSchemeName(),
totalCount, unitName, firstStock.getPackageQuantity(), firstStock.getPalletPackageQuantity(),
firstStock.getPalletTotalQuantity());
return detail;
}
private List<ErpStockStatisticsRespVO.ProductPackagingStock> buildInItemPackagingStocks(List<ErpStockInItemDO> itemList,
Map<Long, ErpProductRespVO> productMap) {
return itemList.stream()
.collect(Collectors.groupingBy(this::buildInItemPackagingKey))
.values().stream()
.map(items -> buildInItemPackagingStock(items, productMap))
.collect(Collectors.toList());
}
private ErpStockStatisticsRespVO.ProductPackagingStock buildInItemPackagingStock(List<ErpStockInItemDO> itemList,
Map<Long, ErpProductRespVO> productMap) {
ErpStockInItemDO firstItem = itemList.get(0);
BigDecimal totalCount = sumInItemCount(itemList);
return buildProductPackagingStock(firstItem.getProductId(), resolveProductName(firstItem.getProductId(), productMap),
firstItem.getPackagingSchemeId(), firstItem.getPackagingSchemeName(), totalCount,
resolveInItemUnitName(firstItem, productMap), firstItem.getPackageQuantity(),
firstItem.getPalletPackageQuantity(), firstItem.getPalletTotalQuantity());
}
private List<ErpStockStatisticsRespVO.ProductPackagingStock> buildOutItemPackagingStocks(List<ErpStockOutItemDO> itemList,
Map<Long, ErpProductRespVO> productMap) {
return itemList.stream()
.collect(Collectors.groupingBy(this::buildOutItemPackagingKey))
.values().stream()
.map(items -> buildOutItemPackagingStock(items, productMap))
.collect(Collectors.toList());
}
private ErpStockStatisticsRespVO.ProductPackagingStock buildOutItemPackagingStock(List<ErpStockOutItemDO> itemList,
Map<Long, ErpProductRespVO> productMap) {
ErpStockOutItemDO firstItem = itemList.get(0);
BigDecimal totalCount = sumOutItemCount(itemList);
return buildProductPackagingStock(firstItem.getProductId(), resolveProductName(firstItem.getProductId(), productMap),
firstItem.getPackagingSchemeId(), firstItem.getPackagingSchemeName(), totalCount,
resolveOutItemUnitName(firstItem, productMap), firstItem.getPackageQuantity(),
firstItem.getPalletPackageQuantity(), firstItem.getPalletTotalQuantity());
}
private ErpStockStatisticsRespVO.ProductPackagingStock buildProductPackagingStock(Long productId, String productName,
Long packagingSchemeId, String packagingSchemeName,
BigDecimal totalCount, String unitName,
BigDecimal packageQuantity,
BigDecimal palletPackageQuantity,
BigDecimal palletTotalQuantity) {
ErpStockStatisticsRespVO.ProductPackagingStock detail = new ErpStockStatisticsRespVO.ProductPackagingStock();
detail.setProductId(productId);
detail.setProductName(productName);
detail.setPackagingSchemeId(packagingSchemeId);
detail.setPackagingSchemeName(packagingSchemeName);
detail.setTotalCount(totalCount);
detail.setUnitName(unitName);
detail.setStockDisplay(formatNumber(totalCount) + nullToEmpty(unitName));
detail.setPackagingDisplay(formatProductQuantity(calculateProductQuantity(totalCount, palletTotalQuantity, packageQuantity)));
detail.setPackageQuantity(packageQuantity);
detail.setPalletPackageQuantity(palletPackageQuantity);
detail.setPalletTotalQuantity(palletTotalQuantity);
return detail;
}
private String buildStockPackagingKey(ErpStockDO stock) {
return buildProductPackagingKey(stock.getProductId(), stock.getDefaultPackagingSchemeId(), stock.getPackageQuantity(),
stock.getPalletPackageQuantity(), stock.getPalletTotalQuantity(), stock.getUnitId(), stock.getUnitName());
}
private String buildInItemPackagingKey(ErpStockInItemDO item) {
return buildProductPackagingKey(item.getProductId(), item.getPackagingSchemeId(), item.getPackageQuantity(),
item.getPalletPackageQuantity(), item.getPalletTotalQuantity(), item.getProductUnitId(), item.getProductUnitName());
}
private String buildOutItemPackagingKey(ErpStockOutItemDO item) {
return buildProductPackagingKey(item.getProductId(), item.getPackagingSchemeId(), item.getPackageQuantity(),
item.getPalletPackageQuantity(), item.getPalletTotalQuantity(), item.getProductUnitId(), item.getProductUnitName());
}
private String buildProductPackagingKey(Long productId, Long packagingSchemeId, BigDecimal packageQuantity,
BigDecimal palletPackageQuantity, BigDecimal palletTotalQuantity,
Long unitId, String unitName) {
return String.valueOf(productId) + "_" + String.valueOf(packagingSchemeId) + "_"
+ String.valueOf(packageQuantity) + "_" + String.valueOf(palletPackageQuantity) + "_"
+ String.valueOf(palletTotalQuantity) + "_" + buildUnitKey(unitId, unitName);
}
private BigDecimal sumStockCount(List<ErpStockDO> stockList) {
return stockList.stream().map(stock -> defaultZero(stock.getCount()))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
private BigDecimal sumInItemCount(List<ErpStockInItemDO> itemList) {
return itemList.stream().map(item -> defaultZero(item.getCount()))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
private BigDecimal sumOutItemCount(List<ErpStockOutItemDO> itemList) {
return itemList.stream().map(item -> defaultZero(item.getCount()))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
private String resolveStockUnitName(List<ErpStockDO> stockList, Map<Long, ErpProductRespVO> productMap) {
return stockList.stream().map(ErpStockDO::getUnitName).filter(Objects::nonNull).findFirst()
.orElseGet(() -> stockList.stream().map(stock -> resolveCurrentProductUnitName(stock.getProductId(), stock.getUnitId(), productMap))
.filter(Objects::nonNull).findFirst().orElse(null));
}
private String resolveInItemUnitName(List<ErpStockInItemDO> itemList, Map<Long, ErpProductRespVO> productMap) {
return itemList.stream().map(item -> resolveInItemUnitName(item, productMap)).filter(Objects::nonNull).findFirst().orElse(null);
}
private String resolveOutItemUnitName(List<ErpStockOutItemDO> itemList, Map<Long, ErpProductRespVO> productMap) {
return itemList.stream().map(item -> resolveOutItemUnitName(item, productMap)).filter(Objects::nonNull).findFirst().orElse(null);
}
private String resolveProductName(Long productId, Map<Long, ErpProductRespVO> productMap) {
ErpProductRespVO product = productMap.get(productId);
return product != null ? product.getName() : null;
}
private Integer resolveCategoryType(ErpStockDO stock, Map<Long, ErpProductRespVO> productMap) {
if (stock.getCategoryType() != null) {
return stock.getCategoryType();
}
return resolveCategoryType(stock.getProductId(), productMap);
}
private Integer resolveCategoryType(Long productId, Map<Long, ErpProductRespVO> productMap) {
ErpProductRespVO product = productMap.get(productId);
return product != null ? product.getCategoryType() : null;
}
private Long countDistinctProduct(List<ErpStockDO> stockList) {
return stockList.stream().map(ErpStockDO::getProductId).distinct().count();
}
private String formatProductStockDisplay(List<ErpStockDO> stockList) {
BigDecimal palletCount = BigDecimal.ZERO;
BigDecimal packageCount = BigDecimal.ZERO;
BigDecimal pieceCount = BigDecimal.ZERO;
for (ErpStockDO stock : stockList) {
ProductQuantity quantity = calculateProductQuantity(stock.getCount(), stock.getPalletTotalQuantity(), stock.getPackageQuantity());
palletCount = palletCount.add(quantity.getPalletCount());
packageCount = packageCount.add(quantity.getPackageCount());
pieceCount = pieceCount.add(quantity.getPieceCount());
}
return formatProductQuantity(palletCount, packageCount, pieceCount);
}
private ProductQuantity calculateProductQuantity(BigDecimal count, BigDecimal palletTotalQuantity, BigDecimal packageQuantity) {
BigDecimal safeCount = defaultZero(count);
BigDecimal palletCount = BigDecimal.ZERO;
BigDecimal packageCount = BigDecimal.ZERO;
BigDecimal pieceCount = safeCount;
if (palletTotalQuantity != null && palletTotalQuantity.compareTo(BigDecimal.ZERO) > 0) {
palletCount = safeCount.divideToIntegralValue(palletTotalQuantity);
pieceCount = safeCount.remainder(palletTotalQuantity);
}
if (packageQuantity != null && packageQuantity.compareTo(BigDecimal.ZERO) > 0) {
packageCount = pieceCount.divideToIntegralValue(packageQuantity);
pieceCount = pieceCount.remainder(packageQuantity);
}
return new ProductQuantity(palletCount, packageCount, pieceCount);
}
private String formatProductQuantity(BigDecimal palletCount, BigDecimal packageCount, BigDecimal pieceCount) {
StringBuilder display = new StringBuilder();
appendDisplay(display, palletCount, "托", false);
appendDisplay(display, packageCount, "包", false);
appendDisplay(display, pieceCount, "个", display.length() == 0);
return display.toString();
}
private String formatProductQuantity(ProductQuantity quantity) {
return formatProductQuantity(quantity.getPalletCount(), quantity.getPackageCount(), quantity.getPieceCount());
}
private void appendDisplay(StringBuilder display, BigDecimal quantity, String unitName, boolean keepZero) {
if (quantity == null || quantity.compareTo(BigDecimal.ZERO) <= 0) {
if (!keepZero) {
return;
}
display.append("0").append(unitName);
return;
}
display.append(formatNumber(quantity)).append(unitName);
}
private BigDecimal defaultZero(BigDecimal value) {
return value != null ? value : BigDecimal.ZERO;
}
private String nullToEmpty(String value) {
return value != null ? value : "";
}
private String formatNumber(BigDecimal value) {
return value == null ? "0" : value.stripTrailingZeros().toPlainString();
}
private void fillStockSnapshot(ErpStockDO stock, ErpProductRespVO product,
ProductPackagingSchemeRespVO defaultPackagingScheme) {
if (product == null) {
@ -196,4 +713,16 @@ public class ErpStockServiceImpl implements ErpStockService {
.setPalletTotalQuantity(stock.getPalletTotalQuantity());
}
@lombok.Data
@lombok.AllArgsConstructor
private static class ProductQuantity {
private BigDecimal palletCount;
private BigDecimal packageCount;
private BigDecimal pieceCount;
}
}

@ -27,6 +27,9 @@ public class DevicePageReqVO extends PageParam {
@Schema(description = "状态", example = "1")
private String status;
@Schema(description = "运行状态", example = "1")
private String operatingStatus;
@Schema(description = "读主题")
private String readTopic;
@ -80,4 +83,4 @@ public class DevicePageReqVO extends PageParam {
@Schema(description = "mqtt订阅主题")
private String topic;
}
}

@ -84,6 +84,7 @@ public interface DeviceMapper extends BaseMapperX<DeviceDO> {
return selectList(new LambdaQueryWrapperX<DeviceDO>()
.eqIfPresent(DeviceDO::getDeviceCode, reqVO.getDeviceCode())
.likeIfPresent(DeviceDO::getDeviceName, reqVO.getDeviceName())
.likeIfPresent(DeviceDO::getTopic, reqVO.getTopic())
.eqIfPresent(DeviceDO::getDeviceType, reqVO.getDeviceType())
.eqIfPresent(DeviceDO::getStatus, reqVO.getStatus())
.eqIfPresent(DeviceDO::getReadTopic, reqVO.getReadTopic())
@ -94,6 +95,7 @@ public interface DeviceMapper extends BaseMapperX<DeviceDO> {
.betweenIfPresent(DeviceDO::getLastOnlineTime, reqVO.getLastOnlineTime())
.eqIfPresent(DeviceDO::getRemark, reqVO.getRemark())
.eqIfPresent(DeviceDO::getIsEnable, reqVO.getIsEnable())
.betweenIfPresent(DeviceDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(DeviceDO::getId));
}

@ -47,7 +47,7 @@ public interface DeviceModelAttributeMapper extends BaseMapperX<DeviceModelAttri
.eqIfPresent(DeviceModelAttributeDO::getDataUnit, reqVO.getDataUnit())
.eqIfPresent(DeviceModelAttributeDO::getRatio, reqVO.getRatio())
.eqIfPresent(DeviceModelAttributeDO::getRemark, reqVO.getRemark())
.eqIfPresent(DeviceModelAttributeDO::getDeviceModelId, reqVO.getId())
.eqIfPresent(DeviceModelAttributeDO::getDeviceModelId, reqVO.getDeviceModelId())
.betweenIfPresent(DeviceModelAttributeDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(DeviceModelAttributeDO::getId);

@ -47,6 +47,7 @@ import cn.iocoder.yudao.module.iot.dal.mysql.mqttdatarecord.MqttDataRecordMapper
import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceAttributeDO;
import cn.iocoder.yudao.module.iot.dal.mysql.device.DeviceAttributeMapper;
import cn.iocoder.yudao.module.iot.framework.mqtt.config.DefaultBizTopicSet;
import cn.iocoder.yudao.module.iot.framework.mqtt.consumer.IMqttservice;
import cn.iocoder.yudao.module.iot.util.MapListStatsCalculator;
import com.alibaba.fastjson.JSON;
@ -139,6 +140,9 @@ public class DeviceServiceImpl implements DeviceService {
@Resource
private IMqttservice mqttService;
@Resource
private DefaultBizTopicSet defaultBizTopicSet;
@Resource
private ErpProductUnitMapper productUnitMapper;
@ -273,24 +277,59 @@ public class DeviceServiceImpl implements DeviceService {
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateDevice(DeviceSaveReqVO updateReqVO) {
// 校验存在
validateDeviceExists(updateReqVO.getId());
//校驗topic是否唯一
validTopicExists(updateReqVO.getTopic());
validTopicExists(updateReqVO.getTopic(), updateReqVO.getId());
// 更新
DeviceDO updateObj = BeanUtils.toBean(updateReqVO, DeviceDO.class);
deviceMapper.updateById(updateObj);
}
private void validTopicExists(String topic) {
boolean exists = deviceMapper.exists(Wrappers.<DeviceDO>lambdaQuery().eq(DeviceDO::getTopic, topic));
private void validTopicExists(String topic, Long currentDeviceId) {
if (StringUtils.isBlank(topic)) {
return;
}
boolean exists = deviceMapper.exists(Wrappers.<DeviceDO>lambdaQuery()
.eq(DeviceDO::getTopic, topic)
.ne(currentDeviceId != null, DeviceDO::getId, currentDeviceId));
if (exists){
throw exception(DEVICE_MQTT_TOPIC_ALREADY_EXIST);
}
}
private void disableGatewayByDevice(DeviceDO device) {
if (device == null) {
return;
}
LambdaUpdateWrapper<GatewayDO> updateWrapper = new LambdaUpdateWrapper<GatewayDO>()
.set(GatewayDO::getIsEnable, false)
.set(GatewayDO::getUpdateTime, LocalDateTime.now());
if (StringUtils.isNotBlank(device.getTopic())) {
updateWrapper.eq(GatewayDO::getTopic, device.getTopic());
} else if (StringUtils.isNotBlank(device.getDeviceCode())) {
updateWrapper.eq(GatewayDO::getGatewayCode, device.getDeviceCode());
} else {
return;
}
if (StringUtils.isNotBlank(device.getDeviceCode())) {
updateWrapper.or().eq(GatewayDO::getGatewayCode, device.getDeviceCode());
}
gatewayMapper.update(null, updateWrapper);
}
private void removeRuntimeTopic(String topic) {
if (StringUtils.isBlank(topic) || defaultBizTopicSet == null || defaultBizTopicSet.getTopicMap() == null) {
return;
}
synchronized (defaultBizTopicSet.getTopicMap()) {
defaultBizTopicSet.getTopicMap().removeIf(item -> topic.equals(item.getSubTopic()));
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteDevice(List<Long> ids) {
@ -323,10 +362,12 @@ public class DeviceServiceImpl implements DeviceService {
deviceContactModelMapper.delete(deviceContactModelDOLambdaQueryWrapper);
}
private void validateDeviceExists(Long id) {
if (deviceMapper.selectById(id) == null) {
private DeviceDO validateDeviceExists(Long id) {
DeviceDO device = deviceMapper.selectById(id);
if (device == null) {
throw exception(DEVICE_NOT_EXISTS);
}
return device;
}
@Override
@ -357,6 +398,10 @@ public class DeviceServiceImpl implements DeviceService {
public PageResult<DeviceRespVO> getDevicePage(DevicePageReqVO pageReqVO) {
// 1. 查询分页设备
if (StringUtils.isNotBlank(pageReqVO.getOperatingStatus())) {
return getDevicePageByOperatingStatus(pageReqVO);
}
PageResult<DeviceDO> deviceDOPageResult = deviceMapper.selectPage(pageReqVO);
List<Long> deviceIds = deviceDOPageResult.getList().stream()
.map(DeviceDO::getId)
@ -414,6 +459,69 @@ public class DeviceServiceImpl implements DeviceService {
return deviceRespVOPageResult;
}
private PageResult<DeviceRespVO> getDevicePageByOperatingStatus(DevicePageReqVO pageReqVO) {
List<DeviceDO> deviceList = deviceMapper.selectList(pageReqVO);
List<DeviceRespVO> deviceRespVOList = BeanUtils.toBean(deviceList, DeviceRespVO.class);
List<Long> deviceIds = deviceRespVOList.stream()
.map(DeviceRespVO::getId)
.collect(Collectors.toList());
Map<Long, LocalDateTime> latestTsMap = tdengineService.newSelectLatestTsBatch(deviceIds);
Map<Long, DeviceOperationRecordDO> latestRecordMap = tdengineService.selectLatestByDeviceAndRuleMinimal(deviceIds, null).stream()
.collect(Collectors.toMap(
DeviceOperationRecordDO::getDeviceId,
r -> r,
(r1, r2) -> r1.getCreateTime().isAfter(r2.getCreateTime()) ? r1 : r2
));
for (DeviceRespVO deviceRespVO : deviceRespVOList) {
Long deviceId = deviceRespVO.getId();
if (latestTsMap.get(deviceId) != null) {
deviceRespVO.setCollectionTime(latestTsMap.get(deviceId));
}
deviceRespVO.setOperatingStatus(resolveDeviceOperatingStatus(latestRecordMap.get(deviceId)));
}
List<DeviceRespVO> filteredList = deviceRespVOList.stream()
.filter(device -> matchesOperatingStatus(device.getOperatingStatus(), pageReqVO.getOperatingStatus()))
.collect(Collectors.toList());
return new PageResult<>(subPage(filteredList, pageReqVO), (long) filteredList.size());
}
private String resolveDeviceOperatingStatus(DeviceOperationRecordDO record) {
if (record == null) {
return DeviceStatusEnum.OFFLINE.getName();
}
DeviceStatusEnum status = DeviceStatusEnum.getByCode(record.getRule());
return status != null ? status.getName() : DeviceStatusEnum.OFFLINE.getName();
}
private boolean matchesOperatingStatus(String actualStatus, String expectedStatus) {
if (StringUtils.equals(actualStatus, expectedStatus)) {
return true;
}
for (DeviceStatusEnum status : DeviceStatusEnum.values()) {
if ((StringUtils.equals(status.getCode(), expectedStatus)
|| StringUtils.equals(status.getName(), expectedStatus)
|| StringUtils.equalsIgnoreCase(status.name(), expectedStatus))
&& StringUtils.equals(status.getName(), actualStatus)) {
return true;
}
}
return false;
}
private List<DeviceRespVO> subPage(List<DeviceRespVO> list, DevicePageReqVO pageReqVO) {
if (Objects.equals(pageReqVO.getPageSize(), PageParam.PAGE_SIZE_NONE)) {
return list;
}
int pageNo = pageReqVO.getPageNo() != null ? pageReqVO.getPageNo() : 1;
int pageSize = pageReqVO.getPageSize() != null ? pageReqVO.getPageSize() : 10;
int fromIndex = Math.min((pageNo - 1) * pageSize, list.size());
int toIndex = Math.min(fromIndex + pageSize, list.size());
return list.subList(fromIndex, toIndex);
}
@Override
public List<DeviceDO> selectList(DevicePageReqVO reqVO){
return deviceMapper.selectList(reqVO);
@ -728,6 +836,7 @@ public class DeviceServiceImpl implements DeviceService {
}
@Override
// @Transactional(rollbackFor = Exception.class)
public Long copyDevice(Long id) {
if (id == null){
throw exception(DEVICE_ID_MODEL_NOT_EXISTS);
@ -744,6 +853,7 @@ public class DeviceServiceImpl implements DeviceService {
int randomNumber = random.nextInt(9000) + 1000;
newDevice.setDeviceCode(deviceDO.getDeviceCode()+ "-" + randomNumber);
newDevice.setDeviceName(deviceDO.getDeviceName() + "-副本");
newDevice.setTopic(null);
deviceMapper.insert(newDevice);
//复制关联表
@ -751,14 +861,20 @@ public class DeviceServiceImpl implements DeviceService {
lambdaQueryWrapper.eq(DeviceContactModelDO::getDeviceId,deviceDO.getId());
List<DeviceContactModelDO> deviceContactModelDOS = deviceContactModelMapper.selectList(lambdaQueryWrapper);
List<DeviceContactModelDO> newContactModelList = new ArrayList<>();
if (deviceContactModelDOS != null && !deviceContactModelDOS.isEmpty()){
for (DeviceContactModelDO deviceModelAttributeDOS : deviceContactModelDOS) {
deviceModelAttributeDOS.setId(null);
deviceModelAttributeDOS.setDeviceId(newDevice.getId());
for (DeviceContactModelDO deviceContactModelDO : deviceContactModelDOS) {
DeviceContactModelDO newContactModel = new DeviceContactModelDO();
BeanUtils.copyProperties(deviceContactModelDO, newContactModel);
newContactModel.setId(null);
newContactModel.setDeviceId(newDevice.getId());
newContactModel.setCreateTime(LocalDateTime.now());
newContactModel.setUpdateTime(LocalDateTime.now());
newContactModelList.add(newContactModel);
}
deviceContactModelMapper.insertBatch(deviceContactModelDOS);
deviceContactModelMapper.insertBatch(newContactModelList);
}
tdengineService.createTdengineTable(newDevice.getId(), newContactModelList);
return newDevice.getId();
@ -1965,12 +2081,7 @@ public class DeviceServiceImpl implements DeviceService {
throw exception(DEVICE_MQTT_TOPIC_EXIST);
}
// 2. 如果状态没有变化,直接返回
if (Objects.equals(deviceDO.getIsEnable(), updateEnabledReqVO.getEnabled())) {
return;
}
// 3. 执行状态更新操作
// 2. 执行状态同步操作:重复启停也要同步 MQTT 与 gateway保证幂等
executeEnableUpdate(deviceDO, updateEnabledReqVO.getEnabled());
@ -1984,6 +2095,8 @@ public class DeviceServiceImpl implements DeviceService {
private void executeEnableUpdate(DeviceDO deviceDO, Boolean enabled) {
MqttException syncException = null;
try {
// 1. 更新设备启用状态
deviceDO.setIsEnable(enabled);
@ -1999,6 +2112,7 @@ public class DeviceServiceImpl implements DeviceService {
mqttService.subscribeTopic(topic);
log.info("MQTT订阅成功: {}", topic);
} catch (MqttException e) {
syncException = e;
log.error("MQTT订阅失败: {}", topic, e);
}
@ -2024,6 +2138,8 @@ public class DeviceServiceImpl implements DeviceService {
} else {
//更新gateway状态
gateway.setGatewayName(deviceDO.getDeviceName());
gateway.setGatewayCode(deviceDO.getDeviceCode());
gateway.setIsEnable(true);
gateway.setUpdateTime(LocalDateTime.now());
@ -2042,17 +2158,14 @@ public class DeviceServiceImpl implements DeviceService {
mqttService.unsubscribeTopic(topic);
log.info("MQTT取消订阅成功: {}", topic);
} catch (MqttException e) {
syncException = e;
log.error("MQTT取消订阅失败: {}", topic, e);
} finally {
removeRuntimeTopic(topic);
}
// 3.2 更新gateway状态为禁用
gatewayMapper.update(
null,
new LambdaUpdateWrapper<GatewayDO>()
.eq(GatewayDO::getTopic, topic)
.set(GatewayDO::getIsEnable, false)
.set(GatewayDO::getUpdateTime, LocalDateTime.now())
);
disableGatewayByDevice(deviceDO);
log.info("gateway订阅记录已禁用 topic={}", topic);
@ -2063,9 +2176,14 @@ public class DeviceServiceImpl implements DeviceService {
}
if (syncException != null) {
throw new RuntimeException("MQTT订阅状态同步失败", syncException);
}
} catch (Exception e) {
log.error("更新设备状态失败 deviceCode={}", deviceDO.getDeviceCode(), e);
throw new RuntimeException("更新设备状态失败", e);
}
}

@ -779,6 +779,8 @@ public class TDengineService {
continue;
}
validateColumnName(attributeCode);
String tdType = JavaToTdengineTypeEnum.getTdTypeByJavaType(dataType);
if (tdType == null) {
tdType = "DOUBLE";
@ -1292,7 +1294,7 @@ public class TDengineService {
Set<String> reservedWords = new HashSet<>(Arrays.asList(
"value", "timestamp", "current", "database", "table",
"user", "password", "select", "insert", "update", "delete",
"create", "drop", "alter", "show", "describe", "use", "ts"
"create", "drop", "alter", "show", "describe", "use", "ts", "state"
));
return reservedWords.contains(columnName.toLowerCase());
@ -1312,7 +1314,7 @@ public class TDengineService {
Set<String> reservedKeywords = new HashSet<>(Arrays.asList(
"value", "timestamp", "current", "database", "table", "user", "password",
"select", "insert", "update", "delete", "create", "drop", "alter",
"show", "describe", "use", "ts", "now", "current_timestamp"
"show", "describe", "use", "ts", "now", "current_timestamp", "state"
));
if (reservedKeywords.contains(columnName.toLowerCase())) {
@ -1359,7 +1361,7 @@ public class TDengineService {
Set<String> reservedKeywords = new HashSet<>(Arrays.asList(
"value", "timestamp", "current", "database", "table", "user", "password",
"select", "insert", "update", "delete", "create", "drop", "alter",
"show", "describe", "use", "ts", "now", "current_timestamp"
"show", "describe", "use", "ts", "now", "current_timestamp", "state"
));
if (reservedKeywords.contains(tableName.toLowerCase())) {

@ -98,6 +98,7 @@ public interface ErrorCodeConstants {
ErrorCode MOLD_GET_NOT_EXISTS = new ErrorCode(5_0087, "领模申请不存在");
ErrorCode MOLD_RETURN_NOT_EXISTS = new ErrorCode(5_0087, "模具入库不存在");
ErrorCode MOLD_OPERATE_NOT_EXISTS = new ErrorCode(5_0087, "模具上下模不存在");
ErrorCode MOLD_OPERATE_DEVICE_HAS_MOLD = new ErrorCode(5_0089, "该设备已有上模模具组,请先下模后再上模");
ErrorCode MOLD_REPAIR_NOT_EXISTS = new ErrorCode(5_0087, "模具维修记录不存在");
ErrorCode MOLD_REPAIR_CODE_EXISTS = new ErrorCode(5_0087, "模具维修记录编码已存在");

@ -59,6 +59,7 @@ public class MoldOperateServiceImpl implements MoldOperateService {
// 插入
MoldOperateDO moldOperate = BeanUtils.toBean(createReqVO, MoldOperateDO.class);
DeviceLedgerDO deviceLedgerDO = deviceLedgerMapper.selectById(createReqVO.getDeviceId());
validateDeviceMoldEmpty(createReqVO, deviceLedgerDO);
fillDeviceAndLineInfo(moldOperate, deviceLedgerDO,createReqVO.getLineId());
MoldBrandDO moldDO = moldBrandMapper.selectById(createReqVO.getMoldId());
@ -116,6 +117,13 @@ public class MoldOperateServiceImpl implements MoldOperateService {
}
}
private void validateDeviceMoldEmpty(MoldOperateSaveReqVO createReqVO, DeviceLedgerDO deviceLedgerDO) {
if (createReqVO.getOperateType() != null && createReqVO.getOperateType() == 1
&& deviceLedgerDO != null && StringUtil.isNotBlank(deviceLedgerDO.getMoldId())) {
throw exception(MOLD_OPERATE_DEVICE_HAS_MOLD);
}
}
@Override
public MoldOperateDO getMoldOperate(Long id) {
return moldOperateMapper.selectById(id);

Loading…
Cancel
Save