调度模块添加仅立库模式

新增mqtt功能
新增施工队信息导入功能
This commit is contained in:
2025-12-31 14:51:58 +08:00
parent b0bbeeec7c
commit 2b7e91e918
27 changed files with 1414 additions and 391 deletions

343
pom.xml
View File

@@ -1,197 +1,206 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- ======================= -->
<!-- 基本项目信息 -->
<!-- ======================= -->
<modelVersion>4.0.0</modelVersion>
<groupId>com.smart</groupId>
<artifactId>smart</artifactId>
<version>3.8.9</version>
<packaging>jar</packaging>
<!-- <packaging>war</packaging>-->
<name>zg</name>
<url>http://www.zg.vip</url>
<description>智慧实物管理系统</description>
<!-- ======================= -->
<!-- Spring Boot 父工程 -->
<!-- ======================= -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.15</version>
<relativePath />
<relativePath/>
</parent>
<!-- ======================= -->
<!-- 全局版本与编译配置 -->
<!-- ======================= -->
<properties>
<!-- 编码 & JDK -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<pagehelper.spring.boot.starter.version>1.4.7</pagehelper.spring.boot.starter.version>
<fastjson.version>2.0.53</fastjson.version>
<!-- Spring / Web -->
<tomcat.version>9.0.102</tomcat.version>
<spring-framework.version>5.3.39</spring-framework.version>
<spring-security.version>5.7.12</spring-security.version>
<logback.version>1.2.13</logback.version>
<!-- 数据库 & ORM -->
<druid.version>1.2.23</druid.version>
<pagehelper.spring.boot.starter.version>1.4.7</pagehelper.spring.boot.starter.version>
<mybatis-plus.version>3.5.5</mybatis-plus.version>
<!-- JSON / 工具 -->
<fastjson.version>2.0.53</fastjson.version>
<commons.io.version>2.13.0</commons.io.version>
<bitwalker.version>1.21</bitwalker.version>
<jwt.version>0.9.1</jwt.version>
<kaptcha.version>2.3.3</kaptcha.version>
<!-- 文档 / Excel -->
<swagger.version>3.0.0</swagger.version>
<poi.version>4.1.2</poi.version>
<!-- OSHI 6.x 需要 JDK11这里固定为 JDK8 可用的 5.8.5 -->
<!-- 系统 / 任务 -->
<oshi.version>5.8.5</oshi.version>
<velocity.version>2.3</velocity.version>
<!-- override dependency version -->
<tomcat.version>9.0.102</tomcat.version>
<logback.version>1.2.13</logback.version>
<spring-security.version>5.7.12</spring-security.version>
<spring-framework.version>5.3.39</spring-framework.version>
<kaptcha.version>2.3.3</kaptcha.version>
<!-- 中间件 / 通信 -->
<redis.version>2.5.15</redis.version>
<okhttp.version>4.12.0</okhttp.version>
<paho.mqtt.version>1.2.5</paho.mqtt.version>
<!-- 存储 -->
<minio.version>7.1.4</minio.version>
<!-- 设备 SDK -->
<rfidreader.version>1.0.0</rfidreader.version>
<zmprinter.version>4.4.7</zmprinter.version>
<mybatis-plus.version>3.5.5</mybatis-plus.version>
<minio.version>7.1.4</minio.version>
<okhttp.version>4.12.0</okhttp.version>
</properties>
<!-- ======================= -->
<!-- 依赖管理 -->
<!-- ======================= -->
<dependencies>
<!-- ===== Spring Boot 基础 ===== -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- ===== 安全 & WebSocket ===== -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- ===== 数据库 & ORM ===== -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper.spring.boot.starter.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- ===== Redis ===== -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- ===== MQTT / HTTP 通信 ===== -->
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>${paho.mqtt.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
</dependency>
<!-- SpringBoot 核心包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- SpringBoot 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- SpringBoot 拦截器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- SpringBoot Web容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring-boot-devtools -->
<!--
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
-->
<!-- spring security 安全认证 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Spring WebSocket 支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- redis 缓存操作 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- pool 对象池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- Mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- pagehelper 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper.spring.boot.starter.version}</version>
</dependency>
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- 自定义验证注解 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- 常用工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<!-- 解析客户端操作系统、浏览器等 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>${bitwalker.version}</version>
</dependency>
<!-- 阿里JSON解析器 -->
<!-- ===== JSON / 工具 ===== -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- Spring框架基本的核心工具-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- Token生成与解析-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>${bitwalker.version}</version>
</dependency>
<!-- ===== Token / 安全 ===== -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<!-- Jaxb -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<!-- Swagger3依赖 -->
<!-- ===== 文档 / Excel ===== -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
@@ -204,48 +213,31 @@
</exclusions>
</dependency>
<!-- 防止进入swagger页面报类型转换错误排除3.0.0中的引用手动增加1.6.2版本 -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.6.2</version>
</dependency>
<!-- 获取系统信息 -->
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>${oshi.version}</version>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<!--MinIo-->
<!-- ===== 系统 / 定时 / 工具 ===== -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.version}</version>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>${oshi.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- velocity代码生成使用模板 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.version}</version>
</dependency>
<!-- 定时任务 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
@@ -257,40 +249,59 @@
</exclusions>
</dependency>
<!-- 验证码 -->
<dependency>
<groupId>pro.fessional</groupId>
<artifactId>kaptcha</artifactId>
<version>${kaptcha.version}</version>
<exclusions>
<exclusion>
<artifactId>servlet-api</artifactId>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- ===== 对象存储 ===== -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.version}</version>
</dependency>
<!-- ===== 设备 SDK ===== -->
<dependency>
<groupId>com.zkweix</groupId>
<artifactId>RFIDReaderAPI</artifactId>
<version>1.0.0</version>
<version>${rfidreader.version}</version>
</dependency>
<dependency>
<groupId>com.zmtech</groupId>
<artifactId>ZMPrinter</artifactId>
<version>4.4.7</version>
<version>${zmprinter.version}</version>
</dependency>
<!-- ===== Lombok ===== -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- ===== 测试 ===== -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- ======================= -->
<!-- 构建配置 -->
<!-- ======================= -->
<build>
<finalName>WisdomManagement</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
@@ -300,7 +311,6 @@
</configuration>
</plugin>
<!-- 关键:固定编译插件版本,并指定 JDK8 的 source/target -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
@@ -308,35 +318,20 @@
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<!-- JDK8 环境不要配置 <release> -->
</configuration>
</plugin>
</plugins>
</build>
<!-- ======================= -->
<!-- Maven 仓库 -->
<!-- ======================= -->
<repositories>
<repository>
<id>public</id>
<id>aliyun</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>

View File

@@ -4,14 +4,7 @@ import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import com.zg.framework.aspectj.lang.annotation.Log;
import com.zg.framework.aspectj.lang.enums.BusinessType;
import com.zg.project.information.domain.ConstructionTeam;
@@ -20,6 +13,7 @@ import com.zg.framework.web.controller.BaseController;
import com.zg.framework.web.domain.AjaxResult;
import com.zg.common.utils.poi.ExcelUtil;
import com.zg.framework.web.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile;
/**
* 施工队信息Controller
@@ -59,6 +53,21 @@ public class ConstructionTeamController extends BaseController
util.exportExcel(response, list, "施工队信息数据");
}
/**
* 导入施工队数据(全部新增,不校验重复)
*/
@PreAuthorize("@ss.hasPermi('information:construction:import')")
@Log(title = "施工队信息", businessType = BusinessType.IMPORT)
@PostMapping("/importData")
public AjaxResult importData(@RequestParam("file") MultipartFile file) throws Exception
{
ExcelUtil<ConstructionTeam> util = new ExcelUtil<>(ConstructionTeam.class);
List<ConstructionTeam> teamList = util.importExcel(file.getInputStream());
String operName = getUsername();
String message = constructionTeamService.importConstructionTeamList(teamList, operName);
return AjaxResult.success(message);
}
/**
* 获取施工队信息详细信息
*/

