1.0版本提交

This commit is contained in:
李宇奇 2025-07-25 12:35:54 +08:00
parent 54045a2fb4
commit ad9ee93500
67 changed files with 2039 additions and 601 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.idea/
**/.idea/
*.iml
.vscode/
wms_log/

5
.idea/.gitignore vendored
View File

@ -1,5 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Environment-dependent path to Maven home directory
/mavenHomeManager.xml

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/202504-Wms-MengYang.iml" filepath="$PROJECT_DIR$/.idea/202504-Wms-MengYang.iml" />
</modules>
</component>
</project>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/202504-Wms-MengYang-box" vcs="Git" />
<mapping directory="$PROJECT_DIR$/202504-Wms-MengYang-tp" vcs="Git" />
</component>
</project>

View File

@ -1,5 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Environment-dependent path to Maven home directory
/mavenHomeManager.xml

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/202504-Wms-MengYang-box.iml" filepath="$PROJECT_DIR$/.idea/202504-Wms-MengYang-box.iml" />
</modules>
</component>
</project>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -1,8 +0,0 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CommonStatePersistent">
<option name="searchCache" value="Vechile" />
</component>
</project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CoolRequestCommonStatePersistent">
<option name="searchCache" value="TAPPTaskBak" />
</component>
</project>

View File

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CoolRequestSetting">
<option name="serializePaths">
<list>
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\0731597f11a84ef9a829b9c437031693" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\c2da60b5bb4f438da6ff954a1346b32a" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\85ec0988f8bb450badbde0c5045e6f4f" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\61490adb4d49462aaa40afbbac8699e6" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\76cdc0dbab984b13a97a0db9a3df6095" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\36536ff461cd40c988172b846eb25446" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\b6d54e66c7284e47989ca35b263db167" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\2be9aef4c51247b89ce2e1cd4b2df4fc" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\b3cc1afd501342d99c561026bce48d76" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\8fae6093623c4ac5a1c0365a4ded5536" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\acedef38ef5940a598c4ffe04e50c401" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\7ade37455d3e4d0ab3491e3a869f61f5" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\d15666989172454c805e3b2272e04141" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\d1888b44bae647b08e8125548c6bb98e" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\dff01454c888463d8f560f44f5704845" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\301bbc7b37a248a6854096f807d20d29" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\fee8eb3055c54e0ab41294f6f8d53bef" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\067a1c7afeb64b63aaf87483016a0581" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\bee22dfdeffc4d5998c22fdeddebae26" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\fa7cf9b8dcfb4c8f9a2f8b6397c8b561" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\969f157d209a4d95bd33fb4c78fbb865" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\1b50b1aa4d154b7d958596a91c09d973" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\60a6a267ee424dada8a2cc2013695964" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\e2629fff699d4beda8d0d9d21552856e" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\09af44f9079d463e9d2d962caeb47d24" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\68b9bda72d11450aa3f7dc61d37426cd" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\ca1c303e1c9240c8b279b02ea529b049" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\fb590408ed954d6f82ac99c73b3838ee" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\2f629eccee904dcb8da5d5a04d549c6d" />
<option value="C:\Users\btoba\.config\.cool-request\request\serialize\949fb2693bfb4d649e246b767241ae51" />
</list>
</option>
<option name="projectCachePath" value="project-b7ef1676-f3ff-4c1d-950c-a8cb1bbc2462" />
</component>
</project>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile default="true" name="Default" enabled="true" />
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="dev_wms_serve" />
</profile>
</annotationProcessing>
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="dev_wms_serve" options="-parameters" />
</option>
</component>
</project>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/java/com/wms_main/service/quartz_job/serviceImpl/JobServiceImpl.java" charset="UTF-8" />
</component>
</project>

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://maven.aliyun.com/repository/public" />
</remote-repository>
</component>
</project>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK" />
</project>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View File

@ -4,12 +4,29 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## 项目概述
这是一个宝应梦阳WMS仓库管理系统后端项目基于Spring Boot 3.3.5和Java 21开发使用Maven构建。系统管理仓库的入库、出库、库存和任务调度等核心业务。
这是一个宝应梦阳WMS仓库管理系统项目包含前后端分离的完整解决方案
- **后端项目**基于Spring Boot 3.3.5和Java 21开发使用Maven构建
- **前端项目**基于Vue 3和Element Plus开发提供Web管理界面
- **业务场景**:支持托盘库(TP)和料箱库(BOX)两种存储类型,管理仓库的入库、出库、库存和任务调度等核心业务
## 项目结构
本项目包含两个子项目,位于不同目录:
- `202504-Wms-MengYang-box/` - 料箱库(BOX)版本
- `202504-Wms-MengYang-tp/` - 托盘库(TP)版本
每个子项目都包含:
- `wms_serve_mengyang/` - Spring Boot后端服务
- `wms_web_mengyang/` - Vue.js前端应用
- `db/` - 数据库SQL文件
## 开发环境设置
### 构建和运行命令
### 后端项目命令Maven
```bash
# 进入后端目录以BOX版本为例
cd 202504-Wms-MengYang-box/wms_serve_mengyang
# 编译项目
mvn clean compile
@ -19,28 +36,66 @@ mvn clean package
# 运行项目
mvn spring-boot:run
# 跳过测试运行(默认配置
# 跳过测试运行(项目配置默认跳过测试
mvn clean package -Dmaven.test.skip=true
```
### 数据库配置
### 前端项目命令Vue.js
```bash
# 进入前端目录以BOX版本为例
cd 202504-Wms-MengYang-box/wms_web_mengyang
# 安装依赖
npm install
# 启动开发服务器
npm run serve
# 构建生产版本
npm run build
# 代码检查和修复
npm run lint
```
### 运行环境配置
**数据库配置**
- 本地开发MySQL `localhost:3306/wms_mengyang_box`
- 生产环境MySQL `10.18.58.21:3306/wms_yachi_nantong`
- 用户名/密码本地使用root/root生产使用user/user
- 认证信息本地使用root/root生产使用user/user
### 应用配置
**后端服务 (Spring Boot)**
- 服务端口12315
- 应用名称wms_main
- 文件上传限制单文件100MB总计1000MB
- 当前配置:`spring.profiles.active=online`
**前端应用 (Vue.js)**
- 开发端口12306
- 后端API地址`http://10.18.58.21:12315`
- 路由模式Hash路由
## 核心架构
### 分层架构
**后端架构Spring Boot**
系统采用标准的MVC三层架构
- **Controller层**REST API接口包含mywms和wms两个模块
- **Service层**业务逻辑层分为controller、business、api三个子模块
- **DAO层**数据访问层使用MyBatis-Plus
**前端架构Vue.js**
采用现代化的Vue 3组件化架构
- **Views层**页面视图组件login、HomeView、SystemCenter
- **Layout层**:业务布局组件(库存、入库、出库等功能页面)
- **Components层**可复用组件sideMenu、appTag
- **API层**HTTP请求封装和接口定义
- **Store层**Vuex状态管理用户信息、菜单权限、标签页
- **Router层**Vue Router路由管理
- **Utils层**:工具函数(日期处理、格式化、加密等)
### 核心业务模块
#### 1. MyWMS模块 (`controller/mywms/`)
@ -72,35 +127,79 @@ mvn clean package -Dmaven.test.skip=true
- `vo/` - 视图对象View Object
### 常用枚举类 (`constant/enums/`)
**WMS业务枚举** (`enums/wms/`)
- `StorageTypeEnums` - 存储类型托盘库TP/料箱库BOX
- `WmsDepthStrategyEnums` - 深度策略枚举
- `WmsTaskTypeEnums` - 任务类型枚举
- `OrderStatusEnum` - 订单状态枚举
- `WmsLocationTypeEnums` - 货位类型枚举
- `WmsGoodsStatusEnums` - 货物状态枚举
- `WmsVehicleStatusEnums` - 载具状态枚举
**WCS集成枚举** (`enums/wcs/`)
- `WcsApiResponseCodeEnums` - WCS API响应码
- `WcsStackerTaskTypeEnums` - 堆垛机任务类型
- `WcsStackerTaskStatusEnums` - 堆垛机任务状态
**任务调度枚举** (`enums/jobs/`)
- `JobStatusEnums` - 任务状态枚举
- `JobTimerTypeEnums` - 定时器类型枚举
## 技术栈
### 核心依赖
### 后端技术栈Spring Boot
**核心依赖**
- Spring Boot Web 3.3.5 - REST API框架
- Spring Boot Quartz - 定时任务调度
- MyBatis-Plus 3.5.7 - ORM框架
- MySQL Connector - 数据库连接
- Lombok - 代码简化
### 工具库
**工具库**
- Hutool 5.8.33 - Java工具类库
- FastJSON 2.0.21 - JSON处理
- EasyExcel 4.0.3 & EasyPOI 4.5.0 - Excel处理
- Apache HttpClient 4.5.13 - HTTP客户端
- Google Guava 33.3.1 - 集合工具
### 前端技术栈Vue.js
**核心框架**
- Vue 3.2.13 - 前端框架
- Vue Router 4.0.3 - 路由管理
- Vuex 4.0.0 - 状态管理
- Element Plus 2.4.0 - UI组件库
**主要依赖**
- Axios 1.3.3 - HTTP请求库
- Moment 2.29.4 - 日期处理
- XLSX 0.18.5 - Excel文件处理
- QRCode.vue 3.4.1 - 二维码生成
- Vue3-print-nb 0.1.4 - 打印功能
- File-saver 2.0.5 - 文件下载
**开发工具**
- Sass 1.83.4 - CSS预处理器
- ESLint - 代码质量检查
- Babel - JavaScript编译器
## 开发注意事项
### 代码规范
### 后端代码规范
- 使用Lombok注解减少样板代码
- 统一的API响应格式WmsApiResponse
- 枚举类管理常量值
- 接口-实现分离的服务层设计
### 前端代码规范
- Vue 3 Composition API优先
- Element Plus组件库统一UI风格
- Vuex模块化状态管理
- ESLint代码质量检查关闭未使用变量警告
- 响应式设计,支持不同屏幕尺寸
### 数据库操作
- 使用MyBatis-Plus进行ORM操作
- 支持批量操作和事务处理
@ -113,11 +212,15 @@ mvn clean package -Dmaven.test.skip=true
- 请求日志过滤器:`RequestLogFilter`
### Excel处理
系统支持完整的Excel导入导出功能
**后端**使用EasyExcel和EasyPOI双重支持
- 货物信息、产品信息、库存信息等业务数据
- 使用EasyExcel和EasyPOI双重支持
- 模板位置:`excel/easypoi/excelTemplate/`
**前端**使用XLSX和Element Plus支持
- 文件上传组件:`excel/` 目录下的各类Excel上传组件
- 支持在线预览和数据导出
- 集成二维码生成和打印功能
## 外部系统集成
### WCS系统集成
@ -131,8 +234,91 @@ mvn clean package -Dmaven.test.skip=true
## 测试说明
**后端测试**
项目配置中跳过了单元测试执行(`maven-surefire-plugin.skip=true`),在开发新功能时建议:
1. 先实现功能代码
2. 使用Postman等工具进行API测试
3. 检查数据库数据状态
4. 验证定时任务执行情况
**前端测试**
- 使用浏览器开发者工具调试
- 通过Vue DevTools检查组件状态
- 使用Element Plus组件库确保UI一致性
- 在不同浏览器Chrome、Firefox、Edge中测试兼容性
## 开发注意事项
### 特殊的Maven配置
- 项目默认跳过单元测试:`maven-surefire-plugin.skip=true`
- 最终构建文件名:`wms-box-server`
- Java版本21 (source和target都是21)
- Maven Wrapper可用使用`./mvnw`替代`mvn`命令
### 重要的文件路径
- 主要工作目录:`202504-Wms-MengYang-box/wms_serve_mengyang/`
- 日志存储:`wms_log/` (按日期和级别分类)
- 数据库脚本:`db/wms_mengyang_box.sql`
- MyBatis映射`src/main/resources/mapper/`
### 常见开发任务
**启动后端服务**
```bash
cd 202504-Wms-MengYang-box/wms_serve_mengyang
mvn spring-boot:run
# 或使用Maven Wrapper
./mvnw spring-boot:run
```
**启动前端开发服务器**
```bash
cd 202504-Wms-MengYang-box/wms_web_mengyang
npm run serve
```
**查看日志**
```bash
# 查看今天的info日志
tail -f wms_log/info/$(date +%Y-%m-%d)/$(date +%Y-%m-%d).0.log
# 查看今天的error日志
tail -f wms_log/error/$(date +%Y-%m-%d)/$(date +%Y-%m-%d).0.log
```
**数据库操作**
- 使用MyBatis-Plus进行ORM操作
- 支持批量操作和事务处理
- 驼峰命名自动映射(`map-underscore-to-camel-case: true`)
- ID生成策略`assign_id`
### 系统集成要点
**WCS系统集成**
- TCP和HTTP双协议支持
- 堆垛机任务调度和状态同步
- 载具管理(托盘和料箱)
**任务调度系统**
- 基于Quartz的定时任务框架
- 关键执行器:`WmsTaskExecutor`、`MyOutExecutor`、`WcsStackerTaskSender`
- 任务状态实时监控
**Excel处理**
- 后端EasyExcel 4.0.3 + EasyPOI 4.5.0
- 前端XLSX 0.18.5 + Element Plus上传组件
- 模板位置:`excel/easypoi/excelTemplate/`
## 故障排除
### 常见问题
1. **端口冲突**确认12315端口未被占用
2. **数据库连接**检查MySQL服务状态和配置
3. **前端代理**确认后端API地址配置正确
4. **日志分析**:按级别查看对应日志目录
### 调试技巧
- 使用`RequestLogFilter`查看API请求日志
- 通过`WmsControllerExceptionHandler`统一异常处理
- 检查定时任务执行状态:查看`TAppJobs`表
- Vue DevTools调试前端状态管理

