feat:新增数据采集产能

main
HuangHuiKang 24 hours ago
parent 5b7456c50a
commit a86d70c0a6

@ -80,13 +80,30 @@ public class DevicePointRulesController {
public CommonResult<PageResult<DevicePointRulesRespVO>> getDevicePointRulesPage(@Valid DevicePointRulesPageReqVO pageReqVO) {
PageResult<DevicePointRulesDO> pageResult = devicePointRulesService.getDevicePointRulesPage(pageReqVO);
PageResult<DevicePointRulesRespVO> rulesRespVOPageResult = BeanUtils.toBean(pageResult, DevicePointRulesRespVO.class);
for (DevicePointRulesRespVO devicePointRulesRespVO : rulesRespVOPageResult.getList()) {
if (StringUtils.isNotBlank(devicePointRulesRespVO.getFieldRule())) {
List<PointRulesRespVO> pointRulesVOList = JSON.parseArray(devicePointRulesRespVO.getFieldRule(), PointRulesRespVO.class);
devicePointRulesRespVO.setPointRulesVOList(pointRulesVOList);
}
}
//
// for (DevicePointRulesRespVO respVO : rulesRespVOPageResult.getList()) {
// String fieldRule = respVO.getFieldRule();
// if (StringUtils.isBlank(fieldRule)) {
// continue;
// }
//
// String json = fieldRule.trim();
// try {
// if (json.startsWith("[")) {
// List<PointRulesRespVO> pointRulesVOList = JSON.parseArray(json, PointRulesRespVO.class);
// respVO.setPointRulesVOList(pointRulesVOList);
// continue;
// }
//
// if (json.startsWith("{")) {
// respVO.setPointRulesVOList(Collections.emptyList());
//
// }
// } catch (Exception e) {
// // 防御性处理,避免单条脏数据影响整页
// respVO.setPointRulesVOList(Collections.emptyList());
// }
// }
return success(rulesRespVOPageResult);
}

@ -24,11 +24,14 @@ import cn.iocoder.yudao.module.iot.framework.mqtt.consumer.impl.AsyncService;
import cn.iocoder.yudao.module.iot.framework.mqtt.entity.MqttData;
import cn.iocoder.yudao.module.iot.framework.mqtt.utils.DateUtils;
import cn.iocoder.yudao.module.iot.framework.mqtt.utils.MqttDataUtils;
import cn.iocoder.yudao.module.iot.framework.util.FormulaEvalUtils;
import cn.iocoder.yudao.module.iot.service.device.DeviceService;
import cn.iocoder.yudao.module.iot.service.device.TDengineService;
import cn.iocoder.yudao.module.iot.service.iotorganization.IotOrganizationService;
import cn.iocoder.yudao.module.iot.service.mqttrecord.MqttRecordService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@ -42,6 +45,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@ -301,21 +305,55 @@ public class MqttDataHandler extends SuperConsumer<String> {
// 3. 入库
if (!validDataList.isEmpty()) {
saveToDatabase(deviceId, validDataList, successCount);
} else {
log.warn("设备 {} 未匹配到 MQTT 数据", deviceId);
}
saveToDatabase(
deviceId,
validDataList,
successCount
);
// 4. 计算并写入产能
try {
handleCapacityFormula(device, varListMap);
} catch (Exception e) {
log.error("设备 {} 产能计算失败", deviceId, e);
}
}
} else {
// handleCapacityFormula
private void handleCapacityFormula(DeviceDO device, Map<String, Object> varListMap) {
DevicePointRulesDO formulaRule = devicePointRulesMapper.selectOne(
Wrappers.<DevicePointRulesDO>lambdaQuery()
.eq(DevicePointRulesDO::getDeviceId, device.getId())
.eq(DevicePointRulesDO::getIdentifier, "COUNT")
.orderByDesc(DevicePointRulesDO::getUpdateTime)
.last("limit 1")
);
if (formulaRule == null || StringUtils.isBlank(formulaRule.getFieldRule())) {
return;
}
log.warn("设备 {} 未匹配到 MQTT 数据", deviceId);
BigDecimal capacity = null;
String rule = formulaRule.getFieldRule().trim();
if (rule.startsWith("[")) {
JSONArray steps = JSON.parseArray(rule);
capacity = FormulaEvalUtils.evalStepArrayByCode(steps, varListMap);
} else {
// 如需兼容旧 expr 结构可保留
JSONObject root = JSON.parseObject(rule);
JSONObject expr = root.getJSONObject("expr");
if (expr != null) {
capacity = FormulaEvalUtils.evalExpr(expr, varListMap);
}
}
if (capacity == null) {
return;
}
tDengineService.insertDeviceCapacityRecord(device.getId(), capacity.doubleValue());
}
private DevicePointRulesDO getDevicePointRules(Long deviceId) {
List<DevicePointRulesDO> list =

@ -0,0 +1,96 @@
package cn.iocoder.yudao.module.iot.framework.util;
// FormulaEvalUtils
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;
public class FormulaEvalUtils {
private FormulaEvalUtils() {}
public static BigDecimal evalExpr(JSONObject node, Map<String, Object> vars) {
if (node == null) return BigDecimal.ZERO;
if (node.containsKey("field")) {
Object value = vars.get(node.getString("field"));
return toBigDecimal(value);
}
if (node.containsKey("value")) {
return toBigDecimal(node.get("value"));
}
String op = node.getString("op");
BigDecimal left = evalExpr(node.getJSONObject("left"), vars);
BigDecimal right = evalExpr(node.getJSONObject("right"), vars);
return apply(left, right, op);
}
//
public static BigDecimal evalStepArrayByCode(JSONArray steps, Map<String, Object> vars) {
if (steps == null || steps.isEmpty()) {
return BigDecimal.ZERO;
}
BigDecimal result = null;
String pendingBigOp = null; // 上一步的 bigOperator
for (int i = 0; i < steps.size(); i++) {
JSONObject step = steps.getJSONObject(i);
if (step == null) continue;
String code = step.getString("code");
String operator = step.getString("operator");
Object valueObj = step.get("value");
BigDecimal codeValue = readCodeValue(code, vars);
BigDecimal value = toBigDecimal(valueObj);
BigDecimal current = apply(codeValue, value, operator); // A/B/C
if (result == null) {
result = current;
} else {
result = apply(result, current, pendingBigOp); // 用上一项的 bigOperator
}
pendingBigOp = step.getString("bigOperator"); // 留给下一轮使用
}
return result == null ? BigDecimal.ZERO : result;
}
private static BigDecimal readCodeValue(String code, Map<String, Object> vars) {
if (StringUtils.isBlank(code) || vars == null) {
return BigDecimal.ZERO;
}
return toBigDecimal(vars.get(code));
}
private static BigDecimal toBigDecimal(Object raw) {
if (raw == null || StringUtils.isBlank(String.valueOf(raw))) {
return BigDecimal.ZERO;
}
return new BigDecimal(String.valueOf(raw));
}
private static BigDecimal apply(BigDecimal left, BigDecimal right, String op) {
if (StringUtils.isBlank(op)) {
return left;
}
switch (op) {
case "+": return left.add(right);
case "-": return left.subtract(right);
case "*": return left.multiply(right);
case "/":
if (right.compareTo(BigDecimal.ZERO) == 0) return BigDecimal.ZERO;
return left.divide(right, 6, RoundingMode.HALF_UP);
default: throw new IllegalArgumentException("Unsupported op: " + op);
}
}
}

@ -2516,4 +2516,23 @@ public class TDengineService {
return record;
}
}
// ========================= 产能相关接口 ===================
@DS("tdengine")
public boolean insertDeviceCapacityRecord(Long deviceId, Double capacityValue) {
if (deviceId == null || capacityValue == null) {
return false;
}
try {
String sql = "INSERT INTO iot_device_capacity_record (ts, capacity_value, device_id) VALUES (NOW, "
+ capacityValue + ", " + deviceId + ")";
jdbcTemplate.execute(sql);
return true;
} catch (Exception e) {
log.error("insertDeviceCapacityRecord failed, deviceId={}, capacityValue={}", deviceId, capacityValue, e);
return false;
}
}
}