View File

@@ -65,7 +65,6 @@ public class MtdController extends BaseController
ExcelUtil<Mtd> util = new ExcelUtil<>(Mtd.class);
List<Mtd> mtdList = util.importExcel(file.getInputStream());
String operName = getUsername();
// String operName = "大爷的!!!";
String message = mtdService.importMtd(mtdList, updateSupport, operName);
return success(message);
}

View File

@@ -29,25 +29,25 @@ public class ConstructionTeam extends BaseEntity
private String teamCode;
/** 创建人 */
@Excel(name = "创建人")
// @Excel(name = "创建人")
private String createdBy;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd")
// @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date createdAt;
/** 修改人 */
@Excel(name = "修改人")
// @Excel(name = "修改人")
private String updatedBy;
/** 修改时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "修改时间", width = 30, dateFormat = "yyyy-MM-dd")
// @Excel(name = "修改时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date updatedAt;
/** 是否删除0正常 1删除 */
@Excel(name = "是否删除", readConverterExp = "0=正常,1=删除")
// @Excel(name = "是否删除", readConverterExp = "0=正常,1=删除")
private String isDelete;
public void setId(Long id)

View File

@@ -58,4 +58,12 @@ public interface IConstructionTeamService
* @return 结果
*/
public int deleteConstructionTeamById(Long id);
/**
* 导入施工队信息数据
*
* @param teamList 工单数据列表
* @param operName 操作人员
*/
String importConstructionTeamList(List<ConstructionTeam> teamList, String operName);
}

View File

@@ -1,6 +1,10 @@
package com.zg.project.information.service.impl;
import java.util.Date;
import java.util.List;
import com.zg.common.exception.ServiceException;
import com.zg.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.zg.project.information.mapper.ConstructionTeamMapper;
@@ -90,4 +94,73 @@ public class ConstructionTeamServiceImpl implements IConstructionTeamService
{
return constructionTeamMapper.deleteConstructionTeamById(id);
}
/**
* 导入施工队信息列表
*
* @param teamList 导入的施工队信息列表
* @param operName 操作人员
* @return 结果
*/
@Override
public String importConstructionTeamList(List<ConstructionTeam> teamList, String operName)
{
if (teamList == null || teamList.isEmpty())
{
throw new ServiceException("导入数据不能为空!");
}
int successNum = 0;
int failureNum = 0;
StringBuilder failureMsg = new StringBuilder();
Date now = new Date();
for (ConstructionTeam team : teamList)
{
try
{
// 可选:最低限度必填校验(建议保留,不然空行也会入库)
if (StringUtils.isBlank(team.getTeamName()) || StringUtils.isBlank(team.getTeamCode()))
{
failureNum++;
failureMsg.append("<br/>施工队名称/编号不能为空,已跳过。");
continue;
}
// 按你供应计划逻辑:不校验重复,全部新增
team.setCreatedBy(operName);
team.setCreatedAt(now);
team.setUpdatedBy(operName);
team.setUpdatedAt(now);
// isDelete 默认 0
if (StringUtils.isBlank(team.getIsDelete()))
{
team.setIsDelete("0");
}
// 走你现有 insertMapper/XML 已存在)
insertConstructionTeam(team);
successNum++;
}
catch (Exception e)
{
failureNum++;
failureMsg.append("<br/>施工队编号:").append(team.getTeamCode())
.append(",施工队名称:").append(team.getTeamName())
.append(" 导入失败:").append(e.getMessage());
}
}
if (failureNum > 0)
{
failureMsg.insert(0, "导入完成,但有 " + failureNum + " 条记录失败:");
throw new ServiceException(failureMsg.toString());
}
return "导入成功,共 " + successNum + " 条数据";
}
}

View File

@@ -0,0 +1,182 @@
package com.zg.project.wisdom.config;
import com.zg.project.wisdom.mqtt.dispatcher.MqttMessageDispatcher;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
/**
* MQTT 客户端初始化配置类
*
* 作用说明:
* 1. Spring Boot 启动时自动创建 MQTT 客户端
* 2. 连接 EMQX Broker
* 3. 订阅 application.yml 中配置的 Topic支持通配符
* 4. 接收传感器上报的数据
*
* 说明:
* - 同一个 MQTT Client 同时支持「订阅(接收)」和「发布(下发指令)」
* - 后端一般只需要一个 Client
*/
@Slf4j
@Configuration
public class MqttClientConfig {
/**
* MQTT 客户端实例
* 由本配置类统一维护
*/
private MqttClient mqttClient;
@Resource
private MqttMessageDispatcher mqttMessageDispatcher;
/**
* 创建 MQTT Client Bean
*
* @param props MQTT 配置(来自 application.yml
*/
@Bean
public MqttClient mqttClient(MqttProperties props) throws MqttException {
// 如果未开启 MQTT 功能,直接不初始化
// 注意:此时 mqttClient Bean 为 null
if (!props.isEnabled()) {
log.warn("[MQTT] mqtt.enabled=falseMQTT 功能未启用");
return null;
}
// 1. 创建 MQTT 客户端
mqttClient = new MqttClient(
props.getBroker(),
props.getClientId(),
new MemoryPersistence()
);
// 2. MQTT 连接参数
MqttConnectOptions options = new MqttConnectOptions();
// 是否清除会话:
// true - 断开后清除订阅(推荐后端使用)
// false - 保留会话(多用于设备端)
options.setCleanSession(props.isCleanSession());
// 心跳时间(秒)
options.setKeepAliveInterval(props.getKeepAlive());
// 连接超时时间(秒)
options.setConnectionTimeout(props.getTimeout());
// 自动重连(非常重要,防止网络抖动)
options.setAutomaticReconnect(true);
// 设置用户名密码(如果配置了)
if (props.getUsername() != null && !props.getUsername().trim().isEmpty()) {
options.setUserName(props.getUsername().trim());
}
if (props.getPassword() != null && !props.getPassword().trim().isEmpty()) {
options.setPassword(props.getPassword().toCharArray());
}
// 3. 设置 MQTT 回调
mqttClient.setCallback(new MqttCallbackExtended() {
/**
* 连接成功(或重连成功)回调
*/
@Override
public void connectComplete(boolean reconnect, String serverURI) {
log.info("[MQTT] 连接成功 reconnect={}, serverURI={}", reconnect, serverURI);
// 连接成功后订阅 Topic
subscribe(props);
}
/**
* 连接丢失回调
*/
@Override
public void connectionLost(Throwable cause) {
log.warn("[MQTT] 连接断开", cause);
}
/**
* 接收到消息回调
*
* @param topic 消息主题(用于区分设备、类型)
* @param message 消息内容
*/
@Override
public void messageArrived(String topic, MqttMessage message) {
String payload = new String(message.getPayload(), StandardCharsets.UTF_8);
mqttMessageDispatcher.dispatch(topic, payload);
}
/**
* 消息发送完成回调(发布消息后触发)
*/
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
// 后端下发指令时可用于确认发送完成
}
});
// 4. 连接 Broker
log.info("[MQTT] 正在连接 Broker{}", props.getBroker());
mqttClient.connect(options);
log.info("[MQTT] MQTT 已连接");
// 5. 启动时先订阅一次(防止 connectComplete 未触发)
subscribe(props);
return mqttClient;
}
/**
* 订阅 Topic支持通配符
*/
private void subscribe(MqttProperties props) {
if (mqttClient == null || !mqttClient.isConnected()) {
log.warn("[MQTT] 订阅失败:客户端未连接");
return;
}
if (props.getTopics() == null || props.getTopics().isEmpty()) {
log.warn("[MQTT] 未配置订阅 Topic");
return;
}
try {
for (String topic : props.getTopics()) {
if (topic == null || topic.trim().isEmpty()) {
continue;
}
mqttClient.subscribe(topic.trim(), props.getQos());
log.info("[MQTT] 已订阅 Topic{}QoS={}", topic.trim(), props.getQos());
}
} catch (Exception e) {
log.error("[MQTT] 订阅 Topic 失败", e);
}
}
/**
* Spring 容器关闭时,优雅断开 MQTT 连接
*/
@PreDestroy
public void destroy() {
try {
if (mqttClient != null) {
if (mqttClient.isConnected()) {
mqttClient.disconnect();
}
mqttClient.close();
}
} catch (Exception ignored) {
}
}
}