View File

@ -1,3 +1,3 @@
# wms_serve_mengyang
宝应梦阳料箱库WMS后端
宝应梦阳托盘库WMS后端

View File

@ -0,0 +1,69 @@
### MyWMS接口测试
### 基础配置
@baseUrl = http://localhost:12315
### 1. 入库订单接口
POST {{baseUrl}}/mywms/orderIn
Content-Type: application/json
{
"taskId": "testOrderId6",
"vehicleNo": "1006"
}
### 2. 出库订单接口
POST {{baseUrl}}/mywms/orderOut
Content-Type: application/json
{
"taskId": "testOrderId1",
"vehicleNo": "1004"
}
### 3. 库存查询接口 - 仅按载具查询
POST {{baseUrl}}/mywms/stock
Content-Type: application/json
{
"requestId": "testOrderId1",
"details": [
{
"vehicleNo": "1001"
}
]
}
### 4. 库存查询接口 - 仅按库位查询
POST {{baseUrl}}/mywms/stock
Content-Type: application/json
{
"requestId": "testOrderId2",
"details": [
{
"locationId": "A02-01-02-02"
}
]
}
### 5. 检查是否允许投料
GET {{baseUrl}}/mywms/allowFeed
### 6. 允许出库
GET {{baseUrl}}/mywms/allowOut
### 测试数据说明
# 1. orderIn: 入库订单接口需要taskId和vehicleNo
# 2. orderOut: 出库订单接口需要taskId和vehicleNo
# 3. stock: 库存查询接口需要requestId和details数组
# - details中可以包含vehicleNo和/或locationId
# 4. allowFeed: 查询当前系统是否允许投料操作GET请求
### 响应格式说明
# 所有接口都返回MyWmsResponse格式:
# {
# "code": 200, // 响应码
# "message": "操作成功", // 响应消息
# "data": {}, // 具体数据
# "success": true // 是否成功
# }

View File

@ -0,0 +1,60 @@
### TaskController接口测试 - WMS任务控制接口
### 基础配置
@baseUrl = http://localhost:12315
### 1. WCS请求载具入库
POST {{baseUrl}}/wms/task/wcsRequestVehicleIn
Content-Type: application/json
{
"origin": "R1",
"vehicleNo": "1006",
"codeMessage": "test",
"remark": "载具入库测试"
}
### 2. 发送任务结果 - 任务完成
POST {{baseUrl}}/wms/task/sendTaskResult
Content-Type: application/json
{
"taskId": "1753403954324010001",
"taskStatus": 100,
"vehicleNo": "1004",
"destination": "C1",
"message": "任务执行成功"
}
### 测试数据说明
# 1. wcsRequestVehicleIn: WCS请求载具入库
# - origin: 点位(必填)
# - vehicleNo: 载具信息(必填)
# - codeMessage: 条码信息(可选)
# - remark: 备注(可选)
# 2. sendTaskResult: WCS反馈任务执行结果
# - taskId: 任务ID必填
# - taskStatus: 任务状态(必填)
# * 0: 等待执行
# * 1: 执行中
# * 2: 执行完成
# * 3: 执行失败
# - vehicleNo: 载具号(必填)
# - destination: 终点(可选)
# - message: 信息(可选)
### 响应格式说明
# wcsRequestVehicleIn返回WcsVehicleInResponse:
# {
# "success": true,
# "code": "SUCCESS",
# "message": "请求成功",
# "data": {}
# }
# sendTaskResult返回BaseWcsApiResponse:
# {
# "success": true,
# "code": "SUCCESS",
# "message": "任务结果接收成功"
# }

View File

@ -9,10 +9,10 @@
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com</groupId>
<artifactId>dev_wms_serve</artifactId>
<artifactId>wms_serve_mengyang_box</artifactId>
<version>3.2</version>
<name>dev_wms_serve</name>
<description>dev_wms_serve</description>
<name>wms_serve_mengyang_box</name>
<description>wms_serve_mengyang_box</description>
<url/>
<licenses>
<license/>
@ -162,6 +162,15 @@
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>21</source>
<target>21</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>

View File

