Compare commits

...

12 Commits

Author SHA1 Message Date
14944d2a99 出入库记录接口更新 2026-01-20 14:02:20 +08:00
a82797c475 移库记录去掉库位和仓库的对应关系校验
施工队新增时去掉编号,由代码生成
新增出入库记录查询接口
2026-01-20 10:20:39 +08:00
f8600107ec bug修改 2026-01-19 10:16:14 +08:00
79807279c1 bug修改 2026-01-16 11:40:44 +08:00
e4f617fc99 Merge remote-tracking branch 'origin/master' 2026-01-14 08:14:19 +08:00
1a565dba0d 生成单据号逻辑修改
线程池线程数修改
2026-01-14 08:14:04 +08:00
c3731b8b31 修改test 2026-01-09 15:09:34 +08:00
5d227b7b6b 去除mqtt功能
修改查询接口
2026-01-08 11:02:34 +08:00
f3d993b33a bug修复20260106 2026-01-06 14:19:58 +08:00
048d52b449 bug修复 2026-01-05 08:10:39 +08:00
2b7e91e918 调度模块添加仅立库模式
新增mqtt功能
新增施工队信息导入功能
2025-12-31 14:51:58 +08:00
b0bbeeec7c 生成出入库单据逻辑修改 2025-12-25 09:42:51 +08:00
51 changed files with 2521 additions and 1074 deletions

344
pom.xml
View File

@@ -1,197 +1,205 @@
<?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 +212,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 +248,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 +310,6 @@
</configuration>
</plugin>
<!-- 关键:固定编译插件版本,并指定 JDK8 的 source/target -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
@@ -308,35 +317,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

@@ -1116,6 +1116,9 @@ public class ExcelUtil<T>
}
}
/**
* 添加单元格
*/
/**
* 添加单元格
*/
@@ -1126,11 +1129,13 @@ public class ExcelUtil<T>
{
// 设置行高
row.setHeight(maxHeight);
// 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
// 根据Excel中设置情况决定是否导出
if (attr.isExport())
{
// 创建cell
cell = row.createCell(column);
if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge())
{
if (subMergedLastRowNum >= subMergedFirstRowNum)
@@ -1138,14 +1143,45 @@ public class ExcelUtil<T>
sheet.addMergedRegion(new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column));
}
}
cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText())));
// 用于读取对象中的属性
Object value = getTargetValue(vo, field, attr);
// =========================
// ✅ BigDecimal按“实际几位显示几位”去掉末尾0核心修改点
// =========================
if (value instanceof BigDecimal)
{
BigDecimal bd = (BigDecimal) value;
if (-1 != attr.scale())
{
bd = bd.setScale(attr.scale(), attr.roundingMode());
}
// 去掉末尾01.000000 -> 11.2300 -> 1.23
String text = bd.stripTrailingZeros().toPlainString();
// 强制按字符串写入,避免 Excel 数值格式导致显示成固定小数位
cell.setCellType(CellType.STRING);
cell.setCellValue(StringUtils.isEmpty(text) ? attr.defaultValue() : text + attr.suffix());
// 统计
addStatisticsData(column, text, attr);
return cell;
}
// =========================
// 原有逻辑(非 BigDecimal保持不变
// =========================
String dateFormat = attr.dateFormat();
String readConverterExp = attr.readConverterExp();
String separator = attr.separator();
String dictType = attr.dictType();
if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value))
{
cell.getCellStyle().setDataFormat(this.wb.getCreationHelper().createDataFormat().getFormat(dateFormat));
@@ -1164,10 +1200,6 @@ public class ExcelUtil<T>
}
cell.setCellValue(sysDictMap.get(dictType + value));
}
else if (value instanceof BigDecimal && -1 != attr.scale())
{
cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue());
}
else if (!attr.handler().equals(ExcelHandlerAdapter.class))
{
cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell));
@@ -1177,6 +1209,7 @@ public class ExcelUtil<T>
// 设置列类型
setCellVo(value, attr, cell);
}
addStatisticsData(column, Convert.toStr(value), attr);
}
}

View File