View File

@@ -0,0 +1,46 @@
package com.zg.project.wisdom.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* MQTT 配置属性(对应 application.yml 中 mqtt.*
*/
@Data
@Component
@ConfigurationProperties(prefix = "mqtt")
public class MqttProperties {
/** 是否启用 MQTT */
private boolean enabled;
/** Broker 地址tcp://192.168.1.29:1883 */
private String broker;
/** 客户端ID在 EMQX 中唯一) */
private String clientId;
/** 账号 */
private String username;
/** 密码 */
private String password;
/** 是否清除会话 */
private boolean cleanSession = true;
/** 心跳时间(秒) */
private int keepAlive = 30;
/** 连接超时时间(秒) */
private int timeout = 10;
/** 默认 QoS */
private int qos = 1;
/** 订阅 Topic 列表(支持通配符) */
private List<String> topics;
}

View File

@@ -0,0 +1,74 @@
package com.zg.project.wisdom.config;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
/**
* MQTT 消息发布客户端
*
* 作用说明:
* - 专门用于向指定设备下发控制指令
* - 例如:开启 / 关闭 / 重启 传感器
*
* 说明:
* - 依赖同一个 MqttClient
* - 发布消息时使用“精确 Topic”不能使用通配符
*/
@Slf4j
@Component
public class MqttPublishClient {
/**
* MQTT 客户端(由 Spring 注入)
*/
private final MqttClient mqttClient;
public MqttPublishClient(MqttClient mqttClient) {
this.mqttClient = mqttClient;
}
/**
* 发布 MQTT 消息(默认 QoS=1不保留消息
*
* @param topic 目标 Topic必须是具体设备的 Topic
* @param payload 消息内容JSON 字符串)
*/
public void publish(String topic, String payload) {
publish(topic, payload, 1, false);
}
/**
* 发布 MQTT 消息(自定义参数)
*/
public void publish(String topic, String payload, int qos, boolean retained) {
// 如果 MQTT 未启用或未初始化
if (mqttClient == null) {
throw new IllegalStateException("MQTT 客户端未初始化mqtt.enabled=false");
}
// 如果 MQTT 尚未连接
if (!mqttClient.isConnected()) {
throw new IllegalStateException("MQTT 客户端未连接到 Broker");
}
try {
MqttMessage msg = new MqttMessage(payload.getBytes(StandardCharsets.UTF_8));
msg.setQos(qos);
msg.setRetained(retained);
mqttClient.publish(topic, msg);
log.info("[MQTT] 指令已发送 topic={}, qos={}, retained={}, payload={}",
topic, qos, retained, payload);
} catch (Exception e) {
log.error("[MQTT] 指令发送失败 topic=" + topic, e);
throw new RuntimeException(e);
}
}
}

View File

@@ -12,7 +12,6 @@ import com.zg.project.wisdom.domain.dto.OutGoodsDTO;
import com.zg.project.wisdom.service.IAgvTaskResultService;
import com.zg.project.wisdom.service.IWcsTaskResultService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
@@ -48,6 +47,7 @@ public class AgvTaskResultController extends BaseController {
return AjaxResult.success("WCS 回调接收成功");
}
/**
* 查询 AGV 状态
*/

View File

@@ -36,6 +36,14 @@ public class DdTaskController extends BaseController {
return toAjax(ddTaskService.insertDdTask(ddTask));
}
/**
* 批量新增调度任务
*/
@PostMapping("/batchAdd")
public AjaxResult batchAdd(@RequestBody List<DdTask> list) {
return toAjax(ddTaskService.insertDdTaskBatch(list));
}
/**
* 执行任务
* @param

View File

@@ -3,6 +3,7 @@ package com.zg.project.wisdom.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.zg.framework.web.domain.BaseEntity;
import com.zg.framework.aspectj.lang.annotation.Excel;
import java.util.Date;
/**
@@ -29,7 +30,7 @@ public class DdTask extends BaseEntity {
@Excel(name = "任务类型")
private String taskType;
/** 任务状态0待1完成,-1取消 */
/** 任务状态0待执行1执行中2完成,-1取消 */
@Excel(name = "任务状态")
private Integer taskStatus;
@@ -65,13 +66,13 @@ public class DdTask extends BaseEntity {
@Excel(name = "审核员")
private String approver;
/** 单据创建时间(原始单据时间) */
/** 任务创建时间(原始单据时间) */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "单据创建时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "任务创建时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date rcptim;
/** 绑定单据ID */
@Excel(name = "rcp id")
@Excel(name = "绑定单据ID")
private String rid;
/** 执行次数 */
@@ -82,85 +83,168 @@ public class DdTask extends BaseEntity {
@Excel(name = "封签码/订单号")
private String prf;
/**
* 调度模式
* 1 = 仅立库WCS
* 2 = 立库 + AGV
*/
@Excel(name = "调度模式", readConverterExp = "1=仅立库,2=立库+AGV")
private Integer dispatchMode;
/** 是否删除0正常 1删除 */
private String isDelete;
// getter/setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
// ===================== getter / setter =====================
public String getTaskNo() { return taskNo; }
public void setTaskNo(String taskNo) { this.taskNo = taskNo; }
public Long getId() {
return id;
}
public String getTaskDtl() { return taskDtl; }
public void setTaskDtl(String taskDtl) { this.taskDtl = taskDtl; }
public void setId(Long id) {
this.id = id;
}
public String getTaskType() { return taskType; }
public void setTaskType(String taskType) { this.taskType = taskType; }
public String getTaskNo() {
return taskNo;
}
public Integer getTaskStatus() { return taskStatus; }
public void setTaskStatus(Integer taskStatus) { this.taskStatus = taskStatus; }
public void setTaskNo(String taskNo) {
this.taskNo = taskNo;
}
public Integer getMidStatus() { return midStatus; }
public void setMidStatus(Integer midStatus) { this.midStatus = midStatus; }
public String getTaskDtl() {
return taskDtl;
}
public String getMid() { return mid; }
public void setMid(String mid) { this.mid = mid; }
public void setTaskDtl(String taskDtl) {
this.taskDtl = taskDtl;
}
public Integer getNum() { return num; }
public void setNum(Integer num) { this.num = num; }
public String getTaskType() {
return taskType;
}
public String getMidType() { return midType; }
public void setMidType(String midType) { this.midType = midType; }
public void setTaskType(String taskType) {
this.taskType = taskType;
}
public String getSourceName() { return sourceName; }
public void setSourceName(String sourceName) { this.sourceName = sourceName; }
public Integer getTaskStatus() {
return taskStatus;
}
public String getTargetName() { return targetName; }
public void setTargetName(String targetName) { this.targetName = targetName; }
public void setTaskStatus(Integer taskStatus) {
this.taskStatus = taskStatus;
}
public String getOperator() { return operator; }
public void setOperator(String operator) { this.operator = operator; }
public Integer getMidStatus() {
return midStatus;
}
public String getApprover() { return approver; }
public void setApprover(String approver) { this.approver = approver; }
public void setMidStatus(Integer midStatus) {
this.midStatus = midStatus;
}
public Date getRcptim() { return rcptim; }
public void setRcptim(Date rcptim) { this.rcptim = rcptim; }
public String getMid() {
return mid;
}
public String getRid() { return rid; }
public void setRid(String rid) { this.rid = rid; }
public void setMid(String mid) {
this.mid = mid;
}
public Integer getDoCount() { return doCount; }
public void setDoCount(Integer doCount) { this.doCount = doCount; }
public Integer getNum() {
return num;
}
public String getPrf() { return prf; }
public void setPrf(String prf) { this.prf = prf; }
public void setNum(Integer num) {
this.num = num;
}
public String getIsDelete() { return isDelete; }
public void setIsDelete(String isDelete) { this.isDelete = isDelete; }
public String getMidType() {
return midType;
}
@Override
public String toString() {
return "DdTask{" +
"id=" + id +
", taskNo='" + taskNo + '\'' +
", taskDtl='" + taskDtl + '\'' +
", taskType='" + taskType + '\'' +
", taskStatus=" + taskStatus +
", midStatus=" + midStatus +
", mid='" + mid + '\'' +
", num=" + num +
", midType='" + midType + '\'' +
", sourceName='" + sourceName + '\'' +
", targetName='" + targetName + '\'' +
", operator='" + operator + '\'' +
", approver='" + approver + '\'' +
", rcptim=" + rcptim +
", rid='" + rid + '\'' +
", doCount=" + doCount +
", prf='" + prf + '\'' +
", isDelete='" + isDelete + '\'' +
'}';
public void setMidType(String midType) {
this.midType = midType;
}
public String getSourceName() {
return sourceName;
}
public void setSourceName(String sourceName) {
this.sourceName = sourceName;
}
public String getTargetName() {
return targetName;
}
public void setTargetName(String targetName) {
this.targetName = targetName;
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
public String getApprover() {
return approver;
}
public void setApprover(String approver) {
this.approver = approver;
}
public Date getRcptim() {
return rcptim;
}
public void setRcptim(Date rcptim) {
this.rcptim = rcptim;
}
public String getRid() {
return rid;
}
public void setRid(String rid) {
this.rid = rid;
}
public Integer getDoCount() {
return doCount;
}
public void setDoCount(Integer doCount) {
this.doCount = doCount;
}
public String getPrf() {
return prf;
}
public void setPrf(String prf) {
this.prf = prf;
}
public Integer getDispatchMode() {
return dispatchMode;
}
public void setDispatchMode(Integer dispatchMode) {
this.dispatchMode = dispatchMode;
}
public String getIsDelete() {
return isDelete;
}
public void setIsDelete(String isDelete) {
this.isDelete = isDelete;
}
}

View File

@@ -34,4 +34,9 @@ public interface AgvTaskResultMapper {
@Param("status") String status);
AgvTaskResult selectByTaskNo(String taskNo);
/**
* 统计指定 taskNo 的记录数量
*/
int countByTaskNo(String taskNo);
}

View File

@@ -53,4 +53,9 @@ public interface DdTaskMapper {
* @return DdTask 对象
*/
DdTask selectByTaskNo(@Param("taskNo") String taskNo);
/**
* 批量新增调度任务
*/
int batchInsertDdTask(@Param("list") List<DdTask> list);
}

View File

@@ -0,0 +1,55 @@
package com.zg.project.wisdom.mqtt.dispatcher;
import com.zg.project.wisdom.mqtt.handler.HumiSensorHandler;
import com.zg.project.wisdom.mqtt.handler.SmokeSensorHandler;
import com.zg.project.wisdom.mqtt.handler.TempSensorHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* MQTT消息分发器
* 负责根据MQTT主题(topic)将消息分发给相应的传感器处理器
*/
@Slf4j
@Component
public class MqttMessageDispatcher {
@Autowired
private TempSensorHandler tempSensorHandler;
@Autowired
private HumiSensorHandler humiSensorHandler;
@Autowired
private SmokeSensorHandler smokeSensorHandler;
/**
* 分发MQTT消息到对应的处理器
* 根据topic的前缀判断消息类型并调用相应的处理器进行处理
*
* @param topic MQTT主题用于判断消息类型
* @param payload 消息负载,包含具体的传感器数据
*/
public void dispatch(String topic, String payload) {
// 处理温度传感器消息
if (topic.startsWith("zg/wms/test/temp/")) {
tempSensorHandler.handle(topic, payload);
return;
}
// 处理湿度传感器消息
if (topic.startsWith("zg/wms/test/humi/")) {
humiSensorHandler.handle(topic, payload);
return;
}
// 处理烟雾传感器消息
if (topic.startsWith("zg/wms/test/smoke/")) {
smokeSensorHandler.handle(topic, payload);
return;
}
log.warn("未知 MQTT Topic: {}", topic);
}
}

View File

@@ -0,0 +1,43 @@
package com.zg.project.wisdom.mqtt.handler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 湿度传感器 Handler
* Topic 示例zg/wms/test/humi/{deviceId}
*/
@Slf4j
@Component
public class HumiSensorHandler {
public static final String TOPIC_PREFIX = "zg/wms/test/humi/";
/**
* 处理湿度传感器消息
* @param topic MQTT主题
* @param payload 消息负载
*/
public void handle(String topic, String payload) {
String deviceId = resolveDeviceId(topic, TOPIC_PREFIX);
log.info("[HUMI] deviceId={}, topic={}, payload={}", deviceId, topic, payload);
// TODO: 这里后续写湿度业务逻辑
}
/**
* 从MQTT主题中解析设备ID
* @param topic MQTT主题
* @param prefix 主题前缀
* @return 设备ID
*/
private String resolveDeviceId(String topic, String prefix) {
if (topic == null || !topic.startsWith(prefix)) {
return "";
}
// 提取设备ID部分
return topic.substring(prefix.length());
}
}

View File

@@ -0,0 +1,47 @@
package com.zg.project.wisdom.mqtt.handler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 烟雾传感器 Handler
* Topic 示例zg/wms/test/smoke/{deviceId}
*/
@Slf4j
@Component
public class SmokeSensorHandler {
/**
* MQTT主题前缀用于烟雾传感器消息
*/
public static final String TOPIC_PREFIX = "zg/wms/test/smoke/";
/**
* 处理烟雾传感器消息
*
* @param topic MQTT主题
* @param payload 消息负载内容
*/
public void handle(String topic, String payload) {
String deviceId = resolveDeviceId(topic, TOPIC_PREFIX);
log.info("[SMOKE] deviceId={}, topic={}, payload={}", deviceId, topic, payload);
// TODO: 这里后续写烟雾业务逻辑
}
/**
* 从MQTT主题中解析设备ID
*
* @param topic 完整的MQTT主题
* @param prefix 主题前缀
* @return 设备ID如果主题格式不正确则返回空字符串
*/
private String resolveDeviceId(String topic, String prefix) {
if (topic == null || !topic.startsWith(prefix)) {
return "";
}
// 提取前缀后的设备ID部分
return topic.substring(prefix.length());
}
}

View File

@@ -0,0 +1,46 @@
package com.zg.project.wisdom.mqtt.handler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 温度传感器 Handler
* Topic 示例zg/wms/test/temp/{deviceId}
*/
@Slf4j
@Component
public class TempSensorHandler {
/**
* MQTT主题前缀用于温度传感器数据
*/
public static final String TOPIC_PREFIX = "zg/wms/test/temp/";
/**
* 处理温度传感器数据
*
* @param topic MQTT主题格式为 zg/wms/test/temp/{deviceId}
* @param payload 传感器数据载荷
*/
public void handle(String topic, String payload) {
String deviceId = resolveDeviceId(topic, TOPIC_PREFIX);
log.info("[TEMP] deviceId={}, topic={}, payload={}", deviceId, topic, payload);
// TODO: 这里后续写温度业务逻辑解析JSON、落库、告警等
}
/**
* 从MQTT主题中解析设备ID
*
* @param topic MQTT主题
* @param prefix 主题前缀
* @return 设备ID如果主题格式不正确则返回空字符串
*/
private String resolveDeviceId(String topic, String prefix) {
if (topic == null || !topic.startsWith(prefix)) {
return "";
}
return topic.substring(prefix.length());
}
}

View File

@@ -48,4 +48,10 @@ public interface IDdTaskService {
* @return
*/
TaskExecuteResultVO executeTask(TaskExecuteDTO dto);
/**
* 批量新增调度任务
*/
int insertDdTaskBatch(List<DdTask> list);
}

View File

@@ -29,15 +29,19 @@ public class QwenOcrRemoteService {
}
/**
* 接收上传的图片文件,转成 Base64 后,
* 上传的图片文件中提取ERP订单号
* 调用 250 上的 /ocr/extractErpByBase64 接口,返回采购订单号(ERP)
*
* @param file 上传的图片文件支持JPEG和PNG格式
* @return 提取到的ERP订单号如果未找到则返回空字符串
* @throws RuntimeException 当调用OCR服务失败或发生IO异常时抛出
*/
public String extractErpOrderNo(MultipartFile file) {
try {
// 1. 读取文件字节
// 读取文件字节内容
byte[] bytes = file.getBytes();
// 2. 推断 contentType
// 推断文件的content type,如果无法获取则根据文件扩展名判断
String contentType = file.getContentType();
if (contentType == null || contentType.isEmpty()) {
String name = file.getOriginalFilename();
@@ -48,11 +52,11 @@ public class QwenOcrRemoteService {
}
}
// 3. 转成 Base64并加上 data: 前缀
// 将图片字节转换为Base64编码并添加data URI前缀
String base64 = Base64.getEncoder().encodeToString(bytes);
String imageBase64 = "data:" + contentType + ";base64," + base64;
// 4. 组装 JSON 请求体{ "imageBase64": "data:image/jpeg;base64,xxxx" }
// 构建包含Base64图片数据的JSON请求体
String json = objectMapper.createObjectNode()
.put("imageBase64", imageBase64)
.toString();
@@ -69,7 +73,7 @@ public class QwenOcrRemoteService {
.post(body)
.build();
// 5. 发送 HTTP 请求
// 发送HTTP POST请求到OCR服务
Response response = httpClient.newCall(request).execute();
try {
if (!response.isSuccessful()) {
@@ -95,4 +99,5 @@ public class QwenOcrRemoteService {
throw new RuntimeException("调用远程 OCR 服务异常:" + e.getMessage(), e);
}
}
}

View File

@@ -214,29 +214,34 @@ public class AgvTaskResultServiceImpl implements IAgvTaskResultService {
* @param
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void handleOutGoods(OutGoodsDTO dto) {
String taskNo = dto.getTaskNo();
Integer materialStatus = dto.getMaterialStatus(); // ✅ 关键判断字段
Integer materialStatus = dto.getMaterialStatus();
String userId = SecurityUtils.getUserId().toString();
Date now = DateUtils.getNowDate();
// 1. 查询 AGV 原任务记录
AgvTaskResult task = agvTaskResultMapper.selectByTaskNo(taskNo);
if (task == null) {
throw new ServiceException("未找到任务:" + taskNo);
// 1️⃣ 先查调度主任务(关键)
DdTask ddTask = ddTaskMapper.selectByTaskNo(taskNo);
if (ddTask == null) {
throw new ServiceException("未找到调度任务:" + taskNo);
}
// 2. 调用 WCS 下架接口
Integer dispatchMode = ddTask.getDispatchMode(); // 1=仅WCS2=WCS+AGV
// 2⃣ 调用 WCS 下架(所有模式都要)
String wcsTaskId = taskNo + "22";
JSONObject wcsParam = new JSONObject();
wcsParam.put("TaskID", wcsTaskId);
wcsParam.put("TrayNo", "");
wcsParam.put("Materialstatus", materialStatus);
if (Objects.equals(materialStatus, 1)) {
wcsParam.put("MaterialCode", task.getMid());
wcsParam.put("Quantity", task.getNum());
String spec = mtdMapper.selectWlmsByWlbh(task.getMid());
wcsParam.put("MaterialCode", ddTask.getMid());
wcsParam.put("Quantity", ddTask.getNum());
String spec = mtdMapper.selectWlmsByWlbh(ddTask.getMid());
wcsParam.put("Specification", spec != null ? spec : "");
} else {
wcsParam.put("MaterialCode", "");
@@ -244,11 +249,10 @@ public class AgvTaskResultServiceImpl implements IAgvTaskResultService {
wcsParam.put("Specification", "");
}
wcsParam.put("Mlocation", task.getSourceName()); // 来源库位
wcsParam.put("Tlocation", task.getTargetName()); // 目标库位
wcsParam.put("outBack", 1); // 固定值
wcsParam.put("Mlocation", ddTask.getSourceName());
wcsParam.put("Tlocation", ddTask.getTargetName());
wcsParam.put("outBack", 1);
log.info("[下架] 调用 WCS 接口: {}", wcsParam.toJSONString());
String wcsResp = HttpUtils.sendPost(wcsJobCreateUrl, wcsParam.toJSONString());
if (StringUtils.isBlank(wcsResp)) {
throw new ServiceException("WCS接口无响应");
@@ -259,34 +263,51 @@ public class AgvTaskResultServiceImpl implements IAgvTaskResultService {
throw new ServiceException("WCS下架失败" + wcsJson.getString("msg"));
}
// 3. 记录 WCS 任务
// 3️⃣ 记录 WCS 任务
WcsTaskResult wcs = new WcsTaskResult();
wcs.setTaskId(wcsTaskId);
wcs.setTaskStatus("3"); // 等待下架
wcs.setTaskStatus("3"); // WCS已接收
wcs.setMsg(wcsJson.getString("msg"));
wcs.setOwner("wms");
wcs.setType("2");
wcs.setPriority("1");
wcs.setSourceName(task.getSourceName());
wcs.setTargetName(task.getTargetName());
wcs.setSourceName(ddTask.getSourceName());
wcs.setTargetName(ddTask.getTargetName());
wcs.setIsDelete("0");
wcs.setCreateBy(userId);
wcs.setCreateTime(now);
wcs.setUpdateTime(now);
wcsTaskResultMapper.insertWcsTaskResult(wcs);
// 4. 调用 AGV 出库搬运(下架点 → 出口)
// =========================
// 4⃣ 仅 WCS 模式:直接结束
// =========================
if (dispatchMode == 1) {
log.info("[下架] 仅WCS调度任务结束 taskNo={}", taskNo);
return;
}
// =========================
// 5⃣ WCS + AGV 模式
// =========================
// 查 AGV 原任务(只在 mode=2 时)
AgvTaskResult agvOrigin = agvTaskResultMapper.selectByTaskNo(taskNo);
if (agvOrigin == null) {
throw new ServiceException("未找到 AGV 原任务:" + taskNo);
}
String agvRequestId = taskNo + "12";
JSONObject agvParam = new JSONObject();
agvParam.put("requestId", agvRequestId);
agvParam.put("taskNo", taskNo);
agvParam.put("owner", "wms");
agvParam.put("type", 2); // 下架搬运
agvParam.put("type", 2);
agvParam.put("priority", "1");
agvParam.put("sourceName", task.getTargetName());
agvParam.put("sourceName", ddTask.getTargetName());
agvParam.put("targetName", "出口口A");
log.info("[下架] 调用 AGV 接口: {}", agvParam.toJSONString());
String agvResp = HttpUtils.sendPost(agvJobCreateUrl, agvParam.toJSONString());
if (StringUtils.isBlank(agvResp)) {
throw new ServiceException("AGV接口无响应");
@@ -297,16 +318,15 @@ public class AgvTaskResultServiceImpl implements IAgvTaskResultService {
throw new ServiceException("AGV搬运失败" + agvJson.getString("msg"));
}
// 5. 保存 AGV 出库搬运记录
AgvTaskResult agv = new AgvTaskResult();
agv.setRequestId(agvRequestId);
agv.setTaskNo(taskNo);
agv.setStatus("CREATED");
agv.setMsg(agvJson.getString("msg"));
agv.setOwner("wms");
agv.setType("2");
agv.setPriority("1");
agv.setTaskNo(taskNo);
agv.setSourceName(task.getTargetName());
agv.setSourceName(ddTask.getTargetName());
agv.setTargetName("出口口A");
agv.setIsDelete("0");
agv.setCreateBy(userId);
@@ -314,16 +334,14 @@ public class AgvTaskResultServiceImpl implements IAgvTaskResultService {
agv.setUpdateTime(now);
agvTaskResultMapper.insertAgvTaskResult(agv);
// 6. 保存 AGV/WCS 对应关系
AgyWcs mapping = new AgyWcs();
mapping.setRequestId(agvRequestId);
mapping.setTaskId(wcsTaskId);
agyWcsMapper.insertAgvWcs(mapping);
log.info("[下架] AGV 搬运任务提交成功");
}
@Override
public boolean existsByRequestIdAndStatus(String requestId, String status) {
return agvTaskResultMapper.countByRequestIdAndStatus(requestId, status) > 0;

View File

@@ -7,6 +7,7 @@ import com.zg.common.utils.DateUtils;
import com.zg.common.utils.SecurityUtils;
import com.zg.common.utils.StringUtils;
import com.zg.common.utils.http.OkHttpUtils;
import com.zg.common.utils.uuid.IdUtils;
import com.zg.project.information.mapper.MtdMapper;
import com.zg.project.wisdom.domain.AgvTaskResult;
import com.zg.project.wisdom.domain.AgyWcs;
@@ -54,9 +55,6 @@ public class DdTaskServiceImpl implements IDdTaskService {
@Value("${agv.job.create-url}")
private String agvJobCreateUrl;
@Value("${wcs.job.create-url}")
private String wcsJobCreateUrl;
@Value("${wcs.job.chuku-url}")
private String wcsJobChukuUrl;
@@ -78,31 +76,38 @@ public class DdTaskServiceImpl implements IDdTaskService {
@Override
public int insertDdTask(DdTask ddTask) {
// 1. 生成任务编号(如 DD20250701143059001
// 1. 生成任务编号
String taskNo = "DD" + DateUtils.dateTimeNow("yyyyMMddHHmmssSSS");
ddTask.setTaskNo(taskNo);
// 2. 判断 mid 是否为空,设置 midStatus0空托盘1有货
if (ddTask.getMid() == null || ddTask.getMid().trim().isEmpty()) {
// 2. midStatus 判断
if (StringUtils.isBlank(ddTask.getMid())) {
ddTask.setMidStatus(0);
} else {
ddTask.setMidStatus(1);
}
// 3. 设置任务初始状态为待建0
// 3. 初始任务状态
ddTask.setTaskStatus(0);
// 4. 设置操作员和审核员(从当前登录用户上下文中获取
// 4. 调度模式校验1=仅WCS2=WCS+AGV
// if (ddTask.getDispatchMode() == null
// || (ddTask.getDispatchMode() != 1 && ddTask.getDispatchMode() != 2)) {
// throw new ServiceException("dispatchMode非法只能是 1仅立库 或 2立库+AGV");
// }
ddTask.setDispatchMode(2);
// 5. 操作人 / 审核人
Long userId = SecurityUtils.getUserId();
ddTask.setOperator(userId.toString());
ddTask.setApprover(userId.toString());
// 5. 设置接收时间 rcptim 和初始化字段
// 6. 其他初始化
ddTask.setRcptim(DateUtils.getNowDate());
ddTask.setRid("");
ddTask.setDoCount(0);
// 6. 设置通用字段
ddTask.setCreateBy(userId.toString());
ddTask.setCreateTime(DateUtils.getNowDate());
ddTask.setUpdateBy(userId.toString());
@@ -112,6 +117,65 @@ public class DdTaskServiceImpl implements IDdTaskService {
return ddTaskMapper.insertDdTask(ddTask);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int insertDdTaskBatch(List<DdTask> list) {
if (list == null || list.isEmpty()) {
throw new ServiceException("调度任务数据不能为空");
}
Long userId = SecurityUtils.getUserId();
Date now = DateUtils.getNowDate();
for (DdTask ddTask : list) {
// 1⃣ 生成唯一任务号
String taskNo = "DD" + IdUtils.fastSimpleUUID();
ddTask.setTaskNo(taskNo);
// 2⃣ 调度模式校验1 / 2
if (ddTask.getDispatchMode() == null
|| (ddTask.getDispatchMode() != 1 && ddTask.getDispatchMode() != 2)) {
throw new ServiceException("dispatchMode非法只能是 1仅立库 或 2立库+AGV");
}
// 3⃣ midStatus
if (StringUtils.isBlank(ddTask.getMid())) {
ddTask.setMidStatus(0);
} else {
ddTask.setMidStatus(1);
}
// 4⃣ 初始状态
ddTask.setTaskStatus(0);
// 5⃣ 操作员 / 审核员
ddTask.setOperator(userId.toString());
ddTask.setApprover(userId.toString());
// 6⃣ 业务字段兜底
if (ddTask.getRcptim() == null) {
ddTask.setRcptim(now);
}
if (ddTask.getRid() == null) {
ddTask.setRid("");
}
if (ddTask.getDoCount() == null) {
ddTask.setDoCount(0);
}
// 7⃣ 通用字段
ddTask.setCreateBy(userId.toString());
ddTask.setCreateTime(now);
ddTask.setUpdateBy(userId.toString());
ddTask.setUpdateTime(now);
ddTask.setIsDelete("0");
}
return ddTaskMapper.batchInsertDdTask(list);
}
@Override
public int updateDdTask(DdTask ddTask) {
return ddTaskMapper.updateDdTask(ddTask);
@@ -219,53 +283,74 @@ public class DdTaskServiceImpl implements IDdTaskService {
wcs.setUpdateTime(now);
wcsTaskResultMapper.insertWcsTaskResult(wcs);
// 调用 AGV使用 OkHttpUtils
requestId = taskNo + "12";
JSONObject agvParam = new JSONObject();
agvParam.put("owner", "wms");
agvParam.put("type", "1");
agvParam.put("priority", 1);
agvParam.put("sourceName", "v01-010101");
agvParam.put("targetName", targetName);
agvParam.put("taskNo", taskNo);
agvParam.put("requestId", requestId);
// ✅ 新增:出库时根据 dispatch_mode 决定是否走 AGV
// dispatch_mode1=仅立库不走AGV2=立库+AGV走AGV
Integer dispatchMode = task.getDispatchMode();
if (Integer.valueOf(1).equals(dispatchMode) || dispatchMode == null) {
log.info("[任务执行] 出库任务dispatch_mode={}1=仅立库/空=默认仅立库),跳过 AGV 调度", dispatchMode);
task.setTaskStatus(2); // 已完成
task.setDoCount(Optional.ofNullable(task.getDoCount()).orElse(0) + 1);
task.setUpdateBy(userId);
task.setUpdateTime(now);
ddTaskMapper.updateDdTask(task);
log.info("[任务执行] 出库 → 调用AGVrequestId={}, param={}", requestId, agvParam);
response = OkHttpUtils.postJson(agvJobCreateUrl, agvParam.toJSONString());
log.info("[任务执行] 仅立库模式,立库调用成功即完成 taskNo={}", taskNo);
try {
respJson = JSON.parseObject(response);
code = respJson.getInteger("code");
msg = respJson.getString("msg");
} catch (Exception e) {
throw new ServiceException("AGV响应解析失败: " + response);
// ❗非常关键:直接 return后面不要再把状态改成 1
TaskExecuteResultVO vo = new TaskExecuteResultVO();
vo.setRequestId(null);
vo.setCode(200);
vo.setMsg("仅立库模式,任务已完成");
return vo;
} else {
// 调用 AGV使用 OkHttpUtils
requestId = taskNo + "12";
JSONObject agvParam = new JSONObject();
agvParam.put("owner", "wms");
agvParam.put("type", "1");
agvParam.put("priority", 1);
agvParam.put("sourceName", "v01-010101");
agvParam.put("targetName", targetName);
agvParam.put("taskNo", taskNo);
agvParam.put("requestId", requestId);
log.info("[任务执行] 出库 → 调用AGVrequestId={}, param={}", requestId, agvParam);
response = OkHttpUtils.postJson(agvJobCreateUrl, agvParam.toJSONString());
try {
respJson = JSON.parseObject(response);
code = respJson.getInteger("code");
msg = respJson.getString("msg");
} catch (Exception e) {
throw new ServiceException("AGV响应解析失败: " + response);
}
if (code != 200) {
throw new ServiceException("AGV任务执行失败: " + msg);
}
AgvTaskResult agv = new AgvTaskResult();
agv.setRequestId(requestId);
agv.setTaskNo(taskNo);
agv.setStatus("CREATED");
agv.setMsg(msg);
agv.setOwner("wms");
agv.setType(taskType);
agv.setPriority("1");
agv.setSourceName(task.getSourceName());
agv.setTargetName(targetName);
agv.setIsDelete("0");
agv.setCreateBy(userId);
agv.setCreateTime(now);
agv.setUpdateTime(now);
agvTaskResultMapper.insertAgvTaskResult(agv);
AgyWcs mapping = new AgyWcs();
mapping.setRequestId(requestId);
mapping.setTaskId(taskIdParam);
agyWcsMapper.insertAgvWcs(mapping);
}
if (code != 200) {
throw new ServiceException("AGV任务执行失败: " + msg);
}
AgvTaskResult agv = new AgvTaskResult();
agv.setRequestId(requestId);
agv.setTaskNo(taskNo);
agv.setStatus("CREATED");
agv.setMsg(msg);
agv.setOwner("wms");
agv.setType(taskType);
agv.setPriority("1");
agv.setSourceName(task.getSourceName());
agv.setTargetName(targetName);
agv.setIsDelete("0");
agv.setCreateBy(userId);
agv.setCreateTime(now);
agv.setUpdateTime(now);
agvTaskResultMapper.insertAgvTaskResult(agv);
AgyWcs mapping = new AgyWcs();
mapping.setRequestId(requestId);
mapping.setTaskId(taskIdParam);
agyWcsMapper.insertAgvWcs(mapping);
} else {
// 入库/移库任务 → 调用 AGV使用 OkHttpUtils
requestId = taskNo + ("0".equals(taskType) ? "11" : "12");
@@ -324,4 +409,5 @@ public class DdTaskServiceImpl implements IDdTaskService {
return vo;
}
}
}

View File

@@ -1,35 +1,94 @@
package com.zg.project.wisdom.service.impl;
import com.zg.common.utils.DateUtils;
import com.zg.project.wisdom.domain.DdTask;
import com.zg.project.wisdom.domain.WcsTaskResult;
import com.zg.project.wisdom.mapper.AgvTaskResultMapper;
import com.zg.project.wisdom.mapper.DdTaskMapper;
import com.zg.project.wisdom.mapper.WcsTaskResultMapper;
import com.zg.project.wisdom.service.IWcsTaskResultService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
@Slf4j
@Service
public class WcsTaskResultServiceImpl implements IWcsTaskResultService {
@Autowired
private WcsTaskResultMapper wcsTaskResultMapper;
@Autowired
private DdTaskMapper ddTaskMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public void handleWcsCallback(WcsTaskResult dto) {
String taskId = dto.getTaskId();
Date now = DateUtils.getNowDate();
WcsTaskResult result = new WcsTaskResult();
result.setTaskId(taskId);
result.setTaskStatus(dto.getTaskStatus());
result.setMsg(dto.getMsg());
result.setCreateBy("wcs");
result.setCreateTime(DateUtils.getNowDate());
result.setIsDelete("0");
// 1⃣ 先记录 WCS 回调日志
WcsTaskResult record = new WcsTaskResult();
record.setTaskId(taskId);
record.setTaskStatus(dto.getTaskStatus());
record.setMsg(dto.getMsg());
record.setOwner("wcs");
record.setIsDelete("0");
record.setCreateBy("wcs");
record.setCreateTime(now);
record.setUpdateTime(now);
wcsTaskResultMapper.insertWcsTaskResult(record);
wcsTaskResultMapper.insertWcsTaskResult(result);
}
// @Override
// @Transactional(rollbackFor = Exception.class)
// public void handleWcsCallback(WcsTaskResult dto) {
//
// String taskId = dto.getTaskId();
// Date now = DateUtils.getNowDate();
//
// // ✅ 仅立库TaskStatus=1 视为成功,并同步完成主任务
// if (taskId != null && taskId.length() >= 3) {
//
// String taskNo = taskId.substring(0, taskId.length() - 2);
// DdTask ddTask = ddTaskMapper.selectByTaskNo(taskNo);
//
// if (ddTask != null
// && Integer.valueOf(1).equals(ddTask.getDispatchMode())
// && "1".equals(dto.getTaskStatus())) {
//
// // 1) 回调日志状态1 -> 3成功
// dto.setTaskStatus("3");
//
// // 2) 主任务完成dd_task.task_status -> 2
// DdTask update = new DdTask();
// update.setId(ddTask.getId());
// update.setTaskStatus(2); // 已完成
// update.setUpdateBy("wcs");
// update.setUpdateTime(now);
// ddTaskMapper.updateDdTask(update);
// }
// }
//
// // 1⃣ 先记录 WCS 回调日志(原逻辑不变,只是 dto 可能已被修正为 3
// WcsTaskResult record = new WcsTaskResult();
// record.setTaskId(taskId);
// record.setTaskStatus(dto.getTaskStatus());
// record.setMsg(dto.getMsg());
// record.setOwner("wcs");
// record.setIsDelete("0");
// record.setCreateBy("wcs");
// record.setCreateTime(now);
// record.setUpdateTime(now);
// wcsTaskResultMapper.insertWcsTaskResult(record);
// }
@Override
public boolean checkTaskStatusExists(String taskId) {
return wcsTaskResultMapper.countByTaskId(taskId) > 0;

View File

@@ -7,9 +7,9 @@ spring:
# 主库数据源
master:
# url: jdbc:mysql://101.132.133.142:3306/wisdom?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://192.168.1.28:3306/wisdom?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# url: jdbc:mysql://192.168.1.28:3306/wisdom?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# url: jdbc:mysql://192.168.1.192:3306/wisdom?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# url: jdbc:mysql://192.168.1.251:3306/wisdom?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://192.168.1.251:3306/wisdom?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# url: jdbc:mysql://localhost:3306/wisdom?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: shzg

View File

@@ -169,4 +169,52 @@ mock:
#配送系统中调用大模型进行图片识别
qwen-ocr:
base-url: http://192.168.1.253:8087/ocr/extractErpByBase64
base-url: http://192.168.1.253:8087/ocr/extractErpByBase64
# =========================
# MQTT 配置EMQX 接入)
# =========================
mqtt:
# 是否启用 MQTT 功能
# true系统启动时自动连接 EMQX 并订阅 Topic
# false不初始化 MQTT 客户端
enabled: true
# MQTT Broker 地址
# tcp://IP:1883 —— MQTT TCP
# ws://IP:8083 —— MQTT WebSocket
# ssl://IP:8883 —— MQTT SSL
broker: tcp://192.168.1.29:1883
# MQTT 客户端 ID在 EMQX 中唯一)
# 建议:系统名 + 模块名,避免重复
clientId: zg-wms-backend
# MQTT 账号EMQX Dashboard 中配置)
username: demo02
# MQTT 密码
password: admin123
# 是否清除会话
# true :断开后清除订阅和未接收消息(推荐后端使用)
# false :保留会话(适合设备端)
cleanSession: true
# 心跳时间(秒)
# 客户端与 Broker 保持连接的心跳间隔
keepAlive: 30
# 连接超时时间(秒)
timeout: 10
# 默认消息 QoS
# 0最多一次不保证到达性能最好
# 1至少一次推荐业务数据常用
# 2仅一次最严格性能最差
qos: 1
# 订阅的 Topic 列表(支持通配符)
# 建议先使用测试 Topic确认链路正常后再接入真实业务 Topic
topics:
- zg/wms/test/#

View File

@@ -71,6 +71,9 @@
<select id="selectByTaskNo" resultType="com.zg.project.wisdom.domain.AgvTaskResult">
SELECT * FROM agv_task_result WHERE task_no = #{taskNo} AND is_delete = '0'
</select>
<select id="countByTaskNo" resultType="java.lang.Integer">
SELECT COUNT(*) FROM agv_task_result WHERE task_no = #{taskNo} AND is_delete = '0'
</select>
<insert id="insertAgvTaskResult" parameterType="AgvTaskResult" useGeneratedKeys="true" keyProperty="id">
INSERT INTO agv_task_result

View File

@@ -1,34 +1,41 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zg.project.wisdom.mapper.DdTaskMapper">
<!-- ======================= ResultMap ======================= -->
<resultMap id="DdTaskResult" type="com.zg.project.wisdom.domain.DdTask">
<result property="id" column="id" />
<result property="taskNo" column="task_no" />
<result property="taskDtl" column="task_dtl" />
<result property="taskType" column="task_type" />
<result property="taskStatus" column="task_status" />
<result property="midStatus" column="mid_status" />
<result property="mid" column="mid" />
<result property="num" column="num" />
<result property="midType" column="mid_type" />
<result property="sourceName" column="source_name" />
<result property="targetName" column="target_name" />
<result property="operator" column="operator" />
<result property="approver" column="approver" />
<result property="rcptim" column="rcptim" />
<result property="rid" column="rid" />
<result property="doCount" column="do_count" />
<result property="prf" column="prf" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="isDelete" column="is_delete" />
<result property="id" column="id"/>
<result property="taskNo" column="task_no"/>
<result property="taskDtl" column="task_dtl"/>
<result property="taskType" column="task_type"/>
<result property="taskStatus" column="task_status"/>
<result property="midStatus" column="mid_status"/>
<result property="mid" column="mid"/>
<result property="num" column="num"/>
<result property="midType" column="mid_type"/>
<result property="sourceName" column="source_name"/>
<result property="targetName" column="target_name"/>
<result property="operator" column="operator"/>
<result property="approver" column="approver"/>
<result property="rcptim" column="rcptim"/>
<result property="rid" column="rid"/>
<result property="doCount" column="do_count"/>
<result property="prf" column="prf"/>
<result property="dispatchMode" column="dispatch_mode"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="isDelete" column="is_delete"/>
</resultMap>
<!-- ======================= 查询列表 ======================= -->
<select id="selectDdTaskList" resultMap="DdTaskResult">
SELECT * FROM dd_task
SELECT *
FROM dd_task
<where>
<if test="taskNo != null and taskNo != ''">
AND task_no = #{taskNo}
@@ -39,6 +46,9 @@
<if test="taskStatus != null">
AND task_status = #{taskStatus}
</if>
<if test="dispatchMode != null">
AND dispatch_mode = #{dispatchMode}
</if>
<if test="isDelete != null">
AND is_delete = #{isDelete}
</if>
@@ -46,30 +56,133 @@
ORDER BY id DESC
</select>
<!-- ======================= 根据ID查询 ======================= -->
<select id="selectDdTaskById" resultMap="DdTaskResult">
SELECT * FROM dd_task WHERE id = #{id} AND is_delete = '0'
SELECT *
FROM dd_task
WHERE id = #{id}
AND is_delete = '0'
</select>
<!-- 根据任务号查询 -->
<!-- ======================= 根据任务号查询 ======================= -->
<select id="selectByTaskNo" resultMap="DdTaskResult" parameterType="String">
SELECT * FROM dd_task WHERE task_no = #{taskNo} AND is_delete = '0' LIMIT 1
SELECT *
FROM dd_task
WHERE task_no = #{taskNo}
AND is_delete = '0'
LIMIT 1
</select>
<insert id="insertDdTask" parameterType="DdTask">
<!-- ======================= 新增调度任务 ======================= -->
<insert id="insertDdTask" parameterType="com.zg.project.wisdom.domain.DdTask">
INSERT INTO dd_task (
task_no, task_dtl, task_type, task_status, mid_status, mid, num, mid_type, source_name,
target_name, operator, approver, rcptim, rid, do_count, prf,
create_by, create_time, update_by, update_time, is_delete
task_no,
task_dtl,
task_type,
task_status,
mid_status,
mid,
num,
mid_type,
source_name,
target_name,
operator,
approver,
rcptim,
rid,
do_count,
prf,
dispatch_mode,
create_by,
create_time,
update_by,
update_time,
is_delete
) VALUES (
#{taskNo}, #{taskDtl}, #{taskType}, #{taskStatus}, #{midStatus}, #{mid}, #{num}, #{midType}, #{sourceName},
#{targetName}, #{operator}, #{approver}, #{rcptim}, #{rid}, #{doCount}, #{prf},
#{createBy}, #{createTime}, #{updateBy}, #{updateTime}, #{isDelete}
#{taskNo},
#{taskDtl},
#{taskType},
#{taskStatus},
#{midStatus},
#{mid},
#{num},
#{midType},
#{sourceName},
#{targetName},
#{operator},
#{approver},
#{rcptim},
#{rid},
#{doCount},
#{prf},
#{dispatchMode},
#{createBy},
#{createTime},
#{updateBy},
#{updateTime},
#{isDelete}
)
</insert>
<update id="updateDdTask" parameterType="DdTask">
<!-- ======================= 批量新增调度任务 ======================= -->
<insert id="batchInsertDdTask">
INSERT INTO dd_task (
task_no,
task_dtl,
task_type,
task_status,
mid_status,
mid,
num,
mid_type,
source_name,
target_name,
operator,
approver,
rcptim,
rid,
do_count,
prf,
dispatch_mode,
create_by,
create_time,
update_by,
update_time,
is_delete
)
VALUES
<foreach collection="list" item="item" separator=",">
(
#{item.taskNo},
#{item.taskDtl},
#{item.taskType},
#{item.taskStatus},
#{item.midStatus},
#{item.mid},
#{item.num},
#{item.midType},
#{item.sourceName},
#{item.targetName},
#{item.operator},
#{item.approver},
#{item.rcptim},
#{item.rid},
#{item.doCount},
#{item.prf},
#{item.dispatchMode},
#{item.createBy},
#{item.createTime},
#{item.updateBy},
#{item.updateTime},
#{item.isDelete}
)
</foreach>
</insert>
<!-- ======================= 修改调度任务 ======================= -->
<update id="updateDdTask" parameterType="com.zg.project.wisdom.domain.DdTask">
UPDATE dd_task
SET task_no = #{taskNo},
SET
task_dtl = #{taskDtl},
task_type = #{taskType},
task_status = #{taskStatus},
@@ -85,12 +198,14 @@
rid = #{rid},
do_count = #{doCount},
prf = #{prf},
dispatch_mode = #{dispatchMode},
update_by = #{updateBy},
update_time = #{updateTime},
is_delete = #{isDelete}
update_time = #{updateTime}
WHERE id = #{id}
AND is_delete = '0'
</update>
<!-- ======================= 根据任务号更新状态 ======================= -->
<update id="updateTaskStatusByTaskNo">
UPDATE dd_task
SET task_status = #{status},
@@ -99,15 +214,19 @@
AND is_delete = '0'
</update>
<!-- ======================= 批量删除 ======================= -->
<delete id="deleteDdTaskByIds">
DELETE FROM dd_task WHERE id IN
DELETE FROM dd_task
WHERE id IN
<foreach collection="array" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<!-- ======================= 单条删除 ======================= -->
<delete id="deleteDdTaskById">
DELETE FROM dd_task WHERE id = #{id}
DELETE FROM dd_task
WHERE id = #{id}
</delete>
</mapper>