@ -0,0 +1,18 @@
package com.wms_main.constant.enums.mywms;
import lombok.Getter;
@Getter
public enum OrderInCBEnums {
COMPLETE(1, "入库完成"),
CANCEL(2, "入库取消"),
EXCEPTION(3, "入库异常");
private final Integer code;
private final String desc;
OrderInCBEnums(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
}

View File

@ -0,0 +1,18 @@
package com.wms_main.constant.enums.mywms;
import lombok.Getter;
@Getter
public enum OrderOutCBEnums {
COMPLETE(1, "出库完成"),
CANCEL(2, "出库取消"),
EXCEPTION(3, "出库异常");
private final Integer code;
private final String desc;
OrderOutCBEnums(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
}

View File

@ -12,6 +12,7 @@ public enum AppConfigKeyEnums {
WCS_STACKER_TASK_URL("WcsStackerTaskUrl", "发送堆垛机任务地址"),
WCS_PICK_TASK_URL("WcsPickTaskUrl", "发送拣选任务地址"),
WCS_CANCEL_PICK_TASK_URL("WcsCancelPickTaskUrl", "发送取消拣选任务地址"),
WCS_CAN_FEED_URL("wcsCanFeedUrl", "查询wcs是否可上料"),
WCS_RELEASE_BOX_URL("WcsReleaseBoxUrl", "发送释放站台箱子地址"),
STAND_CAPACITY("StandCapacity", "站台容量"),
IMAGE_IP("ImageIp", "图片存放ip"),
@ -19,7 +20,9 @@ public enum AppConfigKeyEnums {
STOCK_WARNING_QTY("StockWarningQty", "库存预警数量"),
MES_GET_GOODS_URL("MesGetGoodsUrl", "获取物料信息地址"),
ERP_GET_VEHICLENO_DATA("ErpGetVehicleNoData", "根据托盘号查询简单生产入库单"),
WMS_MANAGE_CALLBACK("WmsManageCallback", "场内WMS接受的入库出库执行后回调地址");
WMS_ORDER_IN_CALLBACK("WmsOrderInCallback", "WMS接受的入库执行后回调地址"),
WMS_ORDER_OUT_CALLBACK("WmsOrderOutCallback", "WMS接受的出库执行后回调地址");
private final String key;
private final String desc;

View File

@ -0,0 +1,13 @@
package com.wms_main.constant.enums.wms;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum WmsAgvLockEnums {
AVAILABLE(0, "可用"),
LOCK(1, "锁定");
private final Integer code;
private final String desc;
}

View File

@ -7,15 +7,20 @@ import com.wms_main.model.dto.request.mywms.StockRespGoodsDetail;
import com.wms_main.model.dto.response.mywms.MyWmsResponse;
import com.wms_main.service.controller.IMyWmsControllerService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@RestController
@ResponseBody
@CrossOrigin
@RequiredArgsConstructor
@RequestMapping("/mywms")
@Slf4j
public class MyWmsController {
private final IMyWmsControllerService myWmsControllerService;
@ -34,4 +39,14 @@ public class MyWmsController {
public MyWmsResponse<List<StockRespGoodsDetail>> stock(@RequestBody StockReq request) {
return myWmsControllerService.stock(request);
}
@GetMapping("/allowFeed")
public MyWmsResponse<Boolean> canFeed() {
return myWmsControllerService.queryCanFeed();
}
@GetMapping("/allowOut")
public MyWmsResponse<Boolean> canOut() {
return myWmsControllerService.queryCanOut();
}
}

View File

@ -0,0 +1,27 @@
package com.wms_main.dao;
import com.baomidou.mybatisplus.extension.service.IService;
import com.wms_main.model.po.TAppAgvLock;
import java.util.List;
/**
* AGV互锁表数据访问接口
*/
public interface ITAppAgvLockService extends IService<TAppAgvLock> {
/**
* 根据AGV和站点查询锁定记录
*/
List<TAppAgvLock> getByAgvAndStation(String agvId, String feedStation);
/**
* 根据站点查询所有锁定记录
*/
List<TAppAgvLock> getByStation(String feedStation);
/**
* 查询超时的锁定记录
*/
List<TAppAgvLock> getTimeoutLocks();
}

View File

@ -0,0 +1,46 @@
package com.wms_main.dao.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wms_main.dao.ITAppAgvLockService;
import com.wms_main.mapper.AppAgvLockMapper;
import com.wms_main.model.po.TAppAgvLock;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
/**
* AGV互锁表数据访问实现
*/
@Service
public class TAppAgvLockServiceImpl extends ServiceImpl<AppAgvLockMapper, TAppAgvLock> implements ITAppAgvLockService {
@Override
public List<TAppAgvLock> getByAgvAndStation(String agvId, String feedStation) {
return list(new LambdaQueryWrapper<TAppAgvLock>()
.eq(TAppAgvLock::getAgvId, agvId)
.eq(TAppAgvLock::getFeedStation, feedStation)
.eq(TAppAgvLock::getLockStatus, 1)
.orderByAsc(TAppAgvLock::getCreateTime)
);
}
@Override
public List<TAppAgvLock> getByStation(String feedStation) {
return list(new LambdaQueryWrapper<TAppAgvLock>()
.eq(TAppAgvLock::getFeedStation, feedStation)
.eq(TAppAgvLock::getLockStatus, 1)
.orderByAsc(TAppAgvLock::getPriority)
.orderByAsc(TAppAgvLock::getCreateTime)
);
}
@Override
public List<TAppAgvLock> getTimeoutLocks() {
return list(new LambdaQueryWrapper<TAppAgvLock>()
.eq(TAppAgvLock::getLockStatus, 1)
.lt(TAppAgvLock::getLockTime, LocalDateTime.now().minusSeconds(300)) // 超过5分钟的锁定
);
}
}

View File

@ -0,0 +1,12 @@
package com.wms_main.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wms_main.model.po.TAppAgvLock;
import org.apache.ibatis.annotations.Mapper;
/**
* AGV互锁表映射器
*/
@Mapper
public interface AppAgvLockMapper extends BaseMapper<TAppAgvLock> {
}

View File

@ -1,5 +1,6 @@
package com.wms_main.model.dto.request.mywms;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -8,8 +9,18 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
public class OrderInCBReq {
String orderId;
String vehicleNo;
Integer orderStatus;
String message;
@JsonProperty("taskId")
private String taskId;
@JsonProperty("vehicleNo")
private String vehicleNo;
@JsonProperty("locationId")
private String locationId;
@JsonProperty("result")
private Integer result;
@JsonProperty("resultMessage")
private String resultMessage;
}

View File

@ -1,15 +1,10 @@
package com.wms_main.model.dto.request.mywms;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.wms_main.constant.enums.wms.WmsTaskTypeEnums;
import com.wms_main.repository.utils.StringUtils;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import java.util.Objects;
@Getter
@Setter
@ -18,34 +13,11 @@ public class OrderInReq {
@JsonProperty("taskId")
private String taskId;
@JsonProperty("storageId")
private Integer storageId;
@JsonProperty("taskType")
private Integer taskType;
@JsonProperty("vehicleNo")
private String vehicleNo;
@JsonProperty("standId")
private String standId;
@JsonProperty("requestTime")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime requestTime;
@JsonProperty("requestUser")
private String requestUser;
public boolean isValid() {
if (StringUtils.isEmpty(this.getTaskId())
|| StringUtils.isEmpty(this.getVehicleNo())
|| StringUtils.isEmpty(this.getStandId())
|| !Objects.equals(this.getTaskType(), WmsTaskTypeEnums.IN.getCode())) {
return false;
} else {
return true;
}
return !StringUtils.isEmpty(this.getTaskId())
&& !StringUtils.isEmpty(this.getVehicleNo());
}
}

View File

@ -1,5 +1,6 @@
package com.wms_main.model.dto.request.mywms;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -8,9 +9,15 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
public class OrderOutCBReq {
String orderId;
String vehicleNo;
Integer orderStatus;
String outStand;
String message;
@JsonProperty("taskId")
private String taskId;
@JsonProperty("result")
private Integer result;
@JsonProperty("resultMessage")
private String resultMessage;
@JsonProperty("vehicleNo")
private String vehicleNo;
}

View File

@ -7,6 +7,6 @@ import lombok.Setter;
@Getter
@Setter
public class OrderOutGoodsDetail {
@JsonProperty("inTaskId")
private String inTaskId;
@JsonProperty("vehicleNo")
private String vehicleNo;
}

View File

@ -1,14 +1,8 @@
package com.wms_main.model.dto.request.mywms;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import java.util.List;
@Getter
@Setter
@ -17,20 +11,6 @@ public class OrderOutReq {
@JsonProperty("taskId")
private String taskId;
@JsonProperty("storageId")
private Integer storageId;
@JsonProperty("taskType")
private Integer taskType;
@JsonProperty("details")
private List<OrderOutGoodsDetail> details;
@JsonProperty("requestTime")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime requestTime;
@JsonProperty("requestUser")
private String requestUser;
@JsonProperty("vehicleNo")
private String vehicleNo;
}

View File

@ -16,25 +16,10 @@ public class StockReq {
@JsonProperty("requestId")
private String requestId;
@JsonProperty("storageId")
private Integer storageId;
@JsonProperty("requestTime")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private String requestTime;
@JsonProperty("requestUser")
private String requestUser;
@JsonProperty("details")
private List<StockReqGoodsDetail> details;
public boolean empty() {
return StringUtils.isEmpty(requestId)
&& StringUtils.isEmpty(requestTime)
&& StringUtils.isEmpty(requestUser)
&& Objects.isNull(storageId);
return StringUtils.isEmpty(requestId);
}
}

View File

@ -12,12 +12,8 @@ public class StockReqGoodsDetail {
@JsonProperty("locationId")
private String locationId;
@JsonProperty("inTaskId")
private String inTaskId;
public boolean empty() {
return StringUtils.isEmpty(vehicleNo)
&& StringUtils.isEmpty(locationId)
&& StringUtils.isEmpty(inTaskId);
&& StringUtils.isEmpty(locationId);
}
}

View File

@ -0,0 +1,13 @@
package com.wms_main.model.dto.request.wcs;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class WcsCanFeedRequest {
/**
* 入库口编号默认为单一入库口
*/
private String inboundPort;
}

View File

@ -0,0 +1,23 @@
package com.wms_main.model.dto.response.mes;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class MesApiResponse {
@JsonProperty("code")
private Integer code;
@JsonProperty("message")
private String message;
public static MesApiResponse error(String msg) {
return new MesApiResponse(999, msg);
}
}

View File

@ -0,0 +1,47 @@
package com.wms_main.model.dto.response.mes;
import lombok.Getter;
import lombok.Setter;
/**
* MES系统查询是否可上料响应
*/
@Getter
@Setter
public class MesCanFeedResponse {
/**
* 是否可以上料
*/
private Boolean canFeed;
/**
* 入库口状态空闲/占用
*/
private String inboundPortStatus;
/**
* 当前处理中的任务数量
*/
private Integer processingTaskCount;
/**
* 响应消息
*/
private String message;
/**
* 预留字段1
*/
private String reserved1;
/**
* 预留字段2
*/
private String reserved2;
/**
* 预留字段3
*/
private String reserved3;
}

View File

@ -17,7 +17,7 @@ public class WcsApiResponse<T> extends BaseWcsApiResponse {
/**
* 返回数据
*/
@JsonProperty("data")
@JsonProperty("returnData")
private T data;
/**

View File

@ -0,0 +1,27 @@
package com.wms_main.model.dto.response.wcs;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
/**
* 立库查询是否可上料响应
*/
@Getter
@Setter
public class WcsCanFeedResponse {
@JsonProperty("responseTime")
@JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd hh:mm:ss")
private LocalDateTime responseTime;
@JsonProperty("allowAction")
boolean allowAction;
@JsonProperty("msg")
String msg;
}

View File

@ -0,0 +1,87 @@
package com.wms_main.model.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
/**
* AGV互锁表
*/
@Getter
@Setter
@TableName(value = "t_app_agv_lock", autoResultMap = true)
public class TAppAgvLock {
@TableId(value = "lock_id")
private String lockId;
/**
* AGV设备ID
*/
@TableField(value = "agv_id")
private String agvId;
/**
* 上料站点
*/
@TableField(value = "feed_station")
private String feedStation;
/**
* 锁定状态: 1-锁定中, 0-已释放
*/
@TableField(value = "lock_status")
private Integer lockStatus;
/**
* 锁定类型: FEED-上料锁定, WAIT-等待锁定
*/
@TableField(value = "lock_type")
private String lockType;
/**
* 优先级: 数字越小优先级越高
*/
@TableField(value = "priority")
private Integer priority;
/**
* 队列位置
*/
@TableField(value = "queue_position")
private Integer queuePosition;
/**
* 锁定时间
*/
@TableField(value = "lock_time")
private LocalDateTime lockTime;
/**
* 释放时间
*/
@TableField(value = "unlock_time")
private LocalDateTime unlockTime;
/**
* 超时时间
*/
@TableField(value = "timeout_seconds")
private Integer timeoutSeconds;
/**
* 备注
*/
@TableField(value = "remark")
private String remark;
@TableField(value = "create_time")
private LocalDateTime createTime;
@TableField(value = "update_time")
private LocalDateTime updateTime;
}

View File

@ -19,8 +19,8 @@ public class TAppOrderOut {
@TableField(value = "order_id")
private String orderId;
@TableField(value = "in_task_id")
private String inTaskId;
@TableField(value = "vehicle_no")
private String vehicleNo;
@TableField(value = "order_status")
private Integer orderStatus;

View File

@ -1,14 +1,12 @@
package com.wms_main.service.api;
import com.wms_main.model.bo.ycwms.YCWmsApiResponse;
import com.wms_main.model.dto.request.mywms.OrderInCBReq;
import com.wms_main.model.dto.request.mywms.OrderOutCBReq;
import com.wms_main.model.dto.response.mywms.MyWmsResponse;
import com.wms_main.model.dto.response.mes.MesApiResponse;
public interface IExternalApiService {
// MyWmsResponse<ErpVehicleNoResp> getVehicleNoData(String vehicleNo);
MyWmsResponse invokeOrderInCB(OrderInCBReq request);
MesApiResponse invokeOrderInCB(OrderInCBReq request);
MyWmsResponse invokeOrderOutCB(OrderOutCBReq request);
MesApiResponse invokeOrderOutCB(OrderOutCBReq request);
}

View File

@ -2,11 +2,13 @@ package com.wms_main.service.api;
import com.wms_main.model.bo.wcs.WcsPickTask;
import com.wms_main.model.bo.wcs.WcsStackerTask;
import com.wms_main.model.dto.request.wcs.WcsCanFeedRequest;
import com.wms_main.model.dto.request.wcs.WcsPickTaskRequest;
import com.wms_main.model.dto.request.wcs.WcsReleaseBoxRequest;
import com.wms_main.model.dto.request.wcs.WcsStackerTaskRequest;
import com.wms_main.model.dto.response.wcs.BaseWcsApiResponse;
import com.wms_main.model.dto.response.wcs.WcsApiResponse;
import com.wms_main.model.dto.response.wcs.WcsCanFeedResponse;
/**
* wcs接口服务
@ -39,4 +41,11 @@ public interface IWcsApiService {
* @return 发送结果
*/
WcsApiResponse<WcsPickTask> cancelPickTask(WcsPickTaskRequest request);
/**
* 查询是否可上料
* @param request 查询请求信息
* @return 查询结果
*/
WcsApiResponse<WcsCanFeedResponse> canFeed(WcsCanFeedRequest request);
}

View File

@ -4,7 +4,7 @@ import com.wms_main.app.AppCommon;
import com.wms_main.constant.enums.wms.AppConfigKeyEnums;
import com.wms_main.model.dto.request.mywms.OrderInCBReq;
import com.wms_main.model.dto.request.mywms.OrderOutCBReq;
import com.wms_main.model.dto.response.mywms.MyWmsResponse;
import com.wms_main.model.dto.response.mes.MesApiResponse;
import com.wms_main.repository.http.HttpClient;
import com.wms_main.repository.http.entity.HttpRequest;
import com.wms_main.repository.http.entity.HttpResponse;
@ -12,12 +12,6 @@ import com.wms_main.service.api.IExternalApiService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
@RequiredArgsConstructor
@ -26,87 +20,27 @@ public class ExternalApiServiceImpl implements IExternalApiService {
private final HttpClient httpClient;// http客户端
private final AppCommon appCommon;// 应用共通
// @Override
// public YcwmsResponse<ErpVehicleNoResp> getVehicleNoData(String vehicleNo) {
// Map<String, String> request = new HashMap<>();
// request.put("vehicleNo", vehicleNo);
// HttpRequest httpRequest = HttpRequest.postInstanceOf(appCommon.getConfigByKey(AppConfigKeyEnums.ERP_GET_VEHICLENO_DATA.getKey()), request);
// HttpResponse httpResponse = httpClient.httpPost(httpRequest);
// if (httpResponse != null && httpResponse.isSuccess()) {
// try {
// String responseMessage = httpResponse.getResponseMessage();
// // 使用Map接收基本响应避免泛型问题
// Map<String, Object> baseResponseMap = JSON.parseObject(responseMessage, Map.class);
//
// // 创建最终返回的对象
// YcwmsResponse<ErpVehicleNoResp> finalResponse = new YcwmsResponse<>();
// finalResponse.setCode((Integer) baseResponseMap.get("code"));
// finalResponse.setMessage((String) baseResponseMap.get("message"));
//
// // 处理returnData部分
// if (baseResponseMap.containsKey("returnData") && baseResponseMap.get("returnData") != null) {
// // 转换returnData部分
// Map<String, Object> returnDataMap = (Map<String, Object>) baseResponseMap.get("returnData");
//
// // 创建ErpVehicleNoResp对象
// ErpVehicleNoResp vehicleData = new ErpVehicleNoResp();
// vehicleData.setVehicleNo((String) returnDataMap.get("vehicleNo"));
//
// // 处理goodsDetail数组
// List<Map<String, Object>> goodsDetailList = (List<Map<String, Object>>) returnDataMap.get("goodsDetail");
// List<ErpGoodsDetail> erpsGoodsDetails = new ArrayList<>();
//
// if (goodsDetailList != null) {
// for (Map<String, Object> detail : goodsDetailList) {
// ErpGoodsDetail goodsDetail = new ErpGoodsDetail();
// goodsDetail.setGoodsId((String) detail.get("goodsId"));
// goodsDetail.setGoodsName((String) detail.get("goodsName"));
// goodsDetail.setBatch((String) detail.get("batch"));
// goodsDetail.setGoodsType((String) detail.get("goodsType"));
// goodsDetail.setSpecification((String) (detail.get("specification")));
// goodsDetail.setQuantity(Double.parseDouble(detail.get("quantity").toString()));
// goodsDetail.setGoodsDesc((String) detail.get("goodsDesc"));
// goodsDetail.setXsfbillno((String) detail.get("xsfbillno"));
// goodsDetail.setXsfseq(Integer.parseInt(detail.get("xsfseq").toString()));
// goodsDetail.setNeibubianhao((String) detail.get("neibubianhao"));
// goodsDetail.setGoodsBarcode((String) detail.get("goodsBarcode"));
// erpsGoodsDetails.add(goodsDetail);
// }
// }
//
// vehicleData.setGoodsDetail(erpsGoodsDetails);
// finalResponse.setReturnData(vehicleData);
// }
//
// return finalResponse;
// } catch (Exception e) {
// return YcwmsResponse.error("解析响应数据失败: " + e.getMessage(), null);
// }
// }
// return YcwmsResponse.error("请求未获得响应信息。", null);
// }
@Override
public MyWmsResponse invokeOrderInCB(OrderInCBReq request) {
HttpRequest httpRequest = HttpRequest.postInstanceOf(appCommon.getConfigByKey(AppConfigKeyEnums.WMS_MANAGE_CALLBACK.getKey()), request);
public MesApiResponse invokeOrderInCB(OrderInCBReq request) {
HttpRequest httpRequest = HttpRequest.postInstanceOf(appCommon.getConfigByKey(AppConfigKeyEnums.WMS_ORDER_IN_CALLBACK.getKey()), request);
HttpResponse httpResponse = httpClient.httpPost(httpRequest);
if (httpResponse != null && httpResponse.isSuccess()) {
MyWmsResponse response = new MyWmsResponse();
response = httpResponse.getData(response.getClass().asSubclass(MyWmsResponse.class));
MesApiResponse response = new MesApiResponse();
response = httpResponse.getData(response.getClass().asSubclass(MesApiResponse.class));
return response;
}
return MyWmsResponse.error("操作失败!", null);
return MesApiResponse.error("操作失败!");
}
@Override
public MyWmsResponse invokeOrderOutCB(OrderOutCBReq request) {
HttpRequest httpRequest = HttpRequest.postInstanceOf(appCommon.getConfigByKey(AppConfigKeyEnums.WMS_MANAGE_CALLBACK.getKey()), request);
public MesApiResponse invokeOrderOutCB(OrderOutCBReq request) {
HttpRequest httpRequest = HttpRequest.postInstanceOf(appCommon.getConfigByKey(AppConfigKeyEnums.WMS_ORDER_OUT_CALLBACK.getKey()), request);
HttpResponse httpResponse = httpClient.httpPost(httpRequest);
if (httpResponse != null && httpResponse.isSuccess()) {
MyWmsResponse response = new MyWmsResponse();
response = httpResponse.getData(response.getClass().asSubclass(MyWmsResponse.class));
MesApiResponse response = new MesApiResponse();
response = httpResponse.getData(response.getClass().asSubclass(MesApiResponse.class));
return response;
}
return MyWmsResponse.error("操作失败!", null);
return MesApiResponse.error("操作失败!");
}
}

View File

@ -4,19 +4,26 @@ import com.wms_main.app.AppCommon;
import com.wms_main.constant.enums.wms.AppConfigKeyEnums;
import com.wms_main.model.bo.wcs.WcsPickTask;
import com.wms_main.model.bo.wcs.WcsStackerTask;
import com.wms_main.model.dto.request.wcs.WcsCanFeedRequest;
import com.wms_main.model.dto.request.wcs.WcsPickTaskRequest;
import com.wms_main.model.dto.request.wcs.WcsReleaseBoxRequest;
import com.wms_main.model.dto.request.wcs.WcsStackerTaskRequest;
import com.wms_main.model.dto.response.wcs.BaseWcsApiResponse;
import com.wms_main.model.dto.response.wcs.WcsApiResponse;
import com.wms_main.model.dto.response.wcs.WcsCanFeedResponse;
import com.wms_main.model.po.TAppAgvLock;
import com.wms_main.repository.http.HttpClient;
import com.wms_main.repository.http.entity.HttpRequest;
import com.wms_main.repository.http.entity.HttpResponse;
import com.wms_main.service.api.IWcsApiService;
import com.wms_main.service.business.IAgvLockService;
import com.wms_main.service.business.IStackerTaskService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
/**
* Wcs接口服务实现
*/
@ -26,6 +33,8 @@ import org.springframework.stereotype.Service;
public class WcsApiServiceImpl implements IWcsApiService {
private final HttpClient httpClient;// http客户端
private final AppCommon appCommon;// 应用共通
private final IAgvLockService agvLockService;// AGV互锁服务
private final IStackerTaskService stackerTaskService;// 库位分配服务
/**
* 发送堆垛机任务
@ -97,4 +106,27 @@ public class WcsApiServiceImpl implements IWcsApiService {
}
return WcsApiResponse.error("请求未获得响应信息。", null);
}
/**
* 查询WCS系统是否可上料
* @param request 查询请求信息
* @return 查询结果
*/
@Override
public WcsApiResponse<WcsCanFeedResponse> canFeed(WcsCanFeedRequest request) {
HttpRequest httpRequest = HttpRequest.postInstanceOf(appCommon.getConfigByKey(AppConfigKeyEnums.WCS_CAN_FEED_URL.getKey()), request);
// HttpResponse httpResponse = httpClient.httpPost(httpRequest);
// if (httpResponse != null && httpResponse.isSuccess()) {
// WcsApiResponse<WcsCanFeedResponse> response = new WcsApiResponse<>();
// response = httpResponse.getData(response.getClass().asSubclass(WcsApiResponse.class));
// return response;
// }
// TODO error -> success
// return WcsApiResponse.error("请求未获得响应信息。", null);
WcsCanFeedResponse wcsCanFeedResponse = new WcsCanFeedResponse();
wcsCanFeedResponse.setResponseTime(LocalDateTime.now());
wcsCanFeedResponse.setMsg("success");
wcsCanFeedResponse.setAllowAction(true);
return WcsApiResponse.success("成功", wcsCanFeedResponse);
}
}

View File

@ -0,0 +1,52 @@
package com.wms_main.service.business;
import com.wms_main.model.po.TAppAgvLock;
/**
* 入库口互锁服务接口
* 管理单一入库口的AGV上料互锁机制
*/
public interface IAgvLockService {
/**
* AGV就位并放货后锁定入库口
* @param agvId AGV设备ID
* @param inboundPort 入库口编号
* @return 锁定结果成功返回锁定记录失败返回null
*/
TAppAgvLock lockInboundPort(String agvId, String inboundPort);
/**
* 检查入库口是否可以上料MES系统调用
* @param inboundPort 入库口编号
* @return 是否可以上料
*/
boolean canFeedToInboundPort(String inboundPort);
/**
* 获取当前入库口处理中的任务数量
* @param inboundPort 入库口编号
* @return 处理中的任务数量
*/
int getProcessingTaskCount(String inboundPort);
/**
* 获取入库口状态描述
* @param inboundPort 入库口编号
* @return 状态描述空闲/占用
*/
String getInboundPortStatus(String inboundPort);
/**
* 清理超时锁定
* @return 清理的记录数
*/
int cleanTimeoutLocks();
/**
* 释放指定入库口的所有活动锁定
* @param inboundPort 入库口编号
* @return 释放的锁定数量
*/
int releaseAllInboundPortLocks(String inboundPort);
}

View File

@ -13,11 +13,11 @@ import java.util.List;
public interface IDepthStrategyService {
/**
* 分析出库订单的深度策略
* @param orderOut 出库订单
* 基于载具号分析深度策略
* @param vehicleNo 载具号
* @return 深度策略分析结果
*/
DepthStrategyResult analyzeDepthStrategy(TAppOrderOut orderOut);
DepthStrategyResult analyzeDepthStrategyByVehicle(String vehicleNo);
/**
* 获取指定库位的深度信息

View File

@ -0,0 +1,232 @@
package com.wms_main.service.business.serviceImpl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.wms_main.constant.enums.wms.WmsAgvLockEnums;
import com.wms_main.dao.ITAppAgvLockService;
import com.wms_main.dao.ITAppTaskService;
import com.wms_main.model.po.TAppAgvLock;
import com.wms_main.model.po.TAppTask;
import com.wms_main.constant.enums.wms.WmsTaskTypeEnums;
import com.wms_main.constant.enums.wms.WmsStackerTaskStatusEnums;
import com.wms_main.repository.utils.StringUtils;
import com.wms_main.repository.utils.UUIDUtils;
import com.wms_main.service.business.IAgvLockService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
/**
* 入库口互锁服务实现
* 管理单一入库口的AGV上料互锁机制
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class AgvLockServiceImpl implements IAgvLockService {
private final ITAppAgvLockService agvLockService;
private final ITAppTaskService appTaskService;
private static final String DEFAULT_INBOUND_PORT = "R1"; // 默认入库口
private static final int DEFAULT_TIMEOUT_SECONDS = 600; // 默认超时时间10分钟
@Override
@Transactional
public TAppAgvLock lockInboundPort(String agvId, String inboundPort) {
if (StringUtils.isEmpty(agvId)) {
log.warn("AGV ID为空");
return null;
}
// 使用默认入库口如果未指定
if (StringUtils.isEmpty(inboundPort)) {
inboundPort = DEFAULT_INBOUND_PORT;
}
// 清理超时锁定
cleanTimeoutLocks();
// 检查入库口是否已被占用
List<TAppAgvLock> existingLocks = agvLockService.list(new LambdaQueryWrapper<TAppAgvLock>().eq(TAppAgvLock::getFeedStation, inboundPort));
if (existingLocks == null || existingLocks.isEmpty()) {
return createInboundPortLock(agvId, inboundPort);
} else {
agvLockService.update(
new LambdaUpdateWrapper<TAppAgvLock>()
.eq(TAppAgvLock::getFeedStation, inboundPort)
.eq(TAppAgvLock::getLockStatus, WmsAgvLockEnums.AVAILABLE.getCode())
.set(TAppAgvLock::getLockStatus, WmsAgvLockEnums.LOCK.getCode())
.set(TAppAgvLock::getLockTime, LocalDateTime.now())
.set(TAppAgvLock::getRemark, "AGV就位并放货锁定入库口")
);
return null;
}
}
@Override
public boolean canFeedToInboundPort(String inboundPort) {
// 使用默认入库口如果未指定
if (StringUtils.isEmpty(inboundPort)) {
inboundPort = DEFAULT_INBOUND_PORT;
}
// 清理超时锁定
cleanTimeoutLocks();
// 检查入库口是否有活动的锁定
List<TAppAgvLock> activeLocks = agvLockService.getByStation(inboundPort);
boolean hasActiveLock = activeLocks.stream()
.anyMatch(lock -> lock.getLockStatus() == 1);
if (hasActiveLock) {
log.debug("入库口 {} 当前被占用,无法上料", inboundPort);
return false;
}
// 检查是否有正在处理的入库任务
int processingTasks = getProcessingTaskCount(inboundPort);
boolean canFeed = processingTasks == 0;
log.debug("入库口 {} 可上料状态: {}, 处理中任务数: {}", inboundPort, canFeed, processingTasks);
return canFeed;
}
@Override
public int getProcessingTaskCount(String inboundPort) {
// 使用默认入库口如果未指定
if (StringUtils.isEmpty(inboundPort)) {
inboundPort = DEFAULT_INBOUND_PORT;
}
// 统计正在处理的入库任务数量包括等待运行状态的任务
List<TAppTask> processingTasks = appTaskService.list(
new LambdaQueryWrapper<TAppTask>()
.eq(TAppTask::getTaskType, WmsTaskTypeEnums.IN.getCode())
.in(TAppTask::getTaskStatus,
WmsStackerTaskStatusEnums.WAIT.getCode(),
WmsStackerTaskStatusEnums.RUN.getCode())
.like(TAppTask::getOrigin, inboundPort) // 假设任务来源包含入库口信息
);
return processingTasks.size();
}
@Override
public String getInboundPortStatus(String inboundPort) {
// 使用默认入库口如果未指定
if (StringUtils.isEmpty(inboundPort)) {
inboundPort = DEFAULT_INBOUND_PORT;
}
// 检查是否有活动锁定
List<TAppAgvLock> activeLocks = agvLockService.getByStation(inboundPort);
boolean hasActiveLock = activeLocks.stream()
.anyMatch(lock -> lock.getLockStatus() == 1);
if (hasActiveLock) {
return "占用";
}
// 检查是否有处理中的任务
int processingTasks = getProcessingTaskCount(inboundPort);
if (processingTasks > 0) {
return "占用";
}
return "空闲";
}
@Override
@Transactional
public int cleanTimeoutLocks() {
List<TAppAgvLock> timeoutLocks = agvLockService.getTimeoutLocks();
if (timeoutLocks.isEmpty()) {
return 0;
}
try {
for (TAppAgvLock lock : timeoutLocks) {
agvLockService.update(
new LambdaUpdateWrapper<TAppAgvLock>()
.eq(TAppAgvLock::getLockId, lock.getLockId())
.set(TAppAgvLock::getLockStatus, 0)
.set(TAppAgvLock::getUnlockTime, LocalDateTime.now())
.set(TAppAgvLock::getRemark, "超时自动释放")
);
log.info("释放超时锁定: AGV {} 在入库口 {}", lock.getAgvId(), lock.getFeedStation());
}
log.info("清理了 {} 个超时锁定", timeoutLocks.size());
return timeoutLocks.size();
} catch (Exception e) {
log.error("清理超时锁定失败", e);
return 0;
}
}
@Override
@Transactional
public int releaseAllInboundPortLocks(String inboundPort) {
// 使用默认入库口如果未指定
if (StringUtils.isEmpty(inboundPort)) {
inboundPort = DEFAULT_INBOUND_PORT;
}
try {
// 释放指定入库口的所有活动锁定
agvLockService.update(
new LambdaUpdateWrapper<TAppAgvLock>()
.eq(TAppAgvLock::getFeedStation, inboundPort)
.eq(TAppAgvLock::getLockStatus, WmsAgvLockEnums.LOCK.getCode())
.set(TAppAgvLock::getLockStatus, WmsAgvLockEnums.AVAILABLE.getCode())
.set(TAppAgvLock::getUnlockTime, LocalDateTime.now())
.set(TAppAgvLock::getRemark, "入库任务完成,批量释放锁定")
);
// 查询实际释放的锁定数量
List<TAppAgvLock> activeLocks = agvLockService.getByStation(inboundPort);
int releasedCount = (int) activeLocks.stream()
.filter(lock -> lock.getLockStatus() == 0)
.count();
log.info("释放入库口 {} 的所有锁定,共释放 {} 个锁定", inboundPort, releasedCount);
return releasedCount;
} catch (Exception e) {
log.error("释放入库口 {} 的所有锁定失败", inboundPort, e);
return 0;
}
}
/**
* 创建入库口锁定记录
*/
private TAppAgvLock createInboundPortLock(String agvId, String inboundPort) {
TAppAgvLock lock = new TAppAgvLock();
lock.setLockId(UUIDUtils.getNewUUID());
lock.setAgvId(agvId);
lock.setFeedStation(inboundPort);
lock.setLockStatus(1);
lock.setLockType("INBOUND_PORT_LOCK");
lock.setPriority(1);
lock.setQueuePosition(1);
lock.setLockTime(LocalDateTime.now());
lock.setTimeoutSeconds(DEFAULT_TIMEOUT_SECONDS);
lock.setCreateTime(LocalDateTime.now());
lock.setUpdateTime(LocalDateTime.now());
lock.setRemark("AGV就位并放货锁定入库口");
if (agvLockService.save(lock)) {
log.info("AGV {} 成功锁定入库口 {}", agvId, inboundPort);
return lock;
}
log.error("AGV {} 锁定入库口 {} 失败", agvId, inboundPort);
return null;
}
}

View File

@ -19,7 +19,7 @@ import java.util.List;
/**
* 简化后的深度策略服务实现
* 核心逻辑根据inTaskId查询库存检测深度策略
* 核心逻辑基于载具号查询库存检测深度策略
*/
@Service
@RequiredArgsConstructor
@ -31,36 +31,32 @@ public class DepthStrategyServiceImpl implements IDepthStrategyService {
private final IStackerTaskService stackerTaskService;
/**
* 分析出库订单的深度策略核心方法
* 基于载具号分析深度策略核心方法
*/
@Override
public DepthStrategyResult analyzeDepthStrategy(TAppOrderOut orderOut) {
// 1. 参数验证
if (orderOut == null || StringUtils.isEmpty(orderOut.getInTaskId())) {
log.warn("出库订单为空或入库任务ID为空");
return DepthStrategyResult.createError("订单信息不完整");
public DepthStrategyResult analyzeDepthStrategyByVehicle(String vehicleNo) {
if (StringUtils.isEmpty(vehicleNo)) {
log.warn("载具号为空");
return DepthStrategyResult.createError("载具号不能为空");
}
// 2. 根据inTaskId查询唯一库存
TAppStock stock = appStockService.getOne(
new LambdaQueryWrapper<TAppStock>()
.eq(TAppStock::getInTaskId, orderOut.getInTaskId())
.eq(TAppStock::getVehicleId, vehicleNo)
.eq(TAppStock::getStockStatus, WmsStockStatusEnums.OK.getCode())
);
if (stock == null) {
log.warn("入库任务ID{}对应的库存不存在", orderOut.getInTaskId());
return DepthStrategyResult.createError("库存不存在");
log.warn("载具号{}对应的库存不存在", vehicleNo);
return DepthStrategyResult.createError("载具库存不存在");
}
// 3. 获取库存深度
Integer depth = getLocationDepth(stock.getLocationId());
if (depth == null) {
log.warn("无法解析库存位置深度: {}", stock.getLocationId());
return DepthStrategyResult.createError("库位深度解析失败");
}
// 4. 深度策略判断
if (depth == 1) {
return DepthStrategyResult.createDirectOut("深度1直接出库");
} else if (depth == 2) {

View File

@ -3,10 +3,13 @@ package com.wms_main.service.business.serviceImpl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.wms_main.app.AppCommon;
import com.wms_main.constant.enums.mywms.OrderInCBEnums;
import com.wms_main.constant.enums.mywms.OrderOutCBEnums;
import com.wms_main.constant.enums.wms.*;
import com.wms_main.dao.*;
import com.wms_main.model.dto.request.mywms.OrderInCBReq;
import com.wms_main.model.dto.request.mywms.OrderOutCBReq;
import com.wms_main.model.dto.response.mywms.MyWmsResponse;
import com.wms_main.model.dto.response.mes.MesApiResponse;
import com.wms_main.model.po.*;
import com.wms_main.repository.utils.ConvertUtils;
import com.wms_main.repository.utils.StringUtils;
@ -22,7 +25,6 @@ import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import static com.wms_main.repository.utils.ConvertUtils.convertDestinationToPoint;
/**
* 服务实现
@ -391,27 +393,28 @@ public class StackerTaskServiceImpl implements IStackerTaskService {
.eq(TAppStock::getVehicleId, orderIn.getVehicleNo()));
}
// OrderInCBReq orderInCBReq = new OrderInCBReq();
// orderInCBReq.setOrderId(orderIn.getOrderId());
// orderInCBReq.setVehicleNo(wmsTask.getVehicleId());
// orderInCBReq.setOrderStatus(OrderStatusEnum.COMPLETE.getCode());
// orderInCBReq.setMessage("入库完成");
//
// int times = 0;
// MyWmsResponse response = null;
// do {
// Thread.sleep(15000L * times);
// response = externalApiService.invokeOrderInCB(orderInCBReq);
// times++;
// } while (!response.getMessage().equals("success") && times <= 10);
// if (response.getMessage().equals("error")) {
// log.error("[wms]上报失败");
// } else {
// log.info("[wms]上报成功");
// }
if (!appOrderInService.removeById(orderIn.getRecordId())) {
log.info("整箱入库完成删除入库单失败,任务:{},原因:删除入库单失败", wmsTask.getTaskId());
}
OrderInCBReq orderInCBReq = new OrderInCBReq();
orderInCBReq.setTaskId(orderIn.getOrderId());
orderInCBReq.setVehicleNo(wmsTask.getVehicleId());
orderInCBReq.setLocationId(wmsTask.getDestination());
orderInCBReq.setResult(OrderInCBEnums.COMPLETE.getCode());
orderInCBReq.setResultMessage("入库完成");
// int times = 0;
// MesApiResponse response = null;
// do {
// Thread.sleep(15000L * times);
// response = externalApiService.invokeOrderInCB(orderInCBReq);
// times++;
// } while (response.getCode() != 0 && times <= 10);
// if (response.getCode() != 0) {
// log.error("[wms]上报失败");
// } else {
// log.info("[wms]上报成功");
// }
// if (!appOrderInService.removeById(orderIn.getRecordId())) {
// log.info("整箱入库完成删除入库单失败,任务:{},原因:删除入库单失败", wmsTask.getTaskId());
// }
TAppVehicle vehicle = new TAppVehicle();
vehicle.setVehicleId(wmsTask.getVehicleId());
vehicle.setVehicleStatus(WmsVehicleStatusEnums.ON.getCode());
@ -508,6 +511,12 @@ public class StackerTaskServiceImpl implements IStackerTaskService {
.eq(TAppStock::getVehicleId, vehicleId));
// 当前载具的任务列表
List<TAppTask> thisVehicleOutTasks = vehicleIdToTaskMap.get(vehicleId);
if (thisVehicleOutTasks.isEmpty()) {
continue;
}
List<String> orderIds = thisVehicleOutTasks.stream().map(TAppTask::getTaskGroup).distinct().toList();
List<TAppOrderOut> orderOuts = appOrderOutService.list(new LambdaQueryWrapper<TAppOrderOut>()
.in(TAppOrderOut::getOrderId, orderIds));
if (!thisVehicleOutTasks.isEmpty()) {
// 存储出库记录
List<TAppTaskBak> stockOutRecordList = thisVehicleOutTasks.stream()
@ -519,39 +528,30 @@ public class StackerTaskServiceImpl implements IStackerTaskService {
.eq(TAppTask::getTaskType, WmsTaskTypeEnums.OUT.getCode())
.in(TAppTask::getTaskId, thisVehicleOutTasks.stream().map(TAppTask::getTaskId).toList()));
// 使用流删除关联的出库订单实体新增的核心部分
List<String> orderIds = thisVehicleOutTasks.stream().map(TAppTask::getTaskGroup).distinct().toList();
List<TAppOrderOut> orderOuts = appOrderOutService.list(new LambdaQueryWrapper<TAppOrderOut>()
.in(TAppOrderOut::getOrderId, orderIds));
if (!orderOuts.isEmpty()) {
appOrderOutService.removeBatchByIds(orderOuts);
}
}
// List<String> orderOutsId = thisVehicleOutTasks.stream().map(TAppTask::getTaskGroup).distinct().toList();
// for (String orderId : orderOutsId) {
// if (orderId.contains("baokai")) {
// continue;
// }
// List<TAppTask> appTasks = vehicleIdToTaskMap.get(vehicleId);
// OrderOutCBReq orderOutCBReq = new OrderOutCBReq();
// orderOutCBReq.setOrderId(orderId);
// orderOutCBReq.setVehicleNo(vehicleId);
// orderOutCBReq.setOrderStatus(OrderStatusEnum.COMPLETE.getCode());
// orderOutCBReq.setOutStand(convertDestinationToPoint(appTasks.getFirst().getDestination()));
// orderOutCBReq.setMessage("出库完成");
//
// int times = 0;
// MyWmsResponse response = null;
// do {
// response = externalApiService.invokeOrderOutCB(orderOutCBReq);
// times++;
// Thread.sleep(15000L * times);
// } while (!response.getMessage().equals("success") && times <= 10);
// if (response.getMessage().equals("error")) {
// log.error("[WMS]上报失败");
// } else {
// log.info("[WMS]上报成功");
// }
// }
// for (TAppOrderOut orderOut : orderOuts) {
// OrderOutCBReq orderOutCBReq = new OrderOutCBReq();
// orderOutCBReq.setTaskId(orderOut.getOrderId());
// orderOutCBReq.setVehicleNo(vehicleId);
// orderOutCBReq.setResult(OrderOutCBEnums.COMPLETE.getCode());
// orderOutCBReq.setResultMessage("出库完成");
// int times = 0;
// MesApiResponse response = null;
// do {
// response = externalApiService.invokeOrderOutCB(orderOutCBReq);
// times++;
// Thread.sleep(15000L * times);
// } while (response.getCode() != 0 && times <= 10);
// if (response.getCode() != 0) {
// log.error("[WMS]上报失败");
// } else {
// log.info("[WMS]上报成功");
// }
// }
}
}
}

View File

@ -14,4 +14,8 @@ public interface IMyWmsControllerService {
MyWmsResponse<Object> orderOut(OrderOutReq request);
MyWmsResponse<List<StockRespGoodsDetail>> stock(StockReq request);
MyWmsResponse<Boolean> queryCanFeed();
MyWmsResponse<Boolean> queryCanOut();
}

View File

@ -1,23 +1,32 @@
package com.wms_main.service.controller.serviceImpl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wms_main.constant.enums.wms.OrderStatusEnum;
import com.wms_main.constant.enums.wms.StorageTypeEnums;
import com.wms_main.constant.enums.wms.WmsDepthStrategyEnums;
import com.wms_main.constant.enums.wms.WmsTaskTypeEnums;
import com.wms_main.constant.enums.wms.WmsStockStatusEnums;
import com.wms_main.dao.ITAppAgvLockService;
import com.wms_main.dao.ITAppLocationService;
import com.wms_main.dao.ITAppOrderInService;
import com.wms_main.dao.ITAppOrderOutService;
import com.wms_main.dao.ITAppStockService;
import com.wms_main.model.bo.wms.DepthStrategyResult;
import com.wms_main.model.dto.request.mywms.*;
import com.wms_main.model.dto.request.wcs.WcsCanFeedRequest;
import com.wms_main.model.dto.response.mywms.MyWmsResponse;
import com.wms_main.model.dto.response.wcs.WcsApiResponse;
import com.wms_main.model.dto.response.wcs.WcsCanFeedResponse;
import com.wms_main.model.po.TAppLocation;
import com.wms_main.model.po.TAppOrderIn;
import com.wms_main.model.po.TAppOrderOut;
import com.wms_main.model.po.TAppStock;
import com.wms_main.repository.utils.StringUtils;
import com.wms_main.repository.utils.UUIDUtils;
import com.wms_main.service.api.IWcsApiService;
import com.wms_main.service.business.IAgvLockService;
import com.wms_main.service.business.IDepthStrategyService;
import com.wms_main.service.business.IStackerTaskService;
import com.wms_main.service.controller.IMyWmsControllerService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -35,8 +44,14 @@ public class MyWmsControllerServiceImpl implements IMyWmsControllerService {
private final ITAppOrderOutService appOrderOutService;
private final ITAppStockService appStockService;
private final IDepthStrategyService depthStrategyService;
private final ITAppLocationService appLocationService;
private final IStackerTaskService stackerTaskService;
private final IAgvLockService agvLockService;
private final IWcsApiService wcsApiService;
private final ObjectMapper objectMapper = new ObjectMapper();
private static final String L_TEMP_FLAG = "temp";
@Override
public MyWmsResponse<Object> orderIn(OrderInReq request) {
try {
@ -44,9 +59,6 @@ public class MyWmsControllerServiceImpl implements IMyWmsControllerService {
if (!request.isValid()) {
return MyWmsResponse.error("参数错误", null);
}
if (!Objects.equals(request.getStorageId(), StorageTypeEnums.BOX.getCode())) {
return MyWmsResponse.error("仓库号错误", null);
}
// 校验是否存在相同的入库单
List<TAppOrderIn> withTaskId = appOrderInService.getWithTaskId(request.getTaskId());
if (withTaskId == null) {
@ -59,8 +71,8 @@ public class MyWmsControllerServiceImpl implements IMyWmsControllerService {
orderIn.setRecordId(UUIDUtils.getNewUUID());
orderIn.setOrderId(request.getTaskId());
orderIn.setVehicleNo(request.getVehicleNo());
orderIn.setInStand(request.getStandId());
orderIn.setRequestUser(request.getRequestUser());
orderIn.setInStand("R1");
orderIn.setRequestUser("wms");
orderIn.setOrderStatus(OrderStatusEnum.CREATE.getCode());
orderIn.setCreateTime(LocalDateTime.now());
orderIn.setUpdateTime(LocalDateTime.now());
@ -76,13 +88,10 @@ public class MyWmsControllerServiceImpl implements IMyWmsControllerService {
@Override
public MyWmsResponse<Object> orderOut(OrderOutReq request) {
try {
if (request == null) return MyWmsResponse.error("参数错误", null);
if (StringUtils.isEmpty(request.getTaskId())
|| request.getDetails() == null
|| request.getStorageId() == null
|| !Objects.equals(request.getTaskType(), WmsTaskTypeEnums.OUT.getCode())) {
if (request == null || StringUtils.isEmpty(request.getTaskId()) || StringUtils.isEmpty(request.getVehicleNo())) {
return MyWmsResponse.error("参数错误", null);
}
List<TAppOrderOut> orderOutCheckList = appOrderOutService.getWithTaskId(request.getTaskId());
if (orderOutCheckList == null) {
return MyWmsResponse.error("数据服务异常,请稍后重试", null);
@ -90,29 +99,39 @@ public class MyWmsControllerServiceImpl implements IMyWmsControllerService {
if (!orderOutCheckList.isEmpty()) {
return MyWmsResponse.error("出库单已存在,请勿重复推送", null);
}
List<TAppOrderOut> orderOutList = new ArrayList<>();
for (OrderOutGoodsDetail orderOutGoodsDetail : request.getDetails()) {
TAppOrderOut orderOut = new TAppOrderOut();
orderOut.setRecordId(UUIDUtils.getNewUUID());
orderOut.setOrderId(request.getTaskId());
orderOut.setInTaskId(orderOutGoodsDetail.getInTaskId());
orderOut.setOrderStatus(OrderStatusEnum.CREATE.getCode());
orderOut.setCreateTime(LocalDateTime.now());
orderOut.setUpdateTime(LocalDateTime.now());
DepthStrategyResult depthStrategy = depthStrategyService.analyzeDepthStrategy(orderOut);
orderOut.setDepthStrategy(depthStrategy.getStrategy().getCode());
orderOut.setDepthStrategyDetail(objectMapper.writeValueAsString(depthStrategy));
if (orderOut.getDepthStrategy().equals(WmsDepthStrategyEnums.NO_ACTION.getCode())) {
return MyWmsResponse.error("载具出库任务添加失败:" + orderOut.getInTaskId(), null);
}
orderOutList.add(orderOut);
List<TAppStock> vehicleStocks = appStockService.list(
new LambdaQueryWrapper<TAppStock>()
.eq(TAppStock::getVehicleId, request.getVehicleNo())
.eq(TAppStock::getStockStatus, WmsStockStatusEnums.OK.getCode())
);
if (vehicleStocks == null || vehicleStocks.isEmpty()) {
return MyWmsResponse.error("载具号" + request.getVehicleNo() + "无库存记录", null);
}
if (!appOrderOutService.saveBatch(orderOutList)) {
TAppOrderOut orderOut = new TAppOrderOut();
orderOut.setRecordId(UUIDUtils.getNewUUID());
orderOut.setOrderId(request.getTaskId());
orderOut.setVehicleNo(request.getVehicleNo());
orderOut.setOrderStatus(OrderStatusEnum.CREATE.getCode());
orderOut.setCreateTime(LocalDateTime.now());
orderOut.setUpdateTime(LocalDateTime.now());
DepthStrategyResult depthStrategy = depthStrategyService.analyzeDepthStrategyByVehicle(request.getVehicleNo());
orderOut.setDepthStrategy(depthStrategy.getStrategy().getCode());
orderOut.setDepthStrategyDetail(objectMapper.writeValueAsString(depthStrategy));
if (orderOut.getDepthStrategy().equals(WmsDepthStrategyEnums.NO_ACTION.getCode())) {
return MyWmsResponse.error("载具出库任务添加失败:" + request.getVehicleNo(), null);
}
if (!appOrderOutService.save(orderOut)) {
return MyWmsResponse.error("出库单保存失败,请稍后再试", null);
}
return MyWmsResponse.success();
} catch (Exception e) {
log.error("出库任务添加失败", e);
return MyWmsResponse.error("出库任务添加失败,请稍后再试", null);
}
}
@ -130,7 +149,6 @@ public class MyWmsControllerServiceImpl implements IMyWmsControllerService {
new LambdaQueryWrapper<TAppStock>()
.eq(StringUtils.isNotEmpty(stockReqGoodsDetail.getVehicleNo()), TAppStock::getVehicleId, stockReqGoodsDetail.getVehicleNo())
.eq(StringUtils.isNotEmpty(stockReqGoodsDetail.getLocationId()), TAppStock::getLocationId, stockReqGoodsDetail.getLocationId())
.eq(StringUtils.isNotEmpty(stockReqGoodsDetail.getInTaskId()), TAppStock::getInTaskId, stockReqGoodsDetail.getInTaskId())
);
if (stockList != null && !stockList.isEmpty()) {
for (TAppStock stock : stockList) {
@ -156,4 +174,39 @@ public class MyWmsControllerServiceImpl implements IMyWmsControllerService {
}
return MyWmsResponse.success(respList);
}
@Override
public MyWmsResponse<Boolean> queryCanFeed() {
TAppLocation location = stackerTaskService.requestOneLocation(null, L_TEMP_FLAG);
if (location != null && StringUtils.isNotEmpty(location.getLocationId())) {
appLocationService.update(new LambdaUpdateWrapper<TAppLocation>()
.eq(TAppLocation::getLocationId, location.getLocationId())
.set(TAppLocation::getIsOccupy, 0)
.set(TAppLocation::getVehicleId, ""));
return MyWmsResponse.success(true);
}
return MyWmsResponse.error("无剩余可用库位", false);
}
@Override
public MyWmsResponse<Boolean> queryCanOut() {
try {
WcsApiResponse<WcsCanFeedResponse> wcsResponse = wcsApiService.canFeed(new WcsCanFeedRequest("R1"));
if (wcsResponse != null && wcsResponse.getData() != null) {
Boolean wcsAllow = wcsResponse.getData().isAllowAction();
Boolean wmsAllow = agvLockService.canFeedToInboundPort("R1");
if (wcsAllow && wmsAllow) {
return MyWmsResponse.success(true);
} else {
return MyWmsResponse.error("出库口锁定中", null);
}
} else {
return MyWmsResponse.error("WCS系统查询失败", null);
}
} catch (Exception e) {
return MyWmsResponse.error("系统异常,请稍后重试", null);
}
}
}

View File

@ -18,6 +18,7 @@ import com.wms_main.model.po.*;
import com.wms_main.repository.utils.ConvertUtils;
import com.wms_main.repository.utils.StringUtils;
import com.wms_main.repository.utils.UUIDUtils;
import com.wms_main.service.business.IAgvLockService;
import com.wms_main.service.business.IStackerTaskService;
import com.wms_main.service.controller.ITaskControllerService;
import lombok.RequiredArgsConstructor;
@ -41,11 +42,18 @@ public class TaskControllerServiceImpl implements ITaskControllerService {
private final IStackerTaskService stackerTaskService;// 堆垛机任务服务
private final ITAppOrderInService orderInService;
private final ITAppWcsTaskBakService appWcsTaskBakService;
private final IAgvLockService agvLockService;// AGV互锁服务
@Override
@Transactional
public WcsVehicleInResponse requireInTask(WcsVehicleInRequest request) {
String vehicleNo = request.getVehicleNo();
String origin = request.getOrigin();
String inboundPort = "R1"; // 默认入库口
log.info("收到入库请求 - 载具号: {}, 来源: {}, 入库口: {}",
vehicleNo, origin, inboundPort);
TAppOrderIn orderIn = orderInService.getOne(
new LambdaQueryWrapper<TAppOrderIn>()
.eq(StringUtils.isNotEmpty(vehicleNo), TAppOrderIn::getVehicleNo, vehicleNo)
@ -55,6 +63,12 @@ public class TaskControllerServiceImpl implements ITaskControllerService {
return WcsVehicleInResponse.error(String.format("条码:%s 不存在待入库的订单", vehicleNo));
}
// 检查入库口是否可用不关心具体是哪台AGV
boolean canFeed = agvLockService.canFeedToInboundPort(inboundPort);
if (!canFeed) {
return WcsVehicleInResponse.error("入库口被占用,请等待其他任务完成后再试");
}
/* 查找可用库位 */
TAppLocation useLocation = stackerTaskService.requestOneLocation(null, vehicleNo);
if (useLocation == null) {
@ -93,6 +107,7 @@ public class TaskControllerServiceImpl implements ITaskControllerService {
.set(TAppOrderIn::getOrderStatus, OrderStatusEnum.RUNNING.getCode())
.set(TAppOrderIn::getUpdateTime, LocalDateTime.now())
);
agvLockService.lockInboundPort("AGV", "");
WcsVehicleInResponse success = new WcsVehicleInResponse();
success.setCode("200");
@ -144,6 +159,19 @@ public class TaskControllerServiceImpl implements ITaskControllerService {
.eq(TAppOrderIn::getOrderId, wmsTask.getTaskGroup())
.set(TAppOrderIn::getOrderStatus, OrderStatusEnum.COMPLETE.getCode())
.set(TAppOrderIn::getCompleteTime, LocalDateTime.now()));
// 如果是入库任务完成释放入库口锁定
if (wmsTask != null && WmsTaskTypeEnums.IN.getCode().equals(wmsTask.getTaskType())) {
try {
String inboundPort = "R1"; // 默认入库口
// 释放该入库口的所有活动锁定不关心具体是哪台AGV
int releasedCount = agvLockService.releaseAllInboundPortLocks(inboundPort);
log.info("入库任务完成,释放入库口 {} 的锁定,共释放 {} 个锁定", inboundPort, releasedCount);
} catch (Exception e) {
log.error("释放入库口锁定时发生异常", e);
}
}
// 移除wcs任务并向wcs备份表添加记录
TAppWcsTaskBak wcsTaskBak = new TAppWcsTaskBak(
wcsTask.getWcsTaskId(),

View File

@ -63,10 +63,10 @@ public class MyOutExecutor implements Job {
*/
private void processSingleOrder(TAppOrderOut order) {
try {
// 1. 根据inTaskId获取关联库存
TAppStock stock = getStockByInTaskId(order.getInTaskId());
// 1. 根据载具号获取关联库存
TAppStock stock = getStockByVehicleNo(order.getVehicleNo());
if (stock == null) {
log.error("未找到inTaskId关联的库存: {}", order.getInTaskId());
log.error("未找到载具号关联的库存: {}", order.getVehicleNo());
return;
}
@ -107,12 +107,12 @@ public class MyOutExecutor implements Job {
}
/**
* 根据inTaskId获取唯一库存
* 根据载具号获取唯一库存
*/
private TAppStock getStockByInTaskId(String inTaskId) {
private TAppStock getStockByVehicleNo(String vehicleNo) {
return appStockService.getOne(
new LambdaQueryWrapper<TAppStock>()
.eq(TAppStock::getInTaskId, inTaskId)
.eq(TAppStock::getVehicleId, vehicleNo)
.eq(TAppStock::getStockStatus, WmsStockStatusEnums.OK.getCode())
);
}

View File

@ -0,0 +1,209 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## 项目概述
这是宝应梦阳WMS前端项目BOX料箱库版本基于Vue 3和Element Plus开发的现代化Web管理界面用于仓库管理系统的操作和监控。
## 开发环境设置
### 基本命令
```bash
# 进入项目目录
cd 202504-Wms-MengYang-box/wms_web_mengyang
# 安装依赖
npm install
# 启动开发服务器
npm run serve
# 构建生产版本
npm run build
# 代码检查和修复
npm run lint
```
### 开发配置
- **开发端口**12306
- **路由模式**Hash路由 (`createWebHashHistory`)
- **默认路由**:登录页面 (`/`) -> 主页 (`/home`) -> 库存页面 (`/stock`)
- **代理配置**Microsoft OAuth认证代理
## 技术栈
### 核心框架
- **Vue 3.2.13** - 前端框架Composition API
- **Vue Router 4.0.3** - 路由管理
- **Vuex 4.0.0** - 状态管理
- **Element Plus 2.4.0** - UI组件库
### 主要依赖
- **Axios 1.3.3** - HTTP请求库
- **Moment 2.29.4** - 日期处理
- **XLSX 0.18.5** - Excel文件处理
- **QRCode.vue 3.4.1** - 二维码生成
- **Vue3-print-nb 0.1.4** - 打印功能
- **File-saver 2.0.5** - 文件下载
### 开发工具
- **Sass 1.83.4** - CSS预处理器
- **Vue CLI 5.0** - 构建工具
- **ESLint** - 代码质量检查(已关闭未使用变量警告)
## 项目架构
### 目录结构
```
src/
├── api/ # API接口封装
├── assets/ # 静态资源
├── components/ # 可复用组件
├── constant/ # 常量配置
├── excel/ # Excel上传组件
├── http/ # HTTP请求配置
├── layout/ # 业务页面组件
├── print/ # 打印相关组件
├── router/ # 路由配置
├── store/ # Vuex状态管理
├── styles/ # 全局样式
├── utils/ # 工具函数
└── views/ # 主要视图组件
```
### 核心业务模块
#### 1. 仓库作业 (`layout/`)
**库存管理**
- `stock.vue` - 库存查询和管理
- `inventory.vue` - 库存盘点
- `inventoryRecord.vue` - 盘点记录
**入出库作业**
- `goodsIn.vue` - 入库作业界面
- `goodsOut.vue` - 出库作业界面
- `orderIn.vue` - 入库单管理
- `orderOut.vue` - 出库单管理
**任务监控**
- `taskMonitor.vue` - 任务执行监控
- `pickTaskMonitor.vue` - 拣选任务监控
- `inTaskRecord.vue` - 入库任务记录
- `outTaskRecord.vue` - 出库任务记录
#### 2. 基础数据管理
- `goods.vue` - 物料信息管理
- `locationsTable.vue` - 库位信息管理
- `standSettings.vue` - 站台(库口)设置
- `vehicle.vue` - 料箱监控
#### 3. 配料系统
- `kitting.vue` - 配料作业
- `kittingList.vue` - 配料单管理
- `kittingRelation.vue` - 配料关系配置
- `clcKanban.vue` - 需求看板
#### 4. 系统管理
- `wmsConfigNew.vue` - 系统配置
- `role_user.vue` - 用户角色管理
- `role_permission.vue` - 权限管理
- `wmsLog.vue` - 系统日志
### API接口模块 (`api/`)
**核心业务接口**
- `goods.js` - 物料信息API
- `stock.js` - 库存相关API
- `task.js` - 任务管理API
- `location.js` - 库位管理API
**作业流程接口**
- `orderIn.js` - 入库订单API
- `orderOut.js` - 出库订单API
- `kateWork.js` - 配料作业API
**系统功能接口**
- `login.js` - 登录认证API
- `user.js` - 用户管理API
- `config.js` - 系统配置API
- `excel.js` - Excel处理API
### 公共组件 (`components/`)
- `sideMenu.vue` - 侧边菜单导航
- `appTag.vue` - 标签页管理
### 工具模块 (`utils/`)
- `dateUtils.js` - 日期处理工具
- `formatter.js` - 数据格式化
- `hashUtils.js` - 哈希计算
- `stringUtils.js` - 字符串处理
- `loading.js` - 加载状态管理
- `myMessageBox.js` - 消息提示封装
## 开发注意事项
### 路由配置
- 使用Hash路由模式兼容性更好
- 实现了路由守卫,未登录用户自动跳转登录页
- 登录状态通过`sessionStorage`中的`user`字段判断
### 状态管理
- 使用Vuex进行全局状态管理
- 主要管理用户信息、菜单权限、标签页状态
### HTTP请求
- 统一使用Axios进行API调用
- 请求封装在`/http/request.js`中
- API接口按业务模块分类组织
### Excel处理
- 支持Excel文件上传和解析
- 专门的Excel上传组件位于`excel/`目录
- 支持多种业务数据的Excel导入导出
### 打印功能
- 使用`vue3-print-nb`实现打印功能
- 打印组件位于`print/`目录
### 样式规范
- 使用Sass预处理器
- 全局样式文件:`styles/index.scss`
- 响应式设计,支持不同屏幕尺寸
## 常见开发任务
### 添加新页面
1. 在`layout/`目录创建Vue组件
2. 在`router/index.js`中配置路由
3. 如需API调用在`api/`目录创建对应接口文件
4. 更新侧边菜单配置
### Excel上传功能
1. 参考`excel/`目录下的现有组件
2. 使用Element Plus的上传组件
3. 后端API处理Excel数据解析
### 新增API接口
1. 在`api/`目录按模块创建文件
2. 使用`@/http/request`进行请求封装
3. 导出接口函数供组件使用
### 调试和测试
- 使用Vue DevTools检查组件状态
- 浏览器开发者工具调试网络请求
- ESLint检查代码质量
- 在不同浏览器中测试兼容性
## 后端API集成
### 默认后端地址
根据当前配置后端API地址需要在`http/request.js`中配置,通常为:
- 开发环境:`http://localhost:12315`
- 生产环境:`http://10.18.58.21:12315`
### 认证机制
- 支持Microsoft OAuth认证代理
- 登录状态存储在`sessionStorage`中
- 路由守卫自动验证用户登录状态

View File

@ -4,12 +4,23 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## 项目概述
这是一个宝应梦阳WMS仓库管理系统后端项目基于Spring Boot 3.3.5和Java 21开发使用Maven构建。系统管理仓库的入库、出库、库存和任务调度等核心业务。
这是宝应梦阳WMS仓库管理系统后端项目TP托盘库版本基于Spring Boot 3.3.5和Java 21开发使用Maven构建。专门针对托盘库存储场景管理仓库的入库、出库、库存和任务调度等核心业务。
## 项目特点
**TP托盘库版本特性**
- 专为托盘存储优化的算法策略
- 支持大型货物的托盘化存储管理
- 与BOX料箱库版本在业务逻辑上有所差异
- 数据库schema针对托盘库场景设计
## 开发环境设置
### 构建和运行命令
### 基本构建命令
```bash
# 进入项目目录
cd 202504-Wms-MengYang-tp/wms_serve_mengyang
# 编译项目
mvn clean compile
@ -19,19 +30,30 @@ mvn clean package
# 运行项目
mvn spring-boot:run
# 跳过测试运行(默认配置)
# 使用Maven Wrapper
./mvnw spring-boot:run
# 跳过测试运行(项目配置默认跳过测试)
mvn clean package -Dmaven.test.skip=true
```
### 数据库配置
- 本地开发MySQL `localhost:3306/wms_mengyang_box`
- 生产环境MySQL `10.18.58.21:3306/wms_yachi_nantong`
- 用户名/密码本地使用root/root生产使用user/user
### 运行环境配置
### 应用配置
**数据库配置**
- 本地开发MySQL `localhost:3306/wms_mengyang_tp`注意使用tp数据库
- 生产环境MySQL `10.18.58.21:3306/wms_yachi_nantong`
- 认证信息本地使用root/root生产使用user/user
**应用配置**
- 服务端口12315
- 应用名称wms_main
- 文件上传限制单文件100MB总计1000MB
- 当前配置:`spring.profiles.active=online`
**Maven特殊配置**
- 项目默认跳过单元测试:`maven-surefire-plugin.skip=true`
- 最终构建文件名:`wms-box-server`注意名称还是box-server但实际是tp版本
- Java版本21 (source和target都是21)
## 核心架构
@ -50,50 +72,134 @@ mvn clean package -Dmaven.test.skip=true
- `POST /stock` - 库存查询接口
#### 2. 深度策略服务 (`service/business/DepthStrategyService`)
核心算法模块,实现智能仓储策略:
- 支持托盘库(TP)和料箱库(BOX)两种存储类型
- 动态深度分析和出库优化策略
核心算法模块,针对托盘库优化:
- **托盘库(TP)专属存储策略**
- 大型货物的存储位置优化算法
- 托盘堆叠和存取路径优化
#### 3. 任务调度系统 (`service/quartz_job/`)
基于Quartz的定时任务
基于Quartz的定时任务框架
- `WmsTaskExecutor` - WMS任务执行器
- `MyOutExecutor` - 出库任务执行器
- `WcsStackerTaskSender` - WCS任务发送器
- `WcsStackerTaskSender` - WCS堆垛机任务发送器
#### 4. 设备集成 (`service/api/`)
- `WcsApiService` - WCS系统集成
- `WcsApiService` - WCS系统集成(托盘库设备)
- `ExternalApiService` - 外部系统API调用
### 数据模型结构
- `po/` - 数据库实体对象(Persistent Object
- `po/` - 数据库实体对象(针对托盘库schema
- `dto/request/` - 请求数据传输对象
- `dto/response/` - 响应数据传输对象
- `bo/` - 业务对象Business Object
- `vo/` - 视图对象View Object
### 常用枚举类 (`constant/enums/`)
- `StorageTypeEnums` - 存储类型托盘库TP/料箱库BOX
- `WmsDepthStrategyEnums` - 深度策略枚举
**WMS业务枚举** (`enums/wms/`)
- `StorageTypeEnums` - 存储类型重点关注TP托盘库类型
- `WmsDepthStrategyEnums` - 深度策略枚举(托盘库专用策略)
- `WmsTaskTypeEnums` - 任务类型枚举
- `OrderStatusEnum` - 订单状态枚举
- `WmsLocationTypeEnums` - 货位类型枚举(托盘货位)
- `WmsVehicleStatusEnums` - 载具状态枚举(托盘载具)
**WCS集成枚举** (`enums/wcs/`)
- `WcsApiResponseCodeEnums` - WCS API响应码
- `WcsStackerTaskTypeEnums` - 堆垛机任务类型(托盘堆垛机)
- `WcsStackerTaskStatusEnums` - 堆垛机任务状态
## 技术栈
### 核心依赖
- Spring Boot Web 3.3.5 - REST API框架
- Spring Boot Quartz - 定时任务调度
- MyBatis-Plus 3.5.7 - ORM框架
- MySQL Connector - 数据库连接
- Lombok - 代码简化
- **Spring Boot Web 3.3.5** - REST API框架
- **Spring Boot Quartz** - 定时任务调度
- **MyBatis-Plus 3.5.7** - ORM框架
- **MySQL Connector** - 数据库连接
- **Lombok** - 代码简化
### 工具库
- Hutool 5.8.33 - Java工具类库
- FastJSON 2.0.21 - JSON处理
- EasyExcel 4.0.3 & EasyPOI 4.5.0 - Excel处理
- Apache HttpClient 4.5.13 - HTTP客户端
- Google Guava 33.3.1 - 集合工具
- **Hutool 5.8.33** - Java工具类库
- **FastJSON 2.0.21** - JSON处理
- **EasyExcel 4.0.3 & EasyPOI 4.5.0** - Excel处理
- **Apache HttpClient 4.5.13** - HTTP客户端
- **Google Guava 33.3.1** - 集合工具
## 开发注意事项
## 重要的文件路径
- **主要工作目录**`202504-Wms-MengYang-tp/wms_serve_mengyang/`
- **日志存储**`wms_log/` (按日期和级别分类)
- **数据库脚本**`db/wms_mengyang_tp.sql`
- **MyBatis映射**`src/main/resources/mapper/`
## 常见开发任务
### 启动应用
```bash
cd 202504-Wms-MengYang-tp/wms_serve_mengyang
mvn spring-boot:run
```
### 查看日志
```bash
# 查看今天的info日志
tail -f wms_log/info/$(date +%Y-%m-%d)/$(date +%Y-%m-%d).0.log
# 查看今天的error日志
tail -f wms_log/error/$(date +%Y-%m-%d)/$(date +%Y-%m-%d).0.log
```
### 数据库操作
- 使用MyBatis-Plus进行ORM操作
- 支持批量操作和事务处理
- 驼峰命名自动映射(`map-underscore-to-camel-case: true`)
- ID生成策略`assign_id`
## 系统集成要点
### WCS系统集成托盘库专用
- TCP和HTTP双协议支持
- **托盘堆垛机**任务调度和状态同步
- **托盘载具**管理和追踪
### 任务调度系统
- 基于Quartz的定时任务框架
- 关键执行器针对托盘库场景优化
- 任务状态实时监控
### Excel处理
- 后端EasyExcel 4.0.3 + EasyPOI 4.5.0
- 支持托盘库相关的业务数据导入导出
- 模板位置:`excel/easypoi/excelTemplate/`
## TP与BOX版本的差异
### 主要区别
1. **数据库schema**:使用`wms_mengyang_tp`数据库
2. **存储策略算法**:针对托盘存储特点优化
3. **设备集成**:对接托盘库专用设备
4. **业务规则**:托盘的尺寸、重量、堆叠规则不同
### 开发注意事项
- 在开发新功能时注意区分TP和BOX版本的业务差异
- 深度策略算法需要考虑托盘的物理特性
- WCS集成需要适配托盘库的设备类型
## 故障排除
### 常见问题
1. **端口冲突**确认12315端口未被占用
2. **数据库连接**检查MySQL服务状态和TP数据库配置
3. **版本混淆**确认操作的是TP版本而非BOX版本
4. **日志分析**:按级别查看对应日志目录
### 调试技巧
- 使用`RequestLogFilter`查看API请求日志
- 通过`WmsControllerExceptionHandler`统一异常处理
- 检查定时任务执行状态:查看`TAppJobs`表
- 注意区分TP和BOX版本的业务逻辑差异
## 开发规范
### 代码规范
- 使用Lombok注解减少样板代码
@ -101,43 +207,10 @@ mvn clean package -Dmaven.test.skip=true
- 枚举类管理常量值
- 接口-实现分离的服务层设计
### 数据库操作
- 使用MyBatis-Plus进行ORM操作
- 支持批量操作和事务处理
- 驼峰命名自动映射
- ID生成策略assign_id
### 错误处理
- 统一异常处理器:`WmsControllerExceptionHandler`
- 标准化错误码:`WmsApiResponseCodeEnums`
- 请求日志过滤器:`RequestLogFilter`
### Excel处理
系统支持完整的Excel导入导出功能
- 货物信息、产品信息、库存信息等业务数据
- 使用EasyExcel和EasyPOI双重支持
- 模板位置:`excel/easypoi/excelTemplate/`
## 外部系统集成
### WCS系统集成
- 堆垛机任务调度
- 载具管理(托盘和料箱)
- TCP/HTTP通信协议
### MES系统对接
- 制造执行系统数据交换
- 生产任务状态同步
## 测试说明
项目配置中跳过了单元测试执行(`maven-surefire-plugin.skip=true`),在开发新功能时建议:
### 测试建议
项目配置跳过单元测试,建议:
1. 先实现功能代码
2. 使用Postman等工具进行API测试
3. 检查数据库数据状态
3. 检查托盘库相关的数据库数据状态
4. 验证定时任务执行情况
## Claude交互指南
### AI助手通用记录
- 说中文think hard
5. 测试WCS设备集成功能

View File

@ -0,0 +1,66 @@
### MyWMS接口测试
### 基础配置
@baseUrl = http://localhost:12315
### 1. 入库订单接口
POST {{baseUrl}}/mywms/orderIn
Content-Type: application/json
{
"taskId": "testOrderId1",
"vehicleNo": "1001"
}
### 2. 出库订单接口
POST {{baseUrl}}/mywms/orderOut
Content-Type: application/json
{
"taskId": "testOrderId1",
"vehicleNo": "1001"
}
### 3. 库存查询接口 - 仅按载具查询
POST {{baseUrl}}/mywms/stock
Content-Type: application/json
{
"requestId": "testOrderId1",
"details": [
{
"vehicleNo": "1001"
}
]
}
### 4. 库存查询接口 - 仅按库位查询
POST {{baseUrl}}/mywms/stock
Content-Type: application/json
{
"requestId": "testOrderId2",
"details": [
{
"locationId": "A01-01-01-02"
}
]
}
### 5. 检查是否允许投料
GET {{baseUrl}}/mywms/allowFeed
### 测试数据说明
# 1. orderIn: 入库订单接口需要taskId和vehicleNo
# 2. orderOut: 出库订单接口需要taskId和vehicleNo
# 3. stock: 库存查询接口需要requestId和details数组
# - details中可以包含vehicleNo和/或locationId
# 4. allowFeed: 查询当前系统是否允许投料操作GET请求
### 响应格式说明
# 所有接口都返回MyWmsResponse格式:
# {
# "code": 200, // 响应码
# "message": "操作成功", // 响应消息
# "data": {}, // 具体数据
# "success": true // 是否成功
# }

View File

@ -0,0 +1,60 @@
### TaskController接口测试 - WMS任务控制接口
### 基础配置
@baseUrl = http://localhost:12315
### 1. WCS请求载具入库
POST {{baseUrl}}/wms/task/wcsRequestVehicleIn
Content-Type: application/json
{
"origin": "R1",
"vehicleNo": "1001",
"codeMessage": "test",
"remark": "载具入库测试"
}
### 2. 发送任务结果 - 任务完成
POST {{baseUrl}}/wms/task/sendTaskResult
Content-Type: application/json
{
"taskId": "TASK202507240001",
"taskStatus": 100,
"vehicleNo": "1001",
"destination": "A01-01-01-02",
"message": "任务执行成功"
}
### 测试数据说明
# 1. wcsRequestVehicleIn: WCS请求载具入库
# - origin: 点位(必填)
# - vehicleNo: 载具信息(必填)
# - codeMessage: 条码信息(可选)
# - remark: 备注(可选)
# 2. sendTaskResult: WCS反馈任务执行结果
# - taskId: 任务ID必填
# - taskStatus: 任务状态(必填)
# * 0: 等待执行
# * 1: 执行中
# * 2: 执行完成
# * 3: 执行失败
# - vehicleNo: 载具号(必填)
# - destination: 终点(可选)
# - message: 信息(可选)
### 响应格式说明
# wcsRequestVehicleIn返回WcsVehicleInResponse:
# {
# "success": true,
# "code": "SUCCESS",
# "message": "请求成功",
# "data": {}
# }
# sendTaskResult返回BaseWcsApiResponse:
# {
# "success": true,
# "code": "SUCCESS",
# "message": "任务结果接收成功"
# }

View File

@ -9,10 +9,10 @@
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com</groupId>
<artifactId>dev_wms_serve</artifactId>
<artifactId>wms_serve_mengyang_tp</artifactId>
<version>3.2</version>
<name>dev_wms_serve</name>
<description>dev_wms_serve</description>
<name>wms_serve_mengyang_tp</name>
<description>wms_serve_mengyang_tp</description>
<url/>
<licenses>
<license/>
@ -162,6 +162,15 @@
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>21</source>
<target>21</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>

View File

@ -10,7 +10,6 @@ import com.wms_main.dao.*;
import com.wms_main.model.dto.request.mywms.OrderInCBReq;
import com.wms_main.model.dto.request.mywms.OrderOutCBReq;
import com.wms_main.model.dto.response.mes.MesApiResponse;
import com.wms_main.model.dto.response.mywms.MyWmsResponse;
import com.wms_main.model.po.*;
import com.wms_main.repository.utils.ConvertUtils;
import com.wms_main.repository.utils.StringUtils;
@ -26,7 +25,6 @@ import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import static com.wms_main.repository.utils.ConvertUtils.convertDestinationToPoint;
/**
* 服务实现
@ -60,11 +58,6 @@ public class StackerTaskServiceImpl implements IStackerTaskService {
*/
private final ITAppTaskBakService appTaskBakService;
private final ITAppWcsTaskBakService appWcsTaskBakService;
/**
* 拣选任务服务
*/
private final ITAppPickTaskService appPickTaskService;
/**
* 设备服务
*/
@ -402,21 +395,21 @@ public class StackerTaskServiceImpl implements IStackerTaskService {
orderInCBReq.setResult(OrderInCBEnums.COMPLETE.getCode());
orderInCBReq.setResultMessage("入库完成");
int times = 0;
MesApiResponse response = null;
do {
Thread.sleep(15000L * times);
response = externalApiService.invokeOrderInCB(orderInCBReq);
times++;
} while (response.getCode() != 0 && times <= 10);
if (response.getCode() != 0) {
log.error("[wms]上报失败");
} else {
log.info("[wms]上报成功");
}
if (!appOrderInService.removeById(orderIn.getRecordId())) {
log.info("整箱入库完成删除入库单失败,任务:{},原因:删除入库单失败", wmsTask.getTaskId());
}
// int times = 0;
// MesApiResponse response = null;
// do {
// Thread.sleep(15000L * times);
// response = externalApiService.invokeOrderInCB(orderInCBReq);
// times++;
// } while (response.getCode() != 0 && times <= 10);
// if (response.getCode() != 0) {
// log.error("[wms]上报失败");
// } else {
// log.info("[wms]上报成功");
// }
// if (!appOrderInService.removeById(orderIn.getRecordId())) {
// log.info("整箱入库完成删除入库单失败,任务:{},原因:删除入库单失败", wmsTask.getTaskId());
// }
TAppVehicle vehicle = new TAppVehicle();
vehicle.setVehicleId(wmsTask.getVehicleId());
vehicle.setVehicleStatus(WmsVehicleStatusEnums.ON.getCode());
@ -534,27 +527,26 @@ public class StackerTaskServiceImpl implements IStackerTaskService {
appOrderOutService.removeBatchByIds(orderOuts);
}
}
for (TAppOrderOut orderOut : orderOuts) {
List<TAppTask> appTasks = vehicleIdToTaskMap.get(vehicleId);
OrderOutCBReq orderOutCBReq = new OrderOutCBReq();
orderOutCBReq.setTaskId(orderOut.getOrderId());
orderOutCBReq.setVehicleNo(vehicleId);
orderOutCBReq.setResult(OrderOutCBEnums.COMPLETE.getCode());
orderOutCBReq.setResultMessage("出库完成");
// for (TAppOrderOut orderOut : orderOuts) {
// OrderOutCBReq orderOutCBReq = new OrderOutCBReq();
// orderOutCBReq.setTaskId(orderOut.getOrderId());
// orderOutCBReq.setVehicleNo(vehicleId);
// orderOutCBReq.setResult(OrderOutCBEnums.COMPLETE.getCode());
// orderOutCBReq.setResultMessage("出库完成");
int times = 0;
MesApiResponse response = null;
do {
response = externalApiService.invokeOrderOutCB(orderOutCBReq);
times++;
Thread.sleep(15000L * times);
} while (response.getCode() != 0 && times <= 10);
if (response.getCode() != 0) {
log.error("[WMS]上报失败");
} else {
log.info("[WMS]上报成功");
}
}
// int times = 0;
// MesApiResponse response = null;
// do {
// response = externalApiService.invokeOrderOutCB(orderOutCBReq);
// times++;
// Thread.sleep(15000L * times);
// } while (response.getCode() != 0 && times <= 10);
// if (response.getCode() != 0) {
// log.error("[WMS]上报失败");
// } else {
// log.info("[WMS]上报成功");
// }
// }
}
}
}

View File

@ -0,0 +1,263 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## 项目概述
这是宝应梦阳WMS前端项目TP托盘库版本基于Vue 3和Element Plus开发的现代化Web管理界面专门用于托盘库存储场景的仓库管理系统操作和监控。
## 项目特点
**TP托盘库版本特性**
- 专为托盘存储场景设计的UI界面
- 支持大型货物的托盘化存储管理操作
- 与BOX料箱库版本在业务功能上有所差异
- 针对托盘库的专用业务流程和界面优化
## 开发环境设置
### 基本命令
```bash
# 进入项目目录
cd 202504-Wms-MengYang-tp/wms_web_mengyang
# 安装依赖
npm install
# 启动开发服务器
npm run serve
# 构建生产版本
npm run build
# 代码检查和修复
npm run lint
```
### 开发配置
- **开发端口**12306
- **路由模式**Hash路由 (`createWebHashHistory`)
- **默认路由**:登录页面 (`/`) -> 主页 (`/home`) -> 库存页面 (`/stock`)
- **代理配置**Microsoft OAuth认证代理
### 与BOX版本的区别
- 项目结构和技术栈基本相同
- **主要差异在业务逻辑**:托盘存储相关的界面和交互
- **后端API对接**调用TP版本后端的专用接口
- **数据展示**:针对托盘库的数据字段和格式
## 技术栈
### 核心框架
- **Vue 3.2.13** - 前端框架Composition API
- **Vue Router 4.0.3** - 路由管理
- **Vuex 4.0.0** - 状态管理
- **Element Plus 2.4.0** - UI组件库
### 主要依赖
- **Axios 1.3.3** - HTTP请求库
- **Moment 2.29.4** - 日期处理
- **XLSX 0.18.5** - Excel文件处理
- **QRCode.vue 3.4.1** - 二维码生成
- **Vue3-print-nb 0.1.4** - 打印功能
- **File-saver 2.0.5** - 文件下载
### 开发工具
- **Sass 1.83.4** - CSS预处理器
- **Vue CLI 5.0** - 构建工具
- **ESLint** - 代码质量检查(已关闭未使用变量警告)
## 项目架构
### 目录结构
```
src/
├── api/ # API接口封装托盘库专用接口
├── assets/ # 静态资源
├── components/ # 可复用组件
├── constant/ # 常量配置(托盘库相关常量)
├── excel/ # Excel上传组件托盘库数据
├── http/ # HTTP请求配置
├── layout/ # 业务页面组件(托盘库业务界面)
├── print/ # 打印相关组件
├── router/ # 路由配置
├── store/ # Vuex状态管理
├── styles/ # 全局样式
├── utils/ # 工具函数
└── views/ # 主要视图组件
```
### 核心业务模块(托盘库专用)
#### 1. 托盘仓库作业 (`layout/`)
**库存管理**
- `stock.vue` - 托盘库存查询和管理
- `inventory.vue` - 托盘库存盘点
- `inventoryRecord.vue` - 盘点记录
**入出库作业**
- `goodsIn.vue` - 托盘入库作业界面
- `goodsOut.vue` - 托盘出库作业界面
- `orderIn.vue` - 托盘入库单管理
- `orderOut.vue` - 托盘出库单管理
**任务监控**
- `taskMonitor.vue` - 托盘任务执行监控
- `pickTaskMonitor.vue` - 托盘拣选任务监控
- `inTaskRecord.vue` - 入库任务记录
- `outTaskRecord.vue` - 出库任务记录
#### 2. 托盘基础数据管理
- `goods.vue` - 托盘物料信息管理
- `locationsTable.vue` - 托盘库位信息管理
- `standSettings.vue` - 托盘站台(库口)设置
- `vehicle.vue` - 托盘载具监控
#### 3. 托盘配料系统
- `kitting.vue` - 托盘配料作业
- `kittingList.vue` - 托盘配料单管理
- `kittingRelation.vue` - 托盘配料关系配置
- `clcKanban.vue` - 托盘需求看板
#### 4. 系统管理
- `wmsConfigNew.vue` - 托盘库系统配置
- `role_user.vue` - 用户角色管理
- `role_permission.vue` - 权限管理
- `wmsLog.vue` - 系统日志
### API接口模块 (`api/`)
**托盘库核心业务接口**
- `goods.js` - 托盘物料信息API
- `stock.js` - 托盘库存相关API
- `task.js` - 托盘任务管理API
- `location.js` - 托盘库位管理API
**托盘作业流程接口**
- `orderIn.js` - 托盘入库订单API
- `orderOut.js` - 托盘出库订单API
- `kateWork.js` - 托盘配料作业API
**系统功能接口**
- `login.js` - 登录认证API
- `user.js` - 用户管理API
- `config.js` - 托盘库系统配置API
- `excel.js` - 托盘库Excel处理API
### 公共组件 (`components/`)
- `sideMenu.vue` - 侧边菜单导航(托盘库功能菜单)
- `appTag.vue` - 标签页管理
### 工具模块 (`utils/`)
- `dateUtils.js` - 日期处理工具
- `formatter.js` - 数据格式化(托盘库数据格式)
- `hashUtils.js` - 哈希计算
- `stringUtils.js` - 字符串处理
- `loading.js` - 加载状态管理
- `myMessageBox.js` - 消息提示封装
## 托盘库特色功能
### 托盘存储特点
- **大型货物管理**:支持重型和大尺寸货物的界面展示
- **托盘堆叠显示**:可视化托盘堆叠状态和高度限制
- **存取路径优化**:界面显示最优存取路径建议
- **托盘载具追踪**:实时显示托盘在系统中的位置
### 与BOX版本的界面差异
1. **数据字段不同**:托盘尺寸、重量、堆叠层数等专用字段
2. **操作流程优化**:针对托盘存储的特殊操作步骤
3. **监控界面**:托盘堆垛机状态、托盘载具位置等专用监控
4. **报表格式**:托盘库专用的统计和报表格式
## 开发注意事项
### 路由配置
- 使用Hash路由模式兼容性更好
- 实现了路由守卫,未登录用户自动跳转登录页
- 登录状态通过`sessionStorage`中的`user`字段判断
### 状态管理
- 使用Vuex进行全局状态管理
- 主要管理用户信息、菜单权限、标签页状态
- **托盘库专用状态**:托盘载具状态、堆垛机状态等
### HTTP请求
- 统一使用Axios进行API调用
- 请求封装在`/http/request.js`中
- API接口按托盘库业务模块分类组织
- **注意**对接TP版本后端API端口12315
### Excel处理托盘库数据
- 支持托盘库专用Excel模板的上传和解析
- 专门的Excel上传组件位于`excel/`目录
- 支持托盘物料、库存、任务等数据的Excel导入导出
### 打印功能
- 使用`vue3-print-nb`实现打印功能
- 打印组件位于`print/`目录
- **托盘库专用**:托盘标签、任务单等打印格式
### 样式规范
- 使用Sass预处理器
- 全局样式文件:`styles/index.scss`
- 响应式设计,支持不同屏幕尺寸
- **托盘库界面优化**:适配托盘库的数据展示需求
## 常见开发任务
### 添加托盘库新页面
1. 在`layout/`目录创建Vue组件
2. 在`router/index.js`中配置路由
3. 如需API调用在`api/`目录创建对应的托盘库接口文件
4. 更新侧边菜单配置
5. **注意**:考虑托盘库的业务特点和数据格式
### 托盘库Excel上传功能
1. 参考`excel/`目录下的现有组件
2. 使用Element Plus的上传组件
3. 后端TP版本API处理托盘Excel数据解析
4. **关键**使用托盘库专用的Excel模板格式
### 新增托盘库API接口
1. 在`api/`目录按模块创建文件
2. 使用`@/http/request`进行请求封装
3. 导出接口函数供组件使用
4. **重要**确保对接TP版本后端API
### 调试和测试
- 使用Vue DevTools检查组件状态
- 浏览器开发者工具调试网络请求
- ESLint检查代码质量
- 在不同浏览器中测试兼容性
- **托盘库专用测试**:验证托盘数据格式和业务逻辑
## 后端API集成
### 默认后端地址
根据当前配置后端TP版本API地址需要在`http/request.js`中配置:
- 开发环境:`http://localhost:12315`TP版本后端
- 生产环境:`http://10.18.58.21:12315`
### 认证机制
- 支持Microsoft OAuth认证代理
- 登录状态存储在`sessionStorage`中
- 路由守卫自动验证用户登录状态
## 开发建议
### TP与BOX版本协同开发
1. **代码复用**:公共组件和工具函数可以复用
2. **业务差异**:注意托盘库和料箱库的业务逻辑差异
3. **数据格式**:确保使用正确的数据字段和格式
4. **API对接**确认对接的是TP版本后端而非BOX版本
### 性能优化
- 托盘库数据量通常较大,注意列表分页和懒加载
- 图表和监控界面的实时更新优化
- Excel处理时的内存管理
### 用户体验
- 托盘库操作相对复杂,提供清晰的操作指引
- 错误提示和状态反馈要及时准确
- 支持键盘快捷操作,提高操作效率