@@ -18,16 +18,16 @@ import java.util.concurrent.ThreadPoolExecutor;
public class ThreadPoolConfig
{
// 核心线程池大小
private int corePoolSize = 50;
private int corePoolSize = 16;
// 最大可创建的线程数
private int maxPoolSize = 200;
private int maxPoolSize = 32;
// 队列最大长度
private int queueCapacity = 1000;
// 线程池维护线程所允许的空闲时间
private int keepAliveSeconds = 300;
private int keepAliveSeconds = 120;
@Bean(name = "threadPoolTaskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor()

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

@@ -38,6 +38,7 @@ public class PcdeDetailController extends BaseController
public TableDataInfo list(PcdeDetail pcdeDetail)
{
startPage();
System.out.printf("111111");
List<PcdeDetail> list = pcdeDetailService.selectPcdeDetailList(pcdeDetail);
return getDataTable(list);
}

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

@@ -19,7 +19,7 @@ public class Mtd extends BaseEntity
private Long Id;
/** 物料号 */
@Excel(name = "物料号")
@Excel(name = "物料")
private String mid;
/** 物料描述 */

View File

@@ -2,6 +2,7 @@ package com.zg.project.information.mapper;
import java.util.List;
import com.zg.project.information.domain.ConstructionTeam;
import io.lettuce.core.dynamic.annotation.Param;
/**
* 施工队信息Mapper接口
@@ -35,6 +36,12 @@ public interface ConstructionTeamMapper
*/
public int insertConstructionTeam(ConstructionTeam constructionTeam);
/**
* 查询当前最大施工队编号
*/
String selectMaxTeamCode();
/**
* 修改施工队信息
*
@@ -58,4 +65,8 @@ public interface ConstructionTeamMapper
* @return 结果
*/
public int deleteConstructionTeamByIds(Long[] ids);
ConstructionTeam selectByTeamCode(@Param("teamCode") String teamCode);
ConstructionTeam selectByTeamName(@Param("teamName") String teamName);
}

View File

@@ -89,4 +89,7 @@ public interface PcdeDetailMapper
String selectWarehouseCodeByPcode(String pcode);
String selectEncodedIdByPcodeAndWarehouse(@Param("pcode") String pcode,
@Param("warehouseCode") String warehouseCode);
}

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,12 +1,20 @@
package com.zg.project.information.service.impl;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
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;
import com.zg.project.information.domain.ConstructionTeam;
import com.zg.project.information.service.IConstructionTeamService;
import static com.zg.common.utils.SecurityUtils.getUsername;
/**
* 施工队信息Service业务层处理
*
@@ -52,9 +60,36 @@ public class ConstructionTeamServiceImpl implements IConstructionTeamService
@Override
public int insertConstructionTeam(ConstructionTeam constructionTeam)
{
// 1⃣ 自动生成施工队编号6 位)
String teamCode = generateTeamCode();
constructionTeam.setTeamCode(teamCode);
// 2⃣ 补充若依通用字段(如果你没用 BaseEntity 自动处理)
constructionTeam.setCreatedBy(getUsername());
constructionTeam.setCreatedAt(new Date());
constructionTeam.setIsDelete("0");
return constructionTeamMapper.insertConstructionTeam(constructionTeam);
}
/**
* 生成施工队编号CT + 6位数字
* 示例CT000001
*/
private String generateTeamCode() {
String maxCode = constructionTeamMapper.selectMaxTeamCode();
if (maxCode == null) {
return "CT000001";
}
// 取数字部分
int num = Integer.parseInt(maxCode.substring(2));
num++;
return String.format("CT%06d", num);
}
/**
* 修改施工队信息
*
@@ -90,4 +125,125 @@ 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();
// ===== ① Excel 内去重 =====
Set<String> teamCodeSet = new HashSet<>();
Set<String> teamNameSet = new HashSet<>();
for (ConstructionTeam team : teamList)
{
String teamCode = team.getTeamCode();
String teamName = team.getTeamName();
if (StringUtils.isBlank(teamCode) || StringUtils.isBlank(teamName))
{
failureNum++;
failureMsg.append("<br/>施工队名称/编号不能为空,已跳过。");
continue;
}
if (!teamCodeSet.add(teamCode))
{
failureNum++;
failureMsg.append("<br/>施工队编号重复Excel 内):").append(teamCode);
continue;
}
if (!teamNameSet.add(teamName))
{
failureNum++;
failureMsg.append("<br/>施工队名称重复Excel 内):").append(teamName);
continue;
}
}
// ===== ② 数据库内去重 =====
for (ConstructionTeam team : teamList)
{
try
{
// 跳过前面校验失败的数据
if (StringUtils.isBlank(team.getTeamCode()) || StringUtils.isBlank(team.getTeamName()))
{
continue;
}
// 按编号查
ConstructionTeam existByCode =
constructionTeamMapper.selectByTeamCode(team.getTeamCode());
if (existByCode != null)
{
failureNum++;
failureMsg.append("<br/>施工队编号已存在:").append(team.getTeamCode());
continue;
}
// 按名称查
ConstructionTeam existByName =
constructionTeamMapper.selectByTeamName(team.getTeamName());
if (existByName != null)
{
failureNum++;
failureMsg.append("<br/>施工队名称已存在:").append(team.getTeamName());
continue;
}
// ===== 新增 =====
team.setCreatedBy(operName);
team.setCreatedAt(now);
team.setUpdatedBy(operName);
team.setUpdatedAt(now);
if (StringUtils.isBlank(team.getIsDelete()))
{
team.setIsDelete("0");
}
constructionTeamMapper.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,
"导入完成,成功 " + successNum + " 条,失败 " + failureNum + " 条:");
throw new ServiceException(failureMsg.toString());
}
return "导入成功,共 " + successNum + " 条数据";
}
}

View File

@@ -7,6 +7,8 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import com.zg.common.exception.ServiceException;
import com.zg.common.utils.StringUtils;
import com.zg.project.wisdom.domain.GysJh;
import com.zg.project.wisdom.domain.vo.PlanToMtdVO;
import com.zg.project.wisdom.mapper.GysJhMapper;
import org.springframework.beans.factory.annotation.Autowired;
@@ -67,9 +69,37 @@ public class MtdServiceImpl implements IMtdService
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int insertMtd(Mtd mtd)
{
return mtdMapper.insertMtd(mtd);
if (mtd == null || StringUtils.isBlank(mtd.getMid())) {
throw new ServiceException("物料号不能为空");
}
// 1⃣ 先新增物料字典
int rows = mtdMapper.insertMtd(mtd);
// 2⃣ 新增成功后,开始“反查供应计划”
if (rows > 0 && StringUtils.isNotBlank(mtd.getUnt())) {
String wlNo = mtd.getMid().trim();
String dw = mtd.getUnt().trim();
// 3⃣ 根据物料号查询供应计划(可能多条)
List<GysJh> jhList = gysJhMapper.selectByWlNo(wlNo);
if (jhList != null && !jhList.isEmpty()) {
for (GysJh jh : jhList) {
// 4⃣ 只处理「供应计划中单位为空」的记录
if (StringUtils.isBlank(jh.getDw())) {
gysJhMapper.updateDwById(jh.getId(), dw);
}
}
}
}
return rows;
}
/**

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 状态
*/
@@ -80,11 +80,13 @@ public class AgvTaskResultController extends BaseController {
*/
@PostMapping("/upGoods")
public AjaxResult upGoods(@RequestBody OutGoodsDTO dto) {
if (StringUtils.isBlank(dto.getTaskNo())) {
throw new ServiceException("taskNo 不能为空");
}
// 返回 taskId
String taskId = agvTaskResultService.handleUpGoods(dto.getTaskNo(), dto.getMaterialStatus());
return AjaxResult.success("上架任务已提交至WCS").put("taskId", taskId);
}

View File

@@ -22,7 +22,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
* @author zg
*/
@RestController
@RequestMapping("/wisdom/task")
@RequestMapping("/wisdom/dispatch")
public class DdTaskController extends BaseController {
@Autowired
@@ -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

@@ -33,7 +33,7 @@ public class GysJhController extends BaseController
@Autowired
private IGysJhService gysJhService;
/**A
/**
* 查询供应计划列表
*/
@PreAuthorize("@ss.hasPermi('plan:jh:list')")
@@ -50,12 +50,10 @@ public class GysJhController extends BaseController
* @param sapNo
* @return
*/
@PreAuthorize("@ss.hasPermi('plan:jh:list')")
// @PreAuthorize("@ss.hasPermi('plan:jh:list')")
@GetMapping("/getBySapNo")
public AjaxResult getBySapNo(String sapNo)
{
List<GysJh> list = gysJhService.getBySapNo(sapNo);
return success(list);
public AjaxResult getBySapNo(@RequestParam String sapNo) {
return gysJhService.getBySapNo(sapNo);
}
/**
@@ -81,7 +79,6 @@ public class GysJhController extends BaseController
ExcelUtil<GysJh> util = new ExcelUtil<>(GysJh.class);
List<GysJh> jhList = util.importExcel(file.getInputStream());
String operName = getUsername();
String message = gysJhService.importGysJhList(jhList, operName);
return AjaxResult.success(message);
}

View File

@@ -6,11 +6,11 @@ import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import com.github.pagehelper.PageHelper;
import com.zg.common.utils.StringUtils;
import com.zg.project.wisdom.domain.dto.*;
import com.zg.project.wisdom.domain.vo.DeliveryBillVO;
import com.zg.project.wisdom.domain.vo.PcodeQtyVO;
import com.zg.project.wisdom.service.QwenOcrRemoteService;
import io.swagger.annotations.ApiOperation;
import com.zg.project.wisdom.service.RkStatisticsService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -51,7 +51,6 @@ public class RkInfoController extends BaseController
query.getPageNum() == null ? 1 : query.getPageNum(),
query.getPageSize() == null ? 10 : query.getPageSize()
);
List<RkInfo> list = rkInfoService.selectRkInfoList(query);
return getDataTable(list);
}
@@ -68,23 +67,21 @@ public class RkInfoController extends BaseController
return getDataTable(list);
}
/**
* 库存查询
* @param dto
* @return
*/
@PostMapping("/pageStatistics")
public Map<String, Object> pageStatistics(@RequestBody RkInfoQueryDTO dto) {
// 使用 PageHelper 分页
PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
List<RkInfo> list = rkInfoService.selectAllRkInfo(dto);
// 统计金额
Long sumMoney = rkInfoService.selectStatistics(dto);
// 统计库位
Long pcdeCount = rkInfoService.selectPcde(dto);
Map<String,Object> dataInfo = new HashMap<>();
dataInfo.put("dataList",getDataTable(list));
dataInfo.put("sumMoney",sumMoney);
dataInfo.put("pcdeCount",pcdeCount);
return dataInfo;
List<RkInfo> list = rkInfoService.selectAllRkInfo(dto);
Map<String, Object> dataInfo = new HashMap<>();
dataInfo.put("dataList", getDataTable(list));
return dataInfo;
}
/**
@@ -104,14 +101,51 @@ public class RkInfoController extends BaseController
}
// @PreAuthorize("@ss.hasPermi('wisdom:stock:list')")
@PostMapping("/bill/groups")
public TableDataInfo billGroups(@RequestBody RkInfoQueryDTO query) {
// 分页
PageHelper.startPage(query.getPageNum(), query.getPageSize());
// 查询
List<RkInfo> rows = rkInfoService.selectGroupedByBill(query);
// @PostMapping("/bill/groups")
// public TableDataInfo billGroups(@RequestBody RkInfoQueryDTO query) {
// // 分页
// PageHelper.startPage(query.getPageNum(), query.getPageSize());
// // 查询
// List<RkInfo> rows = rkInfoService.selectGroupedByBill(query);
//
// return getDataTable(rows);
// }
return getDataTable(rows);
/**
* 获取单据列表(轻量级:仅返回单据头公共信息,不聚合明细字段)
*/
@PostMapping("/bill/groups")
public TableDataInfo billHeaderList(@RequestBody RkInfoQueryDTO query) {
// 1. 分页
PageHelper.startPage(query.getPageNum(), query.getPageSize());
// 2. 查询
List<RkInfo> list = rkInfoService.selectBillHeaderList(query);
return getDataTable(list);
}
/**
* 导出出入库记录
*/
@PreAuthorize("@ss.hasPermi('wisdom:stock:export')")
@Log(title = "出入库记录", businessType = BusinessType.EXPORT)
@PostMapping("/exportPageList")
public void exportPageList(HttpServletResponse response, RkInfoQueryDTO query)
{
List<RkInfo> list = rkInfoService.selectRkInfoPageList(query);
ExcelUtil<RkInfo> util = new ExcelUtil<>(RkInfo.class);
util.exportExcel(response, list, "出入库记录导出");
}
/**
* 出入库查询
*/
@PostMapping("/pageList")
public TableDataInfo pageList(@RequestBody RkInfoQueryDTO dto) {
PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
List<RkInfo> list = rkInfoService.selectRkInfoPageList(dto);
return getDataTable(list);
}
/**
@@ -124,7 +158,7 @@ public class RkInfoController extends BaseController
{
List<RkInfo> list = rkInfoService.selectRkInfoList(rkInfo);
ExcelUtil<RkInfo> util = new ExcelUtil<RkInfo>(RkInfo.class);
util.exportExcel(response, list, "库存单据主数据");
util.exportExcel(response, list, "数据导出");
}
/**
@@ -153,12 +187,25 @@ public class RkInfoController extends BaseController
*/
@PreAuthorize("@ss.hasPermi('wisdom:stock:edit')")
@Log(title = "库存单据主", businessType = BusinessType.UPDATE)
@PutMapping("/update")
@PostMapping("/update")
public AjaxResult edit(@RequestBody RkInfo rkInfo)
{
return toAjax(rkInfoService.updateRkInfo(rkInfo));
}
/**
* 根据出库单据号修改库存单据
*/
@PreAuthorize("@ss.hasPermi('wisdom:stock:edit')")
@Log(title = "库存单据-按出库单号修改", businessType = BusinessType.UPDATE)
@PostMapping("/updateByBillNoCk")
public AjaxResult updateByBillNoCk(@RequestBody RkInfo rkInfo) {
if (StringUtils.isBlank(rkInfo.getBillNoCk())) {
return AjaxResult.error("出库单据号 billNoCk 不能为空");
}
return toAjax(rkInfoService.updateRkInfoByBillNoCk(rkInfo));
}
/**
* 删除库存单据主
*/
@@ -360,4 +407,5 @@ public class RkInfoController extends BaseController
}
}

View File

@@ -1,6 +1,7 @@
package com.zg.project.wisdom.controller;
import com.zg.framework.web.domain.AjaxResult;
import com.zg.project.wisdom.domain.dto.RkInfoQueryDTO;
import com.zg.project.wisdom.domain.vo.*;
import com.zg.project.wisdom.service.RkStatisticsService;
import com.zg.project.wisdom.service.WarehouseStatService;
@@ -178,4 +179,10 @@ public class RkStatisticsController {
List<IOBucketVO> rows = rkStatisticsService.getIOBuckets(range);
return AjaxResult.success(rows);
}
@PostMapping("/statistics")
public Map<String, Object> statistics(@RequestBody RkInfoQueryDTO dto) {
// 调用新的合并查询方法
return rkStatisticsService.selectStockStatistics(dto);
}
}

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