@ -46,11 +46,11 @@ public class DevicePointRulesServiceImpl implements DevicePointRulesService {
validateDevicePointRulesExists(updateReqVO.getId());
// 更新
DevicePointRulesDO updateObj = BeanUtils.toBean(updateReqVO, DevicePointRulesDO.class);
if (!updateReqVO.getPointRulesVOList().isEmpty()){
String jsonString = JSON.toJSONString(updateReqVO.getPointRulesVOList());
updateObj.setFieldRule(jsonString);
}
// if (!updateReqVO.getPointRulesVOList().isEmpty()){
// String jsonString = JSON.toJSONString(updateReqVO.getPointRulesVOList());
// updateObj.setFieldRule(jsonString);
//
// }
devicePointRulesMapper.updateById(updateObj);
}

@ -113,8 +113,8 @@ public class TaskController {
@Operation(summary = "获得生产任务单分页")
@PreAuthorize("@ss.hasPermission('mes:task:query')")
public CommonResult<PageResult<TaskRespVO>> getTaskPage(@Valid TaskPageReqVO pageReqVO) {
PageResult<TaskDO> pageResult = taskService.getTaskPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, TaskRespVO.class));
PageResult<TaskRespVO> pageResult = taskService.getTaskPage(pageReqVO);
return success(pageResult);
}
@GetMapping("/export-excel")
@ -124,10 +124,10 @@ public class TaskController {
public void exportTaskExcel(@Valid TaskPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<TaskDO> list = taskService.getTaskPage(pageReqVO).getList();
List<TaskRespVO> list = taskService.getTaskPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "生产任务单.xls", "数据", TaskRespVO.class,
BeanUtils.toBean(list, TaskRespVO.class));
list);
}
// ==================== 子表(生产任务单明细) ====================

@ -62,11 +62,21 @@ public class TaskRespVO {
@ExcelProperty("创建时间")
private LocalDateTime createTime;
@Schema(description = "任务单类型", example = "你猜")
@Schema(description = "任务单类型", example = "")
@ExcelProperty("任务单类型")
private String taskType;
@Schema(description = "任务总数", example = "")
@ExcelProperty("任务总数")
private Long totalNumber;
@Schema(description = "已计划数量", example = "")
@ExcelProperty("已计划数量")
private Long planNumber;
@Schema(description = "未计划数量", example = "")
@ExcelProperty("未计划数量")
private Long unPlanNumber;

@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.mes.controller.admin.task.vo.TaskDetailRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.task.vo.TaskPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.task.vo.TaskRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.task.vo.TaskSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.plan.PlanDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDO;
@ -76,7 +77,7 @@ public interface TaskService {
* @param pageReqVO
* @return
*/
PageResult<TaskDO> getTaskPage(TaskPageReqVO pageReqVO);
PageResult<TaskRespVO> getTaskPage(TaskPageReqVO pageReqVO);
/**
*

@ -11,10 +11,7 @@ import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO;
import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
import cn.iocoder.yudao.module.erp.service.product.ErpProductUnitService;
import cn.iocoder.yudao.module.mes.controller.admin.plan.vo.PlanStatusEnum;
import cn.iocoder.yudao.module.mes.controller.admin.task.vo.TaskDetailRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.task.vo.TaskPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.task.vo.TaskSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.task.vo.TaskStatusEnum;
import cn.iocoder.yudao.module.mes.controller.admin.task.vo.*;
import cn.iocoder.yudao.module.mes.dal.dataobject.plan.PlanDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDetailDO;
@ -133,8 +130,35 @@ public class TaskServiceImpl implements TaskService {
}
@Override
public PageResult<TaskDO> getTaskPage(TaskPageReqVO pageReqVO) {
return taskMapper.selectPage(pageReqVO);
public PageResult<TaskRespVO> getTaskPage(TaskPageReqVO pageReqVO) {
PageResult<TaskDO> taskDOPageResult = taskMapper.selectPage(pageReqVO);
PageResult<TaskRespVO> bean = BeanUtils.toBean(taskDOPageResult, TaskRespVO.class);
if (bean.getList() == null || bean.getList().isEmpty()) {
return bean;
}
for (TaskRespVO task : bean.getList()) {
List<ViewTaskProductSummary> summaryList = summaryMapper.selectListByTaskId(task.getId());
long totalNumber = 0L;
long planNumber = 0L;
for (ViewTaskProductSummary summary : summaryList) {
totalNumber += summary.getTotalNumber() == null ? 0L : summary.getTotalNumber();
planNumber += summary.getPlanNumber() == null ? 0L : summary.getPlanNumber();
}
long unplanNumber = totalNumber - planNumber;
if (unplanNumber < 0) {
unplanNumber = 0L;
}
task.setTotalNumber(totalNumber);
task.setPlanNumber(planNumber);
task.setUnPlanNumber(unplanNumber);
}
return bean;
}
@Override

@ -141,7 +141,7 @@ public class TaskServiceImplTest extends BaseDbUnitTest {
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
// 调用
PageResult<TaskDO> pageResult = taskService.getTaskPage(reqVO);
PageResult<TaskRespVO> pageResult = taskService.getTaskPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());

Loading…
Cancel
Save