@@ -1,6 +1,9 @@
package com.zg.project.wisdom.domain;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.zg.framework.aspectj.lang.annotation.Excel;
@@ -19,6 +22,10 @@ public class GysJh extends BaseEntity
/** 主键ID */
private Long id;
/** 行号 */
@Excel(name = "行号")
private String xh;
/** 序号 */
@Excel(name = "序号")
private Long indexNo;
@@ -59,28 +66,24 @@ public class GysJh extends BaseEntity
@Excel(name = "合同单价")
private BigDecimal htDj;
/** 合同数量 */
@Excel(name = "合同数量")
private BigDecimal htQty;
/** SAP订单编号 */
@Excel(name = "SAP订单编号")
private String sapNo;
/** 行号 */
@Excel(name = "行号")
private String xh;
/** 计划交货数量 */
@Excel(name = "计划交货数量")
private Long jhQty;
/** 合同数量 */
@Excel(name = "合同数量")
private Long htQty;
private BigDecimal jhQty;
/** 计量单位 */
@Excel(name = "计量单位")
private String dw;
/** 0未到货1已入库2部分入库 */
@Excel(name = "0未到货1已入库2部分入库")
// @Excel(name = "0未到货,1已入库,2部分入库")
private String status;
/** 身份码 */
@@ -92,9 +95,17 @@ public class GysJh extends BaseEntity
private String remark;
/** 是否删除0正常 1删除 */
@Excel(name = "是否删除", readConverterExp = "0=正常,1=删除")
// @Excel(name = "是否删除", readConverterExp = "0=正常,1=删除")
private String isDelete;
/** 查询开始时间yyyy-MM-dd HH:mm:ss */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date beginTime;
/** 查询结束时间yyyy-MM-dd HH:mm:ss */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date endTime;
public void setId(Long id)
{
this.id = id;
@@ -225,25 +236,11 @@ public class GysJh extends BaseEntity
return xh;
}
public void setJhQty(Long jhQty)
{
this.jhQty = jhQty;
}
public void setJhQty(BigDecimal jhQty) { this.jhQty = jhQty; }
public BigDecimal getJhQty() { return jhQty; }
public Long getJhQty()
{
return jhQty;
}
public void setHtQty(Long htQty)
{
this.htQty = htQty;
}
public Long getHtQty()
{
return htQty;
}
public void setHtQty(BigDecimal htQty) { this.htQty = htQty; }
public BigDecimal getHtQty() { return htQty; }
public void setDw(String dw)
{
@@ -295,6 +292,23 @@ public class GysJh extends BaseEntity
return isDelete;
}
public Date getBeginTime() {
return beginTime;
}
public void setBeginTime(Date beginTime) {
this.beginTime = beginTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

View File

@@ -29,8 +29,12 @@ public class RkInfo extends BaseEntity {
@TableField(exist = false)
private List<Long> ids;
/** 是否已出库0已入库1已出库2待审批3借料出库4入库撤销5出库撤销 */
@Excel(name = "库存状态", readConverterExp = "0=已入库,1=已出库,2=待审批,3=借料出库,4=入库撤销,5=出库撤销")
private String isChuku;
/** 供应计划ID对应供应计划表主键 */
@Excel(name = "供应计划ID")
// @Excel(name = "供应计划ID")
private Long gysJhId;
/** 审批人ID非数据库字段 */
@@ -70,7 +74,7 @@ public class RkInfo extends BaseEntity {
/** 所属大仓编码 */
@TableField(exist = false)
@Excel(name = "所属大仓编码")
// @Excel(name = "所属大仓编码")
private String parentWarehouseCode;
/** 所属大仓名称 */
@@ -80,7 +84,7 @@ public class RkInfo extends BaseEntity {
/** 所属小仓编码 */
@TableField(exist = false)
@Excel(name = "所属小仓编码")
// @Excel(name = "所属小仓编码")
private String warehouseCode;
/** 所属小仓名称 */
@@ -105,16 +109,13 @@ public class RkInfo extends BaseEntity {
private Date returnTime;
/** 理货员 */
@Excel(name = "理货员")
// @Excel(name = "理货员")
private String lihuoY;
/** 理货员名称(联查显示用,导出专用) */
@Excel(name = "理货员")
private String lihuoYName;
/** 是否已出库0已入库1已出库2待审批3借料出库4入库撤销5出库撤销 */
@Excel(name = "是否已出库", readConverterExp = "0已入库1已出库2待审批3借料出库4入库撤销5出库撤销")
private String isChuku;
/** 单据号 */
@Excel(name = "单据号")
private String billNo;
@@ -124,11 +125,11 @@ public class RkInfo extends BaseEntity {
private String billNoCk;
/** 是否需要配送(0否 1是 2配送中 3配送完成) */
@Excel(name = "是否需要配送", readConverterExp = "0=否,1=是,2=配送中,3=配送完成")
// @Excel(name = "是否需要配送", readConverterExp = "0=否,1=是,2=配送中,3=配送完成")
private String isDelivery;
/** 县局 */
@Excel(name = "县局")
// @Excel(name = "县局")
private String xj;
/** 项目号 */
@@ -139,10 +140,10 @@ public class RkInfo extends BaseEntity {
@Excel(name = "库存项目描述")
private String xmMs;
@Excel(name = "领取方项目号")
// @Excel(name = "领取方项目号")
private String xmNoCk;
@Excel(name = "领取方项目描述")
// @Excel(name = "领取方项目描述")
private String xmMsCk;
/** 物料号 */
@@ -174,7 +175,7 @@ public class RkInfo extends BaseEntity {
private String sapNo;
/** 行号 */
@Excel(name = "行号")
// @Excel(name = "行号")
private String xh;
/** 计划交货数量 */
@@ -193,12 +194,19 @@ public class RkInfo extends BaseEntity {
@Excel(name = "实际入库数量")
private BigDecimal realQty;
/** 总金额 = 合同单价 × 实际入库数量(导出专用) */
@Excel(name = "总金额")
private BigDecimal totalAmount;
@Excel(name = "备注")
private String remark;
/** 库位码(编码) */
@Excel(name = "库位码")
private String pcode;
/** 库位主键IDpcde_detail.id */
@Excel(name = "库位主键ID")
// @Excel(name = "库位主键ID")
private String pcodeId;
/** 托盘码 */
@@ -206,7 +214,7 @@ public class RkInfo extends BaseEntity {
private String trayCode;
/** 实物ID */
@Excel(name = "实物ID")
// @Excel(name = "实物ID")
private String entityId;
/** 一货一图 - 货物照片URL非表字段 */
@@ -227,21 +235,21 @@ public class RkInfo extends BaseEntity {
private String teamName;
@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 lyTime;
@Excel(name = "出库备注")
private String ckRemark;
@Excel(name = "出库备注")
// @Excel(name = "审核状态")
private String status;
/** 是否移库过0否 1是 */
@Excel(name = "是否移库过")
// @Excel(name = "是否移库过")
private String hasMoved;
/** 是否借料0否 1是 2已归还 */
@Excel(name = "是否借料", readConverterExp = "0=否,1=是,2=已归还")
// @Excel(name = "是否借料", readConverterExp = "0=否,1=是,2=已归还")
private String isBorrowed;
/** 签字图片URLimage_type = sign非表字段 */
@@ -452,6 +460,14 @@ public class RkInfo extends BaseEntity {
public String getCkRemark() { return ckRemark; }
public void setCkRemark(String ckRemark) { this.ckRemark = ckRemark; }
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
@@ -488,6 +504,14 @@ public class RkInfo extends BaseEntity {
public String getFycde2() { return fycde2; }
public void setFycde2(String fycde2) { this.fycde2 = fycde2; }
public BigDecimal getTotalAmount() {
return totalAmount;
}
public void setTotalAmount(BigDecimal totalAmount) {
this.totalAmount = totalAmount;
}
public String getIsDelete() { return isDelete; }
public void setIsDelete(String isDelete) { this.isDelete = isDelete; }

View File

@@ -1,9 +1,17 @@
package com.zg.project.wisdom.domain.dto;
import com.zg.framework.aspectj.lang.annotation.Excel;
import lombok.Data;
@Data
public class OutGoodsDTO {
private String taskNo;
private Integer materialStatus; // 0=空托1=有货(影响是否传物料信息)
/**
* 调度模式
* 1 = 仅立库WCS
* 2 = 立库 + AGV
*/
@Excel(name = "调度模式", readConverterExp = "1=仅立库,2=立库+AGV")
private Integer dispatchMode;
}

View File

@@ -14,6 +14,9 @@ public class RefundRequestDTO {
/** 新库位码 */
private String pcode;
/** 新仓库 */
private String warehouseCode;
/** 入库类型 */
private String rkType;
}

View File

@@ -20,10 +20,10 @@ public class RkInfoQueryDTO extends RkInfo {
private Integer pageSize;
/** 开始时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date statDate;
/** 结束时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date endDate;
}

View File

@@ -1,7 +1,10 @@
package com.zg.project.wisdom.domain.dto;
import com.zg.framework.aspectj.lang.annotation.Excel;
import lombok.Data;
import java.math.BigDecimal;
/**
* 出库明细 DTO
*/
@@ -20,6 +23,9 @@ public class StockOutItemDTO {
/** 托盘码 */
private String trayCode;
/** 实际数量 */
private BigDecimal realQty;
/** 现场照片(单张) */
private String photoUrl; // ✅ 一物一图绑定

View File

@@ -0,0 +1,79 @@
package com.zg.project.wisdom.domain.vo;
import com.zg.framework.aspectj.lang.annotation.Excel;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 出入库统计明细 VO
* 仅用于查询与展示,不参与落库
*/
@Data
public class RkCkStatisticVO implements Serializable {
private static final long serialVersionUID = 1L;
/** 操作类型:入库 / 出库 */
@Excel(name = "操作类型")
private String operationType;
/** 详细类型:入库类型名 / 出库类型名 */
@Excel(name = "详细类型")
private String detailType;
/** 库存状态(在库 / 已出库 / 借料等) */
@Excel(name = "库存状态")
private String stockStatus;
/** 单据号(入库单号或出库单号) */
@Excel(name = "单据号")
private String billNo;
/** 物料号 */
@Excel(name = "物料号")
private String materialCode;
/** 物料描述 */
@Excel(name = "物料描述")
private String materialName;
/** 单位 */
@Excel(name = "单位")
private String unit;
/** 数量(实际数量) */
@Excel(name = "数量")
private BigDecimal quantity;
/** 项目号 */
@Excel(name = "项目号")
private String projectCode;
/** 项目描述 */
@Excel(name = "项目描述")
private String projectName;
/** 库位码 */
@Excel(name = "库位码")
private String locationCode;
/** 订单编号SAP 订单号) */
@Excel(name = "订单编号")
private String orderNo;
/** 供应商名称 */
@Excel(name = "供应商名称")
private String supplierName;
/** 备注 */
@Excel(name = "备注")
private String remark;
/** 入库时间(用户操作入库的日期) */
@Excel(name = "操作时间", dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date operationTime;
}

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

@@ -120,5 +120,16 @@ public interface GysJhMapper
*/
List<PlanToMtdVO> selectMissingMtdItems();
/**
* 根据物料号查询供应计划(用于回填单位)
*/
List<GysJh> selectByWlNo(@Param("wlNo") String wlNo);
/**
* 根据主键更新单位
*/
int updateDwById(@Param("id") Long id,
@Param("dw") String dw);
}

View File

@@ -1,6 +1,7 @@
package com.zg.project.wisdom.mapper;
import java.util.List;
import java.util.Map;
import com.zg.project.inventory.domain.vo.PcdeCntVO;
import com.zg.project.inventory.domain.vo.RkInfoMatchVO;
@@ -34,12 +35,14 @@ public interface RkInfoMapper
/**
* 使用 selectRkInfoVo 作为子查询,外层按 bill_no 分组聚合
* 不新增 resultMap / VO直接用 RkInfoResult 映射需要的字段
*/
List<RkInfo> selectGroupedByBill(@Param("q") RkInfoQueryDTO query,
@Param("needAudit") Integer needAudit);
List<RkInfo> selectBillHeaderList(@Param("q") RkInfoQueryDTO query,
@Param("needAudit") Integer needAudit);
/**
* 修改库存单据主
*
@@ -48,6 +51,14 @@ public interface RkInfoMapper
*/
public int updateRkInfo(RkInfo rkInfo);
/**
* 根据出库单据号更新库存单据
*
* @param rkInfo 库存单据
* @return 影响行数
*/
int updateByBillNoCk(RkInfo rkInfo);
/**
* 删除库存单据主
*
@@ -107,7 +118,7 @@ public interface RkInfoMapper
List<RkInfo> selectTopOverdueStock(@Param("limit") int limit);
/**
* 出库操作
*
* @param update
*/
void updateById(RkInfo update);
@@ -226,9 +237,10 @@ public interface RkInfoMapper
*/
List<RkInfo> selectAllRkInfo(RkInfo query);
Long selectStatistics(RkInfo query);
Long selectPcde(RkInfo query);
/**
* 出入库明细分页查询(统计页面)
*/
List<RkInfo> selectRkInfoPageList(RkInfo query);
int updateBillInfo(RkInfo query);
@@ -243,7 +255,7 @@ public interface RkInfoMapper
/**
* 查询出库单据
*/
List<RkInfo> selectDeliveryCkList(@Param("q") RkInfo query);
List<RkInfo> selectDeliveryCkList(RkInfo query);
/**
@@ -252,4 +264,9 @@ public interface RkInfoMapper
List<RkInfo> selectBorrowAndReturnList(RkInfo rkInfo);
RkInfo selectHeaderByBillNo(String billNo);
String selectMaxBillNo(@Param("prefix") String prefix);
Map<String, Object> selectStockStatistics(RkInfoQueryDTO query);
}

View File

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

View File

@@ -1,6 +1,8 @@
package com.zg.project.wisdom.service;
import java.util.List;
import com.zg.framework.web.domain.AjaxResult;
import com.zg.project.wisdom.domain.GysJh;
import com.zg.project.wisdom.domain.dto.ExcelFieldMapping;
import org.springframework.web.multipart.MultipartFile;
@@ -51,6 +53,9 @@ public interface IGysJhService
* @param ids 需要删除的供应计划主键集合
* @return 结果
*/
public int deleteGysJhByIds(Long[] ids);
/**
@@ -74,7 +79,7 @@ public interface IGysJhService
* @param sapNo
* @return
*/
List<GysJh> getBySapNo(String sapNo);
AjaxResult getBySapNo(String sapNo);
/**
* 按字段映射导入供应计划

View File

@@ -1,6 +1,7 @@
package com.zg.project.wisdom.service;
import java.util.List;
import java.util.Map;
import com.zg.project.inventory.domain.dto.QueryDTO;
import com.zg.project.inventory.domain.vo.ChartDataVO;
@@ -38,6 +39,10 @@ public interface IRkInfoService
*/
List<RkInfo> selectGroupedByBill(RkInfoQueryDTO query);
// 接口
List<RkInfo> selectBillHeaderList(RkInfoQueryDTO query);
/**
* 修改库存单据主
*
@@ -46,6 +51,14 @@ public interface IRkInfoService
*/
public int updateRkInfo(RkInfo rkInfo);
/**
* 根据出库单据号修改库存单据
*
* @param rkInfo 库存单据
* @return 影响行数
*/
int updateRkInfoByBillNoCk(RkInfo rkInfo);
/**
* 批量删除库存单据主
*
@@ -160,9 +173,10 @@ public interface IRkInfoService
*/
List<RkInfo> selectAllRkInfo(RkInfo query);
Long selectStatistics(RkInfo query);
Long selectPcde(RkInfo query);
/**
* 出入库明细分页查询(统计页面)
*/
List<RkInfo> selectRkInfoPageList(RkInfo query);
public int updateBillInfo(RkInfo rkInfo);

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()) {
@@ -83,6 +87,7 @@ public class QwenOcrRemoteService {
// 远程服务返回:{ success: true, found: true/false, erpOrderNo: "0101398982" }
JsonNode erpNode = rootNode.get("erpOrderNo");
if (erpNode == null || erpNode.isNull()) {
return "";
}
@@ -95,4 +100,5 @@ public class QwenOcrRemoteService {
throw new RuntimeException("调用远程 OCR 服务异常:" + e.getMessage(), e);
}
}
}

View File

@@ -1,5 +1,6 @@
package com.zg.project.wisdom.service;
import com.zg.project.wisdom.domain.dto.RkInfoQueryDTO;
import com.zg.project.wisdom.domain.vo.*;
import javax.servlet.http.HttpServletResponse;
@@ -87,4 +88,11 @@ public interface RkStatisticsService {
* 规则:排除 rk_info 中未出库is_chuku=0 或 NULL的占用库位
*/
List<SceneAvailableVO> listAvailableByWarehouse(String warehouseCode);
/**
* 库存统计
* @param dto 查询参数
* @return { "total": 0, "rows": List<StockStatVO> }
*/
Map<String, Object> selectStockStatistics(RkInfoQueryDTO dto);
}

View File

@@ -135,8 +135,12 @@ public class AgvTaskResultServiceImpl implements IAgvTaskResultService {
* 货物上架
* @param
*/
/**
* 货物上架
*/
@Override
public String handleUpGoods(String taskNo, Integer materialStatus) {
Date now = DateUtils.getNowDate();
// 1. 查询调度任务
@@ -172,13 +176,11 @@ public class AgvTaskResultServiceImpl implements IAgvTaskResultService {
// 3. 发起调用
String wcsResp = OkHttpUtils.postJson(wcsJobCreateUrl, wcsParam.toJSONString());
if (StringUtils.isBlank(wcsResp)) {
throw new ServiceException("WCS 接口无响应");
}
JSONObject json = JSON.parseObject(wcsResp);
if (!json.containsKey("result")) {
throw new ServiceException("WCS 返回格式异常:" + wcsResp);
}
@@ -203,40 +205,52 @@ public class AgvTaskResultServiceImpl implements IAgvTaskResultService {
wcs.setUpdateTime(now);
wcsTaskResultMapper.insertWcsTaskResult(wcs);
log.info("[上架] 执行成功任务 {} 状态已更新为已完成", taskNo);
// ✅ 5. 根据 dispatchMode 判断:仅立库(WCS)模式WCS成功任务完成
if (ddTask.getDispatchMode() != null && ddTask.getDispatchMode() == 1) {
ddTaskMapper.updateTaskStatusByTaskNo(taskNo, 2); // 2=已完成
log.info("[上架] dispatchMode=1仅立库模式任务 {} 已更新为已完成(task_status=2)", taskNo);
} else {
log.info("[上架] dispatchMode!=1非仅立库模式任务 {} 不在此处置完成,后续流程继续", taskNo);
}
// ✅ 返回 taskId
return taskId;
}
/**
* 货物下架
* @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 +258,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 +272,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 +327,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 +343,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

@@ -8,6 +8,7 @@ import com.zg.common.exception.ServiceException;
import com.zg.common.utils.DateUtils;
import com.zg.common.utils.SecurityUtils;
import com.zg.common.utils.StringUtils;
import com.zg.framework.web.domain.AjaxResult;
import com.zg.project.wisdom.domain.dto.ExcelFieldMapping;
import org.apache.poi.ss.usermodel.*;
import org.springframework.beans.factory.annotation.Autowired;
@@ -162,11 +163,40 @@ public class GysJhServiceImpl implements IGysJhService
* @return
*/
@Override
public List<GysJh> getBySapNo(String sapNo) {
return gysJhMapper.getBySapNo(sapNo);
public AjaxResult getBySapNo(String sapNo) {
List<GysJh> list = gysJhMapper.getBySapNo(sapNo);
AjaxResult result = AjaxResult.success(list);
if (list == null || list.isEmpty()) {
return result;
}
// 找出已入库(status=1)的物料号,去重、过滤空值
List<String> inStockWlNos = list.stream()
.filter(x -> x != null && "1".equals(String.valueOf(x.getStatus()).trim()))
.map(GysJh::getWlNo)
.filter(wlNo -> wlNo != null && !wlNo.trim().isEmpty())
.map(String::trim)
.distinct()
.collect(java.util.stream.Collectors.toList());
if (!inStockWlNos.isEmpty()) {
result.put("warn", true);
result.put("inStockWlNos", inStockWlNos);
// 组装提示文案(可根据前端展示需要调整长度)
String msg = "该 SAP 订单号下,以下物料号已入库过,请注意:"
+ String.join("", inStockWlNos);
result.put("msg", msg);
}
return result;
}
@Override
@Transactional(rollbackFor = Exception.class)
public int importByMapping(MultipartFile file, List<ExcelFieldMapping> mapping) throws Exception {
@@ -271,11 +301,13 @@ public class GysJhServiceImpl implements IGysJhService
item.setHtDj(parseBigDecimal(cellValue));
break;
case "jhQty":
// Long这里用 Long避免 BigDecimal 转 Long 的报错
item.setJhQty(parseLong(cellValue));
item.setJhQty(parseBigDecimal(cellValue));
break;
case "htQty":
item.setHtQty(parseLong(cellValue));
item.setHtQty(parseBigDecimal(cellValue));
break;
case "entityId":
item.setEntityId(cellValue.trim());
break;
case "sapNo":
item.setSapNo(cellValue.trim());
@@ -289,8 +321,8 @@ public class GysJhServiceImpl implements IGysJhService
// 默认字段
item.setStatus("0");
item.setIsDelete("0");
// String username = SecurityUtils.getUsername();
item.setCreateBy("大爷的!!!");
String username = SecurityUtils.getUsername();
// item.setCreateBy("大爷的!!!");
item.setCreateTime(DateUtils.getNowDate());
result.add(item);

View File

@@ -107,6 +107,11 @@ public class MoveRecordServiceImpl implements IMoveRecordService
return moveRecordMapper.deleteMoveRecordById(id);
}
/**
* 处理库存移库操作
*
* @param dto 移库请求参数包含原库存ID、移库目标列表等
*/
/**
* 处理库存移库操作
*
@@ -124,11 +129,6 @@ public class MoveRecordServiceImpl implements IMoveRecordService
throw new ServiceException("目标位置列表不能为空");
}
// 0.1 校验每个目标库位与仓库关系
for (MoveTargetItem target : dto.getTargets()) {
validatePcodeWarehouseRelation(target.getToPcode(), target.getToCangku());
}
// 1. 查询原始库存记录
RkInfo original = rkInfoMapper.selectRkInfoById(dto.getFromRkId());
if (original == null || "1".equals(original.getIsDelete())) {
@@ -154,47 +154,56 @@ public class MoveRecordServiceImpl implements IMoveRecordService
String username = dto.getMovedBy();
Date now = DateUtils.parseDate(dto.getMovedAt());
// ===== 情况一:整库移库(目标总数量 = 原库存,且仅一个目标=====
// ===== 情况一:整库移库(目标 + 数量相等=====
if (dto.getTargets().size() == 1 && totalQty.compareTo(realQty) == 0) {
MoveTargetItem target = dto.getTargets().get(0);
// 这里 info 用于记录移库前快照
RkInfo info = new RkInfo();
BeanUtils.copyProperties(original, info);
// 移库前快照
RkInfo snapshot = new RkInfo();
BeanUtils.copyProperties(original, snapshot);
// 更新原库存到新位置
original.setWarehouseCode(target.getToCangku());
// 更新原库存位置
original.setCangku(target.getToCangku());
original.setPcode(target.getToPcode());
original.setTrayCode(target.getToTrayCode());
original.setHasMoved("1");
original.setUpdateBy(username);
original.setUpdateTime(now);
rkInfoMapper.updateRkInfo(original);
// 移库记录
moveRecordMapper.insertMoveRecord(
createMoveRecord(snapshot, target, dto)
);
return;
}
// ===== 情况二 / 三:拆分移库 =====
List<RkInfo> insertList = new ArrayList<>();
// 来源快照(用于日志 & 新库存模板)
RkInfo fromSnapshot = new RkInfo();
BeanUtils.copyProperties(original, fromSnapshot);
// 情况三:部分移库(原库存减少)
if (totalQty.compareTo(realQty) < 0) {
original.setRealQty(realQty.subtract(totalQty));
original.setHasMoved("1");
original.setUpdateBy(username);
original.setUpdateTime(now);
rkInfoMapper.updateRkInfo(original);
// 记录移库日志(从 info → target
moveRecordMapper.insertMoveRecord(createMoveRecord(info, target, dto));
return;
}
// ===== 情况二 & 三:需要新建多条库存记录 =====
List<RkInfo> insertList = new ArrayList<>();
// 情况三:部分移库(目标总量 < 原库存) → 原库存数量减少
if (totalQty.compareTo(realQty) < 0) {
original.setRealQty(realQty.subtract(totalQty));
original.setUpdateBy(username);
original.setUpdateTime(now);
rkInfoMapper.updateRkInfo(original);
} else {
// 情况二:原库存刚好用完(但目标多个) → 删除原库存
// 情况二:原库存刚好用完(但目标多个)
rkInfoMapper.deleteRkInfoById(original.getId());
}
// 新增多条目标库存
// 新增目标库存
for (MoveTargetItem target : dto.getTargets()) {
RkInfo newInfo = new RkInfo();
// 注意:这里以 original 为模板(注意上面是否已修改过数量)
BeanUtils.copyProperties(original, newInfo, "id");
// 以移库前快照为模板
BeanUtils.copyProperties(fromSnapshot, newInfo, "id");
newInfo.setCangku(target.getToCangku());
newInfo.setPcode(target.getToPcode());
@@ -209,8 +218,10 @@ public class MoveRecordServiceImpl implements IMoveRecordService
insertList.add(newInfo);
// 移库记录:从原库存 original → 每个 target
moveRecordMapper.insertMoveRecord(createMoveRecord(original, target, dto));
// 移库记录
moveRecordMapper.insertMoveRecord(
createMoveRecord(fromSnapshot, target, dto)
);
}
if (!insertList.isEmpty()) {

View File

@@ -2,6 +2,8 @@ package com.zg.project.wisdom.service.impl;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
@@ -12,6 +14,8 @@ import com.zg.common.exception.ServiceException;
import com.zg.common.utils.DateUtils;
import com.zg.common.utils.SecurityUtils;
import com.zg.common.utils.StringUtils;
import com.zg.project.information.domain.Mtd;
import com.zg.project.information.mapper.MtdMapper;
import com.zg.project.inventory.AutoInventory.mapper.InventoryMatchScanMapper;
import com.zg.project.inventory.Task.mapper.InventoryTaskMapper;
import com.zg.project.inventory.domain.dto.QueryDTO;
@@ -30,12 +34,15 @@ import com.zg.project.wisdom.utils.BillNoUtil;
import com.zg.project.wisdom.utils.CodeConvertUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import com.zg.project.wisdom.mapper.RkInfoMapper;
import com.zg.project.wisdom.domain.RkInfo;
import com.zg.project.wisdom.service.IRkInfoService;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import static com.zg.common.utils.SecurityUtils.getUsername;
/**
@@ -69,6 +76,8 @@ public class RkInfoServiceImpl implements IRkInfoService
@Autowired
private PcdeDetailMapper pcdeDetailMapper;
@Autowired
private MtdMapper mtdMapper;
/**
* 查询库存单据主
@@ -89,43 +98,19 @@ public class RkInfoServiceImpl implements IRkInfoService
* @return 库存单据主
*/
@Override
public List<RkInfo> selectRkInfoList(RkInfo rkInfo) {
public List<RkInfo> selectRkInfoList(RkInfo rkInfo)
{
boolean needAudit = "1".equals(configService.selectConfigByKey("rk.audit.enabled"));
List<RkInfo> list = rkInfoMapper.selectRkInfoList(rkInfo);
LocalDate today = LocalDate.now();
for (RkInfo info : list) {
// 计算库龄
if (info.getRkTime() != null) {
LocalDate rkDate = info.getRkTime().toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
long days = ChronoUnit.DAYS.between(rkDate, today);
info.setStockAge(days);
}
// 查询现场图片 + 审核结果
AuditSignature signature = auditSignatureMapper.selectPhotoUrlByRkId(info.getId());
if (signature != null) {
info.setScenePhotoUrl(signature.getSignUrl());
info.setAuditResult(signature.getAuditResult());
info.setApproverId(signature.getApproverId());
} else {
info.setScenePhotoUrl(null);
info.setAuditResult(null);
info.setApproverId(null);
}
}
// 审核开启,过滤掉审核结果不是“通过” 且 审核人不为空(说明是审核失败)
if (needAudit) {
// 审核开启时,过滤掉“审核失败”的数据
if (needAudit)
{
list = list.stream()
.filter(info ->
// ① 未审核过approverId 为空)或
info.getApproverId() == null ||
// ② 审核通过
"1".equals(info.getAuditResult())
info.getApproverId() == null
|| "1".equals(info.getAuditResult())
)
.collect(Collectors.toList());
}
@@ -183,6 +168,13 @@ public class RkInfoServiceImpl implements IRkInfoService
return rkInfoMapper.selectGroupedByBill(query, needAudit ? 1 : 0);
}
@Override
public List<RkInfo> selectBillHeaderList(RkInfoQueryDTO query) {
// 同样传入审核开关逻辑(保留原逻辑一致性)
boolean needAudit = "1".equals(configService.selectConfigByKey("rk.audit.enabled"));
return rkInfoMapper.selectBillHeaderList(query, needAudit ? 1 : 0);
}
@Override
public RkInfo selectHeaderByBillNo(String billNo) {
if (StringUtils.isBlank(billNo)) {
@@ -213,26 +205,62 @@ public class RkInfoServiceImpl implements IRkInfoService
String pcode = rkInfo.getPcode();
// 只有当小仓 + 库位都有的时候才做这层校验
if (StringUtils.isNotBlank(warehouseCode) && StringUtils.isNotBlank(pcode)) {
// 临时组装一个 PcRkInfoBatchDTO专门给 validateWarehouseAndPcode 用
PcRkInfoBatchDTO tmpDto = new PcRkInfoBatchDTO();
tmpDto.setWarehouseCode(warehouseCode);
List<PcRkInfoItemDTO> rkList = new ArrayList<>();
PcRkInfoItemDTO item = new PcRkInfoItemDTO();
item.setPcode(pcode);
rkList.add(item);
tmpDto.setRkList(rkList);
validateWarehouseAndPcode(tmpDto);
}
// if (StringUtils.isNotBlank(warehouseCode) && StringUtils.isNotBlank(pcode)) {
//
// // 临时组装一个 PcRkInfoBatchDTO专门给 validateWarehouseAndPcode 用
// PcRkInfoBatchDTO tmpDto = new PcRkInfoBatchDTO();
// tmpDto.setWarehouseCode(warehouseCode);
//
// List<PcRkInfoItemDTO> rkList = new ArrayList<>();
// PcRkInfoItemDTO item = new PcRkInfoItemDTO();
// item.setPcode(pcode);
// rkList.add(item);
//
// tmpDto.setRkList(rkList);
//
// validateWarehouseAndPcode(tmpDto);
// }
// ====== 校验通过再更新 ======
return rkInfoMapper.updateRkInfo(rkInfo);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int updateRkInfoByBillNoCk(RkInfo rkInfo) {
// ====== 基础校验 ======
if (StringUtils.isBlank(rkInfo.getBillNoCk())) {
throw new ServiceException("出库单据号 billNoCk 不能为空");
}
// 通用审计字段
rkInfo.setUpdateTime(DateUtils.getNowDate());
rkInfo.setUpdateBy(SecurityUtils.getUserId().toString());
// ====== 仓库 + 库位校验(保持与你 updateRkInfo 完全一致) ======
String warehouseCode = rkInfo.getWarehouseCode();
if (StringUtils.isBlank(warehouseCode)) {
warehouseCode = rkInfo.getCangku();
}
String pcode = rkInfo.getPcode();
if (StringUtils.isNotBlank(warehouseCode) && StringUtils.isNotBlank(pcode)) {
PcRkInfoBatchDTO tmpDto = new PcRkInfoBatchDTO();
tmpDto.setWarehouseCode(warehouseCode);
PcRkInfoItemDTO item = new PcRkInfoItemDTO();
item.setPcode(pcode);
tmpDto.setRkList(Collections.singletonList(item));
validateWarehouseAndPcode(tmpDto);
}
// ====== 按 bill_no_ck 批量更新 ======
return rkInfoMapper.updateByBillNoCk(rkInfo);
}
/**
* 批量处理撤销入库 / 撤销出库的逻辑(不是通用删除接口)
*
@@ -279,13 +307,15 @@ public class RkInfoServiceImpl implements IRkInfoService
if (jh != null) {
String status = jh.getStatus();
if ("2".equals(status)) {
// 回补 jh_qty改为未到货
BigDecimal updatedQty = (jh.getJhQty() == null ? BigDecimal.ZERO : new BigDecimal(jh.getJhQty()))
.add(realQty);
jh.setJhQty(updatedQty.longValue());
BigDecimal updatedQty =
(jh.getJhQty() == null ? BigDecimal.ZERO : jh.getJhQty())
.add(realQty);
jh.setJhQty(updatedQty);
jh.setStatus("0");
jh.setUpdateBy(userId);
jh.setUpdateTime(now);
gysJhMapper.updateGysJh(jh);
} else if ("1".equals(status)) {
// 只更新状态
@@ -355,7 +385,7 @@ public class RkInfoServiceImpl implements IRkInfoService
@Transactional(rollbackFor = Exception.class)
public void batchInsert(PcRkInfoBatchDTO dto) {
String billNo = BillNoUtil.generateTodayBillNo("RK");
String billNo = BillNoUtil.generateTodayBillNo("RK", rkInfoMapper);
List<PcRkInfoItemDTO> list = dto.getRkList();
List<RkInfo> rkInfos = new ArrayList<>();
List<AuditSignature> records = new ArrayList<>();
@@ -364,26 +394,31 @@ public class RkInfoServiceImpl implements IRkInfoService
String userId = SecurityUtils.getUserId().toString();
Date now = DateUtils.getNowDate();
// 🚩 0. 校验库位是否属于当前小仓
validateWarehouseAndPcode(dto);
// 0. 只有前端传了库位编码,才做“库位属于当前小仓”的校验(否则不传库位会被拦截)
/*暂时去掉校验*/
// boolean hasPcode = list != null && list.stream()
// .anyMatch(x -> StringUtils.isNotBlank(x.getPcode()));
// if (hasPcode) {
// validateWarehouseAndPcode(dto);
// }
// ✅ 1. 供应计划扣减映射
// ✅ 1. 供应计划扣减映射(同一个 gysJhId 多条明细时realQty 累加)
Map<Long, BigDecimal> realQtyMap = list.stream()
.filter(item -> item.getGysJhId() != null && item.getRealQty() != null)
.collect(Collectors.toMap(
PcRkInfoItemDTO::getGysJhId,
PcRkInfoItemDTO::getRealQty,
(a, b) -> b
(a, b) -> a.add(b)
));
// ✅ 2-4. 供应计划扣减&状态更新(保你原来的逻辑)
// ✅ 2-4. 供应计划扣减&状态更新(保你原逻辑)
if (!realQtyMap.isEmpty()) {
List<GysJh> jhList = gysJhMapper.selectByIds(new ArrayList<>(realQtyMap.keySet()));
Set<Long> idsToUpdateStatus = new HashSet<>();
for (GysJh jh : jhList) {
Long jhId = jh.getId();
BigDecimal planQty = BigDecimal.valueOf(jh.getJhQty());
BigDecimal planQty = jh.getJhQty() == null ? BigDecimal.ZERO : jh.getJhQty();
BigDecimal realQty = realQtyMap.get(jhId);
if (realQty == null) {
continue;
@@ -392,13 +427,11 @@ public class RkInfoServiceImpl implements IRkInfoService
boolean isEqual = realQty.compareTo(planQty) == 0;
if (!isEqual) {
// 部分入库:扣减计划数量
gysJhMapper.decreaseJhQtyById(jhId, realQty);
if (!needAudit) {
gysJhMapper.updateStatusById(jhId, "2"); // 2 = 部分入库
}
} else {
// 全部入库:状态=1
if (!needAudit) {
gysJhMapper.updateStatusById(jhId, "1"); // 1 = 全部入库
}
@@ -410,27 +443,18 @@ public class RkInfoServiceImpl implements IRkInfoService
}
}
// ✅ 5. 构建 RkInfo 入库记录(注意:小仓 = dto.getWarehouseCode()
// ✅ 5. 构建 RkInfo 入库记录(库位字段按“前端是否传 pcode”决定是否设置
for (PcRkInfoItemDTO item : list) {
RkInfo rk = new RkInfo();
// 物料、项目、供应商、xj县局、数量等等从 item 拷贝
BeanUtils.copyProperties(item, rk);
rk.setBillNo(billNo);
rk.setRkType(dto.getRkType());
rk.setWlType(dto.getWlType());
rk.setLihuoY(dto.getLihuoY());
// 🚩 所属小仓:页面选的小仓编码
rk.setCangku(dto.getWarehouseCode());
// 🚩 入库时间:页面传,若为空则使用当前时间
rk.setRkTime(dto.getRkTime() != null ? dto.getRkTime() : DateUtils.getNowDate());
// 库位 encodedId
String encodedId = pcdeDetailMapper.selectEncodedIdByPcode(item.getPcode());
rk.setPcodeId(encodedId);
rk.setCreateBy(userId);
rk.setCreateTime(now);
rk.setIsDelete("0");
@@ -439,6 +463,45 @@ public class RkInfoServiceImpl implements IRkInfoService
rk.setFycde1(item.getFycde1());
rk.setFycde2(item.getFycde2());
if (StringUtils.isBlank(rk.getDw()) && StringUtils.isNotBlank(item.getWlNo())) {
Mtd mtd = mtdMapper.selectMtdByMid(item.getWlNo().trim());
if (mtd != null && StringUtils.isNotBlank(mtd.getUnt())) {
rk.setDw(mtd.getUnt());
}
}
// 库位编码前端不传,就不设置库位相关字段(并且不查 encodedId
if (StringUtils.isNotBlank(item.getPcode())) {
// pcode 有值:允许设置/绑定库位
rk.setPcode(item.getPcode());
// 只有有 pcode 才查 pcodeId
String encodedId = pcdeDetailMapper.selectEncodedIdByPcodeAndWarehouse(
item.getPcode(),
dto.getWarehouseCode()
);
rk.setPcodeId(encodedId);
// 这些字段如果前端没传,保持 null如果传了就写入
if (StringUtils.isNotBlank(item.getTrayCode())) {
rk.setTrayCode(item.getTrayCode());
} else {
rk.setTrayCode(null);
}
if (StringUtils.isNotBlank(item.getEntityId())) {
rk.setEntityId(item.getEntityId());
} else {
rk.setEntityId(null);
}
} else {
// pcode 不传:明确不设置库位信息
rk.setPcode(null);
rk.setPcodeId(null);
rk.setTrayCode(null);
rk.setEntityId(null);
}
if (needAudit) {
rk.setStatus("0"); // 待审核
rk.setIsChuku("2"); // 待入库(审核中)
@@ -463,15 +526,18 @@ public class RkInfoServiceImpl implements IRkInfoService
AuditSignature photo = new AuditSignature();
photo.setRkId(rk.getId());
photo.setBillNo(billNo);
photo.setBillType("0"); // 入库
photo.setAuditResult("2"); // 待审核
photo.setBillType("0");
photo.setAuditResult("2");
photo.setSignerId(userId);
photo.setSignerRole("2"); // 现场照片
photo.setImageType("1"); // photo
photo.setSignerRole("2");
photo.setImageType("1");
photo.setSignUrl(item.getPhotoUrl());
photo.setSignTime(now);
photo.setPcode(item.getPcode());
photo.setTrayCode(item.getTrayCode());
// 这里照旧:有库位就写库位/托盘;没库位就是 null
photo.setPcode(rk.getPcode());
photo.setTrayCode(rk.getTrayCode());
photo.setApproverId(dto.getApproverId());
photo.setIsCurrent("1");
photo.setIsDelete("0");
@@ -491,15 +557,15 @@ public class RkInfoServiceImpl implements IRkInfoService
AuditSignature mainSign = new AuditSignature();
mainSign.setBillNo(billNo);
mainSign.setBillType("0"); // 入库
mainSign.setBillType("0");
mainSign.setSignerId(userId);
mainSign.setSignerRole("0"); // 发起人
mainSign.setSignerRole("0");
mainSign.setApproverId(dto.getApproverId());
mainSign.setSignUrl(dto.getSignatureUrl());
mainSign.setImageType("0"); // sign
mainSign.setImageType("0");
mainSign.setSignTime(now);
mainSign.setIsCurrent("1");
mainSign.setAuditResult("2"); // 待审核
mainSign.setAuditResult("2");
mainSign.setIsDelete("0");
mainSign.setCreateBy(userId);
mainSign.setCreateTime(now);
@@ -614,8 +680,8 @@ public class RkInfoServiceImpl implements IRkInfoService
@Transactional(rollbackFor = Exception.class)
public int batchInsertApp(RkInfoBatchDTO dto) {
// ✅ 0. 小仓 + 库位一致性校验(统一用 warehouseCode
validateWarehouseAndPcode(dto);
// // ✅ 0. 小仓 + 库位一致性校验(统一用 warehouseCode
// validateWarehouseAndPcode(dto);
List<RkInfo> saveList = new ArrayList<>();
String userId = SecurityUtils.getUserId().toString();
@@ -626,7 +692,7 @@ public class RkInfoServiceImpl implements IRkInfoService
}
boolean needAudit = "1".equals(configService.selectConfigByKey("rk.audit.enabled"));
String billNo = BillNoUtil.generateTodayBillNo("RK");
String billNo = BillNoUtil.generateTodayBillNo("RK", rkInfoMapper);
// ✅ 第1步构造 gysJhId → realQty 的映射(从 scanList 中提取)
Map<Long, BigDecimal> realQtyMap = new HashMap<>();
@@ -650,7 +716,9 @@ public class RkInfoServiceImpl implements IRkInfoService
List<GysJh> jhList = gysJhMapper.selectByIds(allJhIds);
for (GysJh jh : jhList) {
Long jhId = jh.getId();
BigDecimal planQty = BigDecimal.valueOf(jh.getJhQty());
BigDecimal planQty = jh.getJhQty() == null
? BigDecimal.ZERO
: jh.getJhQty();
BigDecimal realQty = realQtyMap.get(jhId);
if (realQty == null) {
continue;
@@ -867,42 +935,133 @@ public class RkInfoServiceImpl implements IRkInfoService
boolean needAudit = "1".equals(configService.selectConfigByKey("rk.audit.enabled"));
// Step 2: 生成出库单据号
String billNo = BillNoUtil.generateTodayBillNo("CK");
String billNo = BillNoUtil.generateTodayBillNo("CK", rkInfoMapper);
// Step 3: 批量更新 rk_info 出库字段
// 记录原记录id -> 出库记录id部分出库时照片要绑定到新插入的出库记录
Map<Long, Long> outRkIdMap = new HashMap<>();
// Step 3: 出库处理(支持部分出库)
for (StockOutItemDTO item : dto.getCkList()) {
RkInfo update = new RkInfo();
update.setId(item.getId());
update.setBillNoCk(billNo);
update.setIsDelivery(dto.getIsDelivery());
update.setCkType(dto.getCkType());
update.setTeamCode(dto.getTeamCode());
update.setLyTime(now);
update.setCkLihuoY(dto.getCkLihuoY());
update.setCkRemark(item.getCkRemark());
update.setXmNoCk(dto.getXmNoCk());
update.setXmMsCk(dto.getXmMsCk());
update.setUpdateBy(userId);
update.setUpdateTime(now);
if ("JLCK".equals(dto.getCkType())) {
update.setIsBorrowed("1");
update.setBorrowTime(dto.getBorrowTime());
update.setReturnTime(dto.getReturnTime());
if (item.getId() == null) {
throw new ServiceException("出库记录ID不能为空");
}
if (item.getRealQty() == null || item.getRealQty().compareTo(BigDecimal.ZERO) <= 0) {
throw new ServiceException("出库数量必须大于0id=" + item.getId());
}
if (needAudit) {
update.setStatus("3"); // 出库待审核
update.setIsChuku("JLCK".equals(dto.getCkType()) ? "3" : "2");
// 3.1 查询原库存记录
RkInfo db = rkInfoMapper.selectRkInfoById(item.getId());
if (db == null) {
throw new ServiceException("库存记录不存在id=" + item.getId());
}
BigDecimal oldQty = db.getRealQty() == null ? BigDecimal.ZERO : db.getRealQty();
BigDecimal outQty = item.getRealQty();
// 3.2 校验不能超出库存
BigDecimal newQty = oldQty.subtract(outQty);
if (newQty.compareTo(BigDecimal.ZERO) < 0) {
throw new ServiceException("出库数量不能大于库存数量id=" + item.getId()
+ ",库存=" + oldQty + ",出库=" + outQty);
}
boolean partialOut = newQty.compareTo(BigDecimal.ZERO) > 0;
if (partialOut) {
// ✅ 部分出库:原记录只扣减 real_qty
RkInfo reduce = new RkInfo();
reduce.setId(db.getId());
reduce.setRealQty(newQty);
reduce.setUpdateBy(userId);
reduce.setUpdateTime(now);
rkInfoMapper.updateById(reduce);
// ✅ 插入一条新的“出库记录”:复制原记录 + 覆盖本次出库字段
RkInfo outRow = new RkInfo();
BeanUtils.copyProperties(db, outRow);
outRow.setId(null);
// —— 出库这次操作字段
outRow.setBillNoCk(billNo);
outRow.setIsDelivery(dto.getIsDelivery());
outRow.setCkType(dto.getCkType());
outRow.setTeamCode(dto.getTeamCode());
outRow.setLyTime(now);
outRow.setCkLihuoY(dto.getCkLihuoY());
outRow.setCkRemark(item.getCkRemark());
outRow.setXmNoCk(dto.getXmNoCk());
outRow.setXmMsCk(dto.getXmMsCk());
// 新出库记录数量=本次出库数量
outRow.setRealQty(outQty);
// 借料字段(仅 JLCK 才写)
if ("JLCK".equals(dto.getCkType())) {
outRow.setIsBorrowed("1");
outRow.setBorrowTime(dto.getBorrowTime());
outRow.setReturnTime(dto.getReturnTime());
}
// 审核状态 + is_chuku部分出库新出库记录按正常出库逻辑走
if (needAudit) {
outRow.setStatus("3"); // 出库待审核
outRow.setIsChuku("JLCK".equals(dto.getCkType()) ? "3" : "2");
} else {
outRow.setStatus("1"); // 审核通过
outRow.setIsChuku("JLCK".equals(dto.getCkType()) ? "3" : "1");
}
// 通用字段
outRow.setIsDelete("0");
outRow.setCreateBy(userId);
outRow.setCreateTime(now);
outRow.setUpdateBy(userId);
outRow.setUpdateTime(now);
rkInfoMapper.insertRkInfo(outRow);
// 记录映射:照片绑定新出库记录
outRkIdMap.put(db.getId(), outRow.getId());
} else {
update.setStatus("1"); // 审核通过
update.setIsChuku("JLCK".equals(dto.getCkType()) ? "3" : "1");
}
// ✅ 全部出库:不扣减 real_qty直接改状态
RkInfo update = new RkInfo();
update.setId(item.getId());
update.setBillNoCk(billNo);
update.setIsDelivery(dto.getIsDelivery());
update.setCkType(dto.getCkType());
update.setTeamCode(dto.getTeamCode());
update.setLyTime(now);
update.setCkLihuoY(dto.getCkLihuoY());
update.setCkRemark(item.getCkRemark());
update.setXmNoCk(dto.getXmNoCk());
update.setXmMsCk(dto.getXmMsCk());
update.setUpdateBy(userId);
update.setUpdateTime(now);
rkInfoMapper.updateById(update);
if ("JLCK".equals(dto.getCkType())) {
update.setIsBorrowed("1");
update.setBorrowTime(dto.getBorrowTime());
update.setReturnTime(dto.getReturnTime());
}
if (needAudit) {
update.setStatus("3"); // 出库待审核
update.setIsChuku("JLCK".equals(dto.getCkType()) ? "3" : "2");
} else {
update.setStatus("1"); // 审核通过
update.setIsChuku("JLCK".equals(dto.getCkType()) ? "3" : "1");
}
rkInfoMapper.updateById(update);
// 全出库:照片绑定原记录
outRkIdMap.put(item.getId(), item.getId());
}
}
// Step 4: 如果启用审核,写入签字记录
// Step 4: 如果启用审核,写入签字记录部分出库照片绑定新出库记录ID
if (needAudit) {
List<AuditSignature> recordList = new ArrayList<>();
@@ -930,6 +1089,8 @@ public class RkInfoServiceImpl implements IRkInfoService
for (StockOutItemDTO item : dto.getCkList()) {
if (StringUtils.isBlank(item.getPhotoUrl())) continue;
Long rkIdForPhoto = outRkIdMap.getOrDefault(item.getId(), item.getId());
AuditSignature photo = new AuditSignature();
photo.setBillNo(billNo);
photo.setBillType("1");
@@ -944,9 +1105,12 @@ public class RkInfoServiceImpl implements IRkInfoService
photo.setIsDelete("0");
photo.setCreateBy(userId);
photo.setCreateTime(now);
photo.setRkId(item.getId());
// ✅ 关键:部分出库绑定“新出库记录”的 rk_id
photo.setRkId(rkIdForPhoto);
photo.setPcode(item.getPcode());
photo.setTrayCode(item.getTrayCode());
recordList.add(photo);
}
@@ -958,6 +1122,7 @@ public class RkInfoServiceImpl implements IRkInfoService
return dto.getCkList().size();
}
@Override
@Transactional(rollbackFor = Exception.class) // 开启事务,异常时回滚
public void matchWithStatus(QueryDTO dto) {
@@ -1106,8 +1271,6 @@ public class RkInfoServiceImpl implements IRkInfoService
@Override
public int refundMaterial(RefundRequestDTO dto) {
Long originalId = dto.getOriginalId();
String newPcode = dto.getPcode();
// 1. 查原出库记录
RkInfo original = rkInfoMapper.selectRkInfoById(originalId);
if (original == null || "1".equals(original.getIsDelete())) {
@@ -1119,10 +1282,12 @@ public class RkInfoServiceImpl implements IRkInfoService
BeanUtils.copyProperties(original, newEntry);
newEntry.setId(null);
newEntry.setIsChuku("0");
newEntry.setPcode(newPcode);
newEntry.setPcode(dto.getPcode());
newEntry.setCangku(dto.getWarehouseCode());
newEntry.setRkType(dto.getRkType());
newEntry.setRkTime(DateUtils.getNowDate());
newEntry.setBillNo(BillNoUtil.generateTodayBillNo("RK"));
newEntry.setBorrowTime(original.getBorrowTime());
newEntry.setBillNo(BillNoUtil.generateTodayBillNo("RK", rkInfoMapper));
newEntry.setCreateBy(SecurityUtils.getUserId().toString());
newEntry.setCreateTime(DateUtils.getNowDate());
newEntry.setUpdateBy(null);
@@ -1139,6 +1304,8 @@ public class RkInfoServiceImpl implements IRkInfoService
update.setReturnTime(DateUtils.getNowDate());
update.setUpdateBy(getUsername());
update.setUpdateTime(DateUtils.getNowDate());
update.setPcode(dto.getPcode());
update.setCangku(dto.getWarehouseCode());
rkInfoMapper.updateById(update);
return rows;
@@ -1200,21 +1367,16 @@ public class RkInfoServiceImpl implements IRkInfoService
return rkInfoMapper.listRkInfoByPcode(pcode);
}
@Override
public List<RkInfo> selectAllRkInfo(RkInfo query) {
return rkInfoMapper.selectAllRkInfo(query);
}
@Override
public Long selectStatistics(RkInfo query) {
return rkInfoMapper.selectStatistics(query);
public List<RkInfo> selectRkInfoPageList(RkInfo query) {
return rkInfoMapper.selectRkInfoPageList(query);
}
@Override
public Long selectPcde(RkInfo query) {
return rkInfoMapper.selectPcde(query);
}
@Override
public int updateBillInfo(RkInfo query) {
return rkInfoMapper.updateBillInfo(query);
@@ -1252,7 +1414,7 @@ public class RkInfoServiceImpl implements IRkInfoService
e.setBillNo(dto.getBillNo());
e.setRkType(dto.getRkType());
e.setWlType(dto.getWlType());
e.setWarehouseCode(dto.getWarehouseCode());
e.setCangku(dto.getWarehouseCode());
e.setLihuoY(dto.getLihuoY());
e.setRkTime(rkTime);
@@ -1288,7 +1450,7 @@ public class RkInfoServiceImpl implements IRkInfoService
// 计划剩余(表里 jh_qty 列)
BigDecimal remain = plan.getJhQty() == null
? BigDecimal.ZERO
: BigDecimal.valueOf(plan.getJhQty());
: plan.getJhQty();
// ① 全额入库realQty >= remain -> 直接置 1不做扣减
if (remain.compareTo(BigDecimal.ZERO) <= 0 || realQty.compareTo(remain) >= 0) {
@@ -1318,11 +1480,14 @@ public class RkInfoServiceImpl implements IRkInfoService
List<Long> toPartial = new ArrayList<>();
for (GysJh r : rows) {
long remain = r.getJhQty() == null ? 0L : r.getJhQty();
if (remain <= 0L) {
toAll.add(r.getId()); // 已经无剩余 -> 置 1
BigDecimal remain = r.getJhQty() == null
? BigDecimal.ZERO
: r.getJhQty();
if (remain.compareTo(BigDecimal.ZERO) <= 0) {
toAll.add(r.getId()); // 已经无剩余 -> 置 1全部入库
} else {
toPartial.add(r.getId()); // 仍有剩余 -> 置 2
toPartial.add(r.getId()); // 仍有剩余 -> 置 2(部分入库)
}
}

View File

@@ -5,10 +5,13 @@ import com.zg.common.exception.ServiceException;
import com.zg.common.utils.DateUtils;
import com.zg.common.utils.StringUtils;
import com.zg.common.utils.poi.ExcelUtil;
import com.zg.project.wisdom.domain.dto.RkInfoQueryDTO;
import com.zg.project.wisdom.domain.vo.*;
import com.zg.project.wisdom.mapper.GysJhMapper;
import com.zg.project.wisdom.mapper.RkInfoMapper;
import com.zg.project.wisdom.mapper.RkStatisticsMapper;
import com.zg.project.wisdom.service.RkStatisticsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@@ -23,9 +26,12 @@ import java.util.*;
@Service
public class RkStatisticsServiceImpl implements RkStatisticsService {
@Resource
@Autowired
private RkStatisticsMapper rkStatisticsMapper;
@Autowired
private RkInfoMapper rkInfoMapper;
@Override
public List<Map<String, Object>> getAgeStatsAsList() {
@@ -426,4 +432,24 @@ public class RkStatisticsServiceImpl implements RkStatisticsService {
}
return list;
}
@Override
public Map<String, Object> selectStockStatistics(RkInfoQueryDTO query) {
if (query.getIsChuku() == null) {
query.setIsChuku("0"); // 默认只查在库
}
Map<String, Object> result = rkInfoMapper.selectStockStatistics(query);
// 处理空值情况,防止前端拿到 null 报错
if (result == null) {
result = new HashMap<>();
}
result.putIfAbsent("sumMoney", 0);
result.putIfAbsent("sumQty", 0);
result.putIfAbsent("pcdeCount", 0);
return result;
}
}

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

@@ -1,34 +1,66 @@
package com.zg.project.wisdom.utils;
import com.zg.project.wisdom.mapper.RkInfoMapper;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.ThreadLocalRandom;
/**
* 单据号生成工具类
*
* 目标:
* 1) 每次业务操作都生成全新单据号不依赖历史不查DB
* 2) 单号短、可读
* 3) 避免串单:不同出库操作绝不复用同一个 billNo
*
* 推荐格式:
* CK24011315384227
* 前缀 + yyMMdd + HHmmss + 2位随机数
*/
public class BillNoUtil {
// 内存缓存key = prefix + datevalue = 当前递增号
private static final Map<String, AtomicInteger> DAILY_SEQUENCE_MAP = new ConcurrentHashMap<>();
private static final String DEFAULT_PREFIX = "RK";
/**
* 生成当天单据号(格式RK20250731_1、CK20250731_2
* @param prefix 单据前缀(如 RK 或 CK
* @return 单据号
* 生成单据号(短号,不查数据库
* 格式PREFIX + yyMMdd + HHmmss + 2位随机数
* 示例CK24011315384227
*
* @param prefix 单据前缀RK/CK/...
* @param rkInfoMapper 兼容旧签名(不再使用,可传 null
*/
public static synchronized String generateTodayBillNo(String prefix) {
if (prefix == null || prefix.trim().isEmpty()) {
prefix = "RK";
}
public static synchronized String generateTodayBillNo(String prefix, RkInfoMapper rkInfoMapper) {
// 获取当天日期字符串 yyyyMMdd
String today = new SimpleDateFormat("yyyyMMdd").format(new Date());
String key = prefix + today;
// 前缀处理
final String p = (prefix == null || prefix.trim().isEmpty())
? DEFAULT_PREFIX
: prefix.trim().toUpperCase();
// 获取并递增序号
AtomicInteger counter = DAILY_SEQUENCE_MAP.computeIfAbsent(key, k -> new AtomicInteger(1));
int sequence = counter.getAndIncrement();
// 时间到秒yyMMddHHmmss12位
String time = new SimpleDateFormat("yyMMddHHmmss").format(new Date());
return prefix + today + "_" + sequence;
// 2位随机数10~99避免同一秒内多次点击撞号
int rnd = ThreadLocalRandom.current().nextInt(10, 100);
return p + time + rnd;
}
/**
* 需要更短的版本(可选)
* 格式PREFIX + yyMMdd + 4位随机数
* 示例CK2401134837
*
* 注意:比带秒的版本更短,但理论碰撞概率略高(一般够用)
*/
public static synchronized String generateShortBillNo(String prefix) {
final String p = (prefix == null || prefix.trim().isEmpty())
? DEFAULT_PREFIX
: prefix.trim().toUpperCase();
String day = new SimpleDateFormat("yyMMdd").format(new Date());
int rnd4 = ThreadLocalRandom.current().nextInt(1000, 10000);
return p + day + rnd4;
}
}

View File

@@ -6,7 +6,7 @@ spring:
druid:
# 主库数据源
master:
# url: jdbc:mysql://101.132.133.142:3306/wisdom?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# url: jdbc:mysql://47.100.212.83: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

View File

@@ -169,4 +169,4 @@ mock:
#配送系统中调用大模型进行图片识别
qwen-ocr:
base-url: http://192.168.1.253:8087/ocr/extractErpByBase64
base-url: http://192.168.1.253:8087/ocr/extractErpByBase64

View File

@@ -31,12 +31,28 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="isDelete != null and isDelete != ''"> and is_delete = #{isDelete}</if>
</where>
</select>
<select id="selectConstructionTeamById" parameterType="Long" resultMap="ConstructionTeamResult">
<include refid="selectConstructionTeamVo"/>
where id = #{id}
</select>
<select id="selectByTeamCode" resultMap="ConstructionTeamResult">
SELECT *
FROM construction_team
WHERE team_code = #{teamCode}
AND is_delete = '0'
LIMIT 1
</select>
<select id="selectByTeamName" resultMap="ConstructionTeamResult">
SELECT *
FROM construction_team
WHERE team_name = #{teamName}
AND is_delete = '0'
LIMIT 1
</select>
<insert id="insertConstructionTeam" parameterType="ConstructionTeam" useGeneratedKeys="true" keyProperty="id">
insert into construction_team
<trim prefix="(" suffix=")" suffixOverrides=",">
@@ -59,6 +75,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</trim>
</insert>
<select id="selectMaxTeamCode" resultType="java.lang.String">
SELECT MAX(team_code)
FROM construction_team
WHERE team_code LIKE 'CT%'
</select>
<update id="updateConstructionTeam" parameterType="ConstructionTeam">
update construction_team
<trim prefix="SET" suffixOverrides=",">

View File

@@ -182,6 +182,15 @@
LIMIT 1
</select>
<select id="selectEncodedIdByPcodeAndWarehouse" resultType="java.lang.String">
SELECT encoded_id
FROM pcde_detail
WHERE pcode = #{pcode}
AND warehouse_code = #{warehouseCode}
AND is_delete = '0'
LIMIT 1
</select>
<!-- 单条插入 -->
<insert id="insertPcdeDetail"
parameterType="PcdeDetail"

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>

View File

@@ -59,11 +59,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="status != null and status != ''">
and status = #{status}
</if>
and (status is null or trim(status) != '1')
<!-- and (status is null or trim(status) != '1') -->
<if test="isDelete != null and isDelete != ''">
and is_delete = #{isDelete}
</if>
<!-- 按创建时间筛选create_time 在 [beginTime, endTime] -->
<if test="beginTime != null">
and create_time <![CDATA[>=]]> #{beginTime}
</if>
<if test="endTime != null">
and create_time <![CDATA[<]]> #{endTime}
</if>
</where>
</select>
@@ -75,7 +81,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="getBySapNo" parameterType="java.lang.String" resultMap="GysJhResult">
<include refid="selectGysJhVo"/>
WHERE sap_no = #{sapNo}
AND status != '1'
</select>
<!-- 【已注释】唯一性校验查询基于SAP订单号、项目号、物料号判断是否已存在 -->
@@ -234,5 +239,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</foreach>
</delete>
<select id="selectByWlNo" resultMap="GysJhResult">
SELECT *
FROM gys_jh
WHERE wl_no = #{wlNo}
AND is_delete = '0'
</select>
<update id="updateDwById">
UPDATE gys_jh
SET dw = #{dw}
WHERE id = #{id}
</update>
</mapper>

File diff suppressed because it is too large Load Diff