From c33b088a896efeb9bae0af1424752496c2c4f006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E6=97=AD=E6=98=8E=5C73939?= <739390650@QQ.COM> Date: Fri, 21 Nov 2025 11:07:14 +0800 Subject: [PATCH] =?UTF-8?q?opt:=E8=B6=8A=E5=8D=97=E5=AF=8C=E4=BD=B3?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nl/wms/ext_manage/enums/EXTConstant.java | 15 + .../ext_manage/service/WmsToAcsService.java | 22 + .../service/impl/WmsToAcsServiceImpl.java | 15 + .../org/nl/wms/ext_manage/util/AcsUtil.java | 95 +++ .../controller/BigscreenController.java | 74 ++ .../service/PdaBigScreenService.java | 45 ++ .../service/dto/IvtAnalyse.java | 11 + .../service/impl/PdaBigScreenServiceImpl.java | 762 ++++++++++++++++++ 8 files changed, 1039 insertions(+) create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/wms/pda_manage/bigscreen_manage/controller/BigscreenController.java create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/wms/pda_manage/bigscreen_manage/service/PdaBigScreenService.java create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/wms/pda_manage/bigscreen_manage/service/dto/IvtAnalyse.java create mode 100644 nladmin-system/nlsso-server/src/main/java/org/nl/wms/pda_manage/bigscreen_manage/service/impl/PdaBigScreenServiceImpl.java diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext_manage/enums/EXTConstant.java b/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext_manage/enums/EXTConstant.java index cddbd1a..e989388 100644 --- a/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext_manage/enums/EXTConstant.java +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext_manage/enums/EXTConstant.java @@ -32,6 +32,21 @@ public class EXTConstant { */ public final static String SEND_TASK_ACS_API = "api/wms/task"; + /** + * 下发ACS获取车辆运行状态 + */ + public final static String GET_CAR_STATUS_ACS_API = "api/wms/getCarStatus"; + + /** + * 下发ACS暂停 + */ + public final static String SUSPEND_ACS_API = "api/wms/suspend"; + + /** + * 下发ACS暂停 + */ + public final static String START_ACS_API = "api/wms/start"; + /** * ERP获取物料详情请求地址 */ diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext_manage/service/WmsToAcsService.java b/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext_manage/service/WmsToAcsService.java index 9443acb..2ad0ab3 100644 --- a/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext_manage/service/WmsToAcsService.java +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext_manage/service/WmsToAcsService.java @@ -22,4 +22,26 @@ public interface WmsToAcsService { * @return AcsResponse */ AcsResponse renotifyAcs(List list); + + + /** + * 获取车辆运行状态 + * @return AcsResponse + */ + AcsResponse getCarStatus(); + + /** + * 暂停 + * @param carNo 车辆号 + * @return AcsResponse + */ + AcsResponse suspend(String carNo); + + /** + * 暂停 + * @param carNo 车辆号 + * @return AcsResponse + */ + AcsResponse start(String carNo); + } diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext_manage/service/impl/WmsToAcsServiceImpl.java b/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext_manage/service/impl/WmsToAcsServiceImpl.java index 379f4d3..1e9f468 100644 --- a/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext_manage/service/impl/WmsToAcsServiceImpl.java +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext_manage/service/impl/WmsToAcsServiceImpl.java @@ -26,4 +26,19 @@ public class WmsToAcsServiceImpl implements WmsToAcsService { public AcsResponse renotifyAcs(List list) { return AcsUtil.notifyAcs(EXTConstant.SEND_TASK_ACS_API, list); } + + @Override + public AcsResponse getCarStatus() { + return AcsUtil.getCarStatus(EXTConstant.GET_CAR_STATUS_ACS_API); + } + + @Override + public AcsResponse suspend(String carNo) { + return AcsUtil.suspendAndStart(EXTConstant.SUSPEND_ACS_API,carNo); + } + + @Override + public AcsResponse start(String carNo) { + return AcsUtil.suspendAndStart(EXTConstant.START_ACS_API,carNo); + } } diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext_manage/util/AcsUtil.java b/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext_manage/util/AcsUtil.java index c84d917..8a79603 100644 --- a/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext_manage/util/AcsUtil.java +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext_manage/util/AcsUtil.java @@ -70,4 +70,99 @@ public class AcsUtil { return resultAcs; } + + /** + * 获取车辆运行状态 + * @param api acs地址 + * @return AcsResponse + */ + public static AcsResponse getCarStatus(String api) { + log.info("下发ACS获取车辆运行状态:-------------------"); + // 返回参数 + AcsResponse resultAcs; + // 系统参数类 + SysParamServiceImpl sysParamService = SpringContextHolder.getBean(SysParamServiceImpl.class); + //判断是否连接ACS系统 + Param isConnectAcs = sysParamService.findByCode(SysParamConstant.IS_CONNECT_ACS); + if (ObjectUtil.isEmpty(isConnectAcs)) { + return AcsResponse.requestError("系统参数表中:" + SysParamConstant.IS_CONNECT_ACS + "不存在"); + } + if (isConnectAcs.getValue().equals(IOSConstant.IS_DELETE_NO)) { + return AcsResponse.requestOkMessage("下发成功,未连接ACS系统!"); + } + + //ACS地址 + Param acsUrlParam = sysParamService.findByCode(SysParamConstant.ACS_URL); + if (ObjectUtil.isEmpty(acsUrlParam)) { + return AcsResponse.requestError("系统参数表中:" + SysParamConstant.ACS_URL + "不存在"); + } + + String url = acsUrlParam.getValue() + api; + try { + String resultMsg = HttpRequest.post(url) + .body("{}") + .execute().body(); + // 格式转换 + JSONObject result = JSONObject.parseObject(resultMsg); + resultAcs = JSONObject.toJavaObject(result, AcsResponse.class); + + log.info("下下发ACS获取车辆运行状态的输出参数为:-------------------" + resultMsg); + } catch (Exception e) { + //网络不通 + String msg = e.getMessage(); + log.error("连接失败:{}", msg); + return AcsResponse.requestError("网络不通,操作失败!"); + } + return resultAcs; + } + + + + /** + * 获取车辆运行状态 + * @param api acs地址 + * @return AcsResponse + */ + public static AcsResponse suspendAndStart(String api,String carNo) { + JSONObject jsonObject = new JSONObject() ; + jsonObject.put("carNo",carNo); + log.info("下发ACS车辆启听:-------------------"); + // 返回参数 + AcsResponse resultAcs; + // 系统参数类 + SysParamServiceImpl sysParamService = SpringContextHolder.getBean(SysParamServiceImpl.class); + //判断是否连接ACS系统 + Param isConnectAcs = sysParamService.findByCode(SysParamConstant.IS_CONNECT_ACS); + if (ObjectUtil.isEmpty(isConnectAcs)) { + return AcsResponse.requestError("系统参数表中:" + SysParamConstant.IS_CONNECT_ACS + "不存在"); + } + if (isConnectAcs.getValue().equals(IOSConstant.IS_DELETE_NO)) { + return AcsResponse.requestOkMessage("下发成功,未连接ACS系统!"); + } + + //ACS地址 + Param acsUrlParam = sysParamService.findByCode(SysParamConstant.ACS_URL); + if (ObjectUtil.isEmpty(acsUrlParam)) { + return AcsResponse.requestError("系统参数表中:" + SysParamConstant.ACS_URL + "不存在"); + } + + String url = acsUrlParam.getValue() + api; + try { + String resultMsg = HttpRequest.post(url) + .body(JSONObject.toJSONString(jsonObject)) + .execute().body(); + // 格式转换 + JSONObject result = JSONObject.parseObject(resultMsg); + resultAcs = JSONObject.toJavaObject(result, AcsResponse.class); + + log.info("下发ACS车辆启听的输出参数为:-------------------" + resultMsg); + } catch (Exception e) { + //网络不通 + String msg = e.getMessage(); + log.error("连接失败:{}", msg); + return AcsResponse.requestError("网络不通,操作失败!"); + } + return resultAcs; + } + } diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/wms/pda_manage/bigscreen_manage/controller/BigscreenController.java b/nladmin-system/nlsso-server/src/main/java/org/nl/wms/pda_manage/bigscreen_manage/controller/BigscreenController.java new file mode 100644 index 0000000..1951aea --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/wms/pda_manage/bigscreen_manage/controller/BigscreenController.java @@ -0,0 +1,74 @@ +package org.nl.wms.pda_manage.bigscreen_manage.controller; + + +import cn.dev33.satoken.annotation.SaIgnore; +import com.alibaba.fastjson.JSONObject; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.nl.common.logging.annotation.Log; +import org.nl.wms.pda_manage.bigscreen_manage.service.PdaBigScreenService; +import org.nl.wms.pda_manage.util.PdaResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 大屏功能 + * + * @author zhengxuming + * @since 2025-06-06 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/pda/bigscreen") +@Slf4j +@SaIgnore +public class BigscreenController { + + @Autowired + private PdaBigScreenService pdaBigScreenService; + + + @PostMapping("/getData") + @Log("大屏数据") + @SaIgnore + public ResponseEntity getData() { + PdaResponse data = pdaBigScreenService.getData(); + return new ResponseEntity<>(data, HttpStatus.OK); + } + + @PostMapping("/getAgvStatusByCode") + @Log("根据AGV编号获取设备状态") + @SaIgnore + public ResponseEntity getAgvStatusByCode(@RequestBody JSONObject whereJson) { + PdaResponse data = pdaBigScreenService.getAgvStatusByCode(whereJson); + return new ResponseEntity<>(data, HttpStatus.OK); + } + + + // @PostMapping("/suspend") + // @Log("暂停") + // public ResponseEntity suspend(@RequestBody JSONObject whereJson) { + // assertNotBlankJson(whereJson, "请求参数不能为空", "vehicle_code", "material_id","qty"); + // return new ResponseEntity<>(pdaIosInService.groupPlate(whereJson),HttpStatus.OK); + // } + + // @PostMapping("/start") + // @Log("启动") + // public ResponseEntity start(@RequestBody JSONObject whereJson) { + // assertNotBlankJson(whereJson, "请求参数不能为空", "vehicle_code", "material_id","qty"); + // return new ResponseEntity<>(pdaIosInService.groupPlate(whereJson),HttpStatus.OK); + // } + + // @PostMapping("/charge") + // @Log("去充电") + // public ResponseEntity charge(@RequestBody JSONObject whereJson) { + // assertNotBlankJson(whereJson, "请求参数不能为空", "vehicle_code", "material_id","qty"); + // return new ResponseEntity<>(pdaIosInService.groupPlate(whereJson),HttpStatus.OK); + // } + +} diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/wms/pda_manage/bigscreen_manage/service/PdaBigScreenService.java b/nladmin-system/nlsso-server/src/main/java/org/nl/wms/pda_manage/bigscreen_manage/service/PdaBigScreenService.java new file mode 100644 index 0000000..4f146cd --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/wms/pda_manage/bigscreen_manage/service/PdaBigScreenService.java @@ -0,0 +1,45 @@ +package org.nl.wms.pda_manage.bigscreen_manage.service; + + +import com.alibaba.fastjson.JSONObject; +import org.nl.wms.pda_manage.util.PdaResponse; + +/** + *

+ * 大屏显示 服务类 + *

+ * + * @author Liuxy + * @since 2025-06-24 + */ +public interface PdaBigScreenService { + + /** + * 获取大屏数据 + *根据配置的仓库 + * @return PdaResponse + */ + PdaResponse getData(); + + /** + * 获取大屏数据 + *根据配置的仓库 + * @return PdaResponse + */ + PdaResponse getAgvStatusByCode(JSONObject whereJson); + + + JSONObject getDashboardData(String storCode); + + /** + * 获取报表数据 + * @return 报表数据JSON对象,包含统计数据和历史数据 + */ + JSONObject getReportData(); + + /** + * 获取按周对比数据 + * @return 按周对比数据JSON对象 + */ + JSONObject getWeeklyData(); +} diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/wms/pda_manage/bigscreen_manage/service/dto/IvtAnalyse.java b/nladmin-system/nlsso-server/src/main/java/org/nl/wms/pda_manage/bigscreen_manage/service/dto/IvtAnalyse.java new file mode 100644 index 0000000..8823dda --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/wms/pda_manage/bigscreen_manage/service/dto/IvtAnalyse.java @@ -0,0 +1,11 @@ +package org.nl.wms.pda_manage.bigscreen_manage.service.dto; + +import lombok.Data; + +@Data +public class IvtAnalyse { + private String stor_code; + private String material_id; + private String material_name; + private Integer sum_qty; +} diff --git a/nladmin-system/nlsso-server/src/main/java/org/nl/wms/pda_manage/bigscreen_manage/service/impl/PdaBigScreenServiceImpl.java b/nladmin-system/nlsso-server/src/main/java/org/nl/wms/pda_manage/bigscreen_manage/service/impl/PdaBigScreenServiceImpl.java new file mode 100644 index 0000000..7482ff6 --- /dev/null +++ b/nladmin-system/nlsso-server/src/main/java/org/nl/wms/pda_manage/bigscreen_manage/service/impl/PdaBigScreenServiceImpl.java @@ -0,0 +1,762 @@ +package org.nl.wms.pda_manage.bigscreen_manage.service.impl; + +import cn.hutool.core.date.DateField; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONException; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import org.nl.wms.basedata_manage.service.IStructattrService; +import org.nl.wms.basedata_manage.service.dao.Structattr; +import org.nl.wms.basedata_manage.service.dao.mapper.BsrealStorattrMapper; +import org.nl.wms.basedata_manage.service.dao.mapper.StructattrMapper; +import org.nl.wms.ext_manage.service.WmsToAcsService; +import org.nl.wms.pda_manage.bigscreen_manage.service.PdaBigScreenService; +import org.nl.wms.pda_manage.util.PdaResponse; +import org.nl.wms.sch_manage.enums.TaskStatus; +import org.nl.wms.sch_manage.service.ISchBaseTaskService; +import org.nl.wms.sch_manage.service.dao.SchBaseTask; +import org.nl.wms.warehouse_manage.enums.IOSConstant; +import org.nl.wms.warehouse_manage.enums.IOSEnum; +import org.nl.wms.warehouse_manage.service.IOutBillService; +import org.nl.wms.warehouse_manage.service.dao.IOStorInv; +import org.nl.wms.warehouse_manage.service.dao.IOStorInvDis; +import org.nl.wms.warehouse_manage.service.dao.mapper.IOStorInvDisMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + *

+ * 大屏显示 实现类 + *

+ * + * @author Liuxy + * @since 2025-06-24 + */ +@Service +public class PdaBigScreenServiceImpl implements PdaBigScreenService { + + @Autowired + private IStructattrService iStructattrService; + + @Resource + private IOutBillService iOutBillService; + + @Resource + private BsrealStorattrMapper screenMapper; + + @Resource + private IOStorInvDisMapper ioStorInvDisMapper; + + @Autowired + private ISchBaseTaskService iSchBaseTaskService; + + @Autowired + private StructattrMapper structattrMapper; + + @Autowired + private WmsToAcsService wmsToAcsService; + + @Override + public PdaResponse getData() { + // 1. 构建统计数据 + JSONObject stats = new JSONObject(); + JSONObject result = new JSONObject(); + + //1、查询任务 + // 今日任务数 + QueryWrapper todayTaskWrapper = new QueryWrapper<>(); + todayTaskWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(SchBaseTask::getTask_status, TaskStatus.FINISHED.getCode()) + .ge(SchBaseTask::getCreate_time, DateUtil.beginOfDay(DateUtil.date())) + .le(SchBaseTask::getCreate_time, DateUtil.endOfDay(DateUtil.date())); + int todayTasks = iSchBaseTaskService.count(todayTaskWrapper); + stats.put("todayTasks", todayTasks); + + // 执行中任务数 + QueryWrapper executingTaskWrapper = new QueryWrapper<>(); + executingTaskWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(SchBaseTask::getTask_status, TaskStatus.EXECUTING.getCode()); + int executingTasks = iSchBaseTaskService.count(executingTaskWrapper); + stats.put("executingTasks", executingTasks); + + result.put("stats", stats); + + //通过查询ACS系统获取 + String agvStatus = wmsToAcsService.getCarStatus().getResponseDate(); + result.put("agvStatus", JSONObject.parse(agvStatus)); + return PdaResponse.requestParamOk(result); + } + + @Override + public PdaResponse getAgvStatusByCode(JSONObject whereJson) { + String agvStatus = wmsToAcsService.getCarStatus().getResponseDate(); + JSONObject jsonObject =getAgvStatusByNumber(agvStatus,whereJson.getInteger("device_code")); + return PdaResponse.requestParamOk(jsonObject); + } + + /** + * 根据AGV编号从JSON字符串中查找并返回对应的JSONObject + * + * @param agvStatusJson 包含AGV状态的JSON字符串 + * @param agvNumber 要查找的AGV编号 (例如 2) + * @return 找到的AGV状态JSONObject,如果未找到则返回null + */ + public static JSONObject getAgvStatusByNumber(String agvStatusJson, int agvNumber) { + try { + // 1. 使用Fastjson将JSON字符串解析为JSONArray + JSONArray agvArray = JSON.parseArray(agvStatusJson); + + // 2. 遍历JSONArray中的每个JSONObject + for (Object obj : agvArray) { + JSONObject agvObject = (JSONObject) obj; + + // 3. 获取当前对象的device_code + String deviceCode = agvObject.getString("device_code"); + + // 增加null检查,防止因缺少device_code字段而引发异常 + if (deviceCode != null) { + try { + // 4. 将device_code字符串转换为整数,并与目标编号比较 + int currentDeviceCode = Integer.parseInt(deviceCode); + if (currentDeviceCode == agvNumber) { + // 5. 如果编号匹配,则返回当前JSONObject + return agvObject; + } + } catch (NumberFormatException e) { + // 如果device_code的值无法转换为整数(例如 "AGV2"), + // 则打印一条警告信息并跳过这个对象 + System.err.println("警告: device_code '" + deviceCode + "' 不是一个有效的数字,已跳过此条目。"); + } + } + } + + } catch (JSONException e) { + // 如果JSON格式不正确,会抛出JSONException + e.printStackTrace(); + System.err.println("解析AGV状态JSON字符串失败: " + e.getMessage()); + } + + // 如果遍历完所有元素都没有找到匹配的,返回null + return null; + } + + + @Override + public JSONObject getDashboardData(String storCode) { + JSONObject item = new JSONObject(); + //1.【库位总数】数据 + item.put("overview", overview(storCode)); + item.put("locations", locations(storCode)); + return item; + } + + /** +// +// * 货位使用 +// * +// * @return JSONObject { +// * total_qty: 总货位数 +// * use_qty: 已用货位 +// * emp_qty: 空余货位 +// * use_percentage: 百分比(使用货位百分比) +// * } +// */ + private JSONObject overview(String storCode) { + return structattrMapper.overview(storCode); + } + + private List locations(String storCode) { + return structattrMapper.locations(storCode); + } + + private JSONObject pointUse(String storCode) { + // 返回数据 + JSONObject result = new JSONObject(); + // 查询所有未删除且启用仓位 + int emp_qty = iStructattrService.count( + new QueryWrapper().lambda() + .eq(Structattr::getIs_used, Boolean.TRUE) + .eq(Structattr::getStor_code, storCode) + .isNull(Structattr::getStoragevehicle_code)); + int use_qty = iStructattrService.count( + new QueryWrapper().lambda() + .eq(Structattr::getIs_used, Boolean.TRUE) + .eq(Structattr::getStor_code, storCode) + .isNotNull(Structattr::getStoragevehicle_code)); + int total_qty = iStructattrService.count( + new QueryWrapper().lambda() + .eq(Structattr::getIs_used, Boolean.TRUE) + .eq(Structattr::getStor_code, storCode)); + // 总货位数 + result.put("total_qty", total_qty); + // 已用货位数 + result.put("use_qty", use_qty); + // 空余货位 + result.put("emp_qty", emp_qty); + // 使用货位百分比 + double use_percentage = NumberUtil.mul(NumberUtil.div(use_qty, total_qty==0?1:total_qty), 100); + result.put("use_percentage", NumberUtil.round(use_percentage, 2)); + return result; + } + + + /** + * 实时库存分析 + * + * @return JSONObject { + * total_qty: 总数 + * data: [ + * material_name: 物料名称 + * ivt_qty: 库存数量 + * percentage: 百分比 + * ] + * } + */ + private JSONObject ivtAnalyse(String storCode) { + return null; + } + + /** + * 出入库趋势 + * + * @return JSONObject { + * in: [{date:日期,qty:数量}] + * out: [{date:日期,qty:数量}] + * } + */ + private JSONObject inAndOutTrend(String storCode) { + // 获取七天天数集合 + DateTime dateTime = DateUtil.offsetDay(DateUtil.parseDate(DateUtil.today()), -6); + List dateList = DateUtil.rangeToList(dateTime, DateUtil.parse(DateUtil.today()), DateField.DAY_OF_YEAR).stream() + .map(DateTime::toString) + .map(row -> row.substring(0, 10)) + .collect(Collectors.toList()); + // 查询七天内的出入库数据 + List allIosList = iOutBillService.list( + new QueryWrapper().lambda() + .in(IOStorInv::getBiz_date, dateList) + .eq(IOStorInv::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(IOStorInv::getStor_code, storCode) + .eq(IOStorInv::getBill_status, IOSEnum.BILL_STATUS.code("完成")) + ); + // 查询入库单据 + List inIosList = allIosList.stream() + .filter(row -> row.getIo_type().equals(IOSEnum.IO_TYPE.code("入库"))) + .collect(Collectors.toList()); + // 查询出库单据 + List outIosList = allIosList.stream() + .filter(row -> row.getIo_type().equals(IOSEnum.IO_TYPE.code("出库"))) + .collect(Collectors.toList()); + // 组织数据 + List inList = new ArrayList<>(); + List outList = new ArrayList<>(); + for (String date : dateList) { + // 处理入库数量 + JSONObject jsonIn = new JSONObject(); + jsonIn.put("date", date); + double in_qty = inIosList.stream() + .filter(row -> row.getBiz_date().equals(date)) + .map(row -> row.getTotal_qty().doubleValue()) + .reduce(Double::sum).orElse(0.00); + jsonIn.put("qty", NumberUtil.round(in_qty, 2)); + inList.add(jsonIn); + + // 处理出库数据 + JSONObject jsonOut = new JSONObject(); + jsonOut.put("date", date); + double out_qty = outIosList.stream() + .filter(row -> row.getBiz_date().equals(date)) + .map(row -> row.getTotal_qty().doubleValue()) + .reduce(Double::sum).orElse(0.00); + jsonOut.put("qty", NumberUtil.round(out_qty, 2)); + outList.add(jsonOut); + } + + JSONObject result = new JSONObject(); + result.put("in", inList); + result.put("out", outList); + return result; + } + + /** + * 今日出入库 + * + * @return JSONObject { + * in: {total_qty:总数量,vehicle_qty:托盘数量} + * out: {total_qty:总数量,vehicle_qty:托盘数量} + * } + */ + private JSONObject toDayInAndOut(String storCode) { + // 查询今天出入库单据 + List inList = iOutBillService.list( + new QueryWrapper().lambda() + .eq(IOStorInv::getBiz_date, DateUtil.today()) + .eq(IOStorInv::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(IOStorInv::getStor_code, storCode) + .eq(IOStorInv::getIo_type, IOSEnum.IO_TYPE.code("入库")) + .eq(IOStorInv::getBill_status, IOSEnum.BILL_STATUS.code("完成")) + ); + // 查询今天出库单据 + List outList = iOutBillService.list( + new QueryWrapper().lambda() + .eq(IOStorInv::getBiz_date, DateUtil.today()) + .eq(IOStorInv::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(IOStorInv::getStor_code, storCode) + .eq(IOStorInv::getIo_type, IOSEnum.IO_TYPE.code("出库")) + .eq(IOStorInv::getBill_status, IOSEnum.BILL_STATUS.code("完成")) + ); + + // 入库 + JSONObject jsonIn = new JSONObject(); + // 总数量 + double total_qty_in = inList.stream() + .map(row -> row.getTotal_qty().doubleValue()) + .reduce(Double::sum).orElse(0.0); + jsonIn.put("total_qty", NumberUtil.round(total_qty_in, 2)); + + // 托盘数 + List inDisList = new ArrayList<>(); + if (ObjectUtil.isNotEmpty(inList)) { + inDisList = ioStorInvDisMapper.selectList( + new QueryWrapper().lambda() + .in(IOStorInvDis::getIostorinv_id, inList.stream() + .map(IOStorInv::getIostorinv_id) + .collect(Collectors.toList()) + ) + ); + } + jsonIn.put("vehicle_qty", inDisList.size()); + + // 出库 + JSONObject jsonOut = new JSONObject(); + // 总数量 + double total_qty_out = outList.stream() + .map(row -> row.getTotal_qty().doubleValue()) + .reduce(Double::sum).orElse(0.0); + jsonOut.put("total_qty", NumberUtil.round(total_qty_out, 2)); + // 托盘数 + List outDisList = new ArrayList<>(); + if (ObjectUtil.isNotEmpty(outList)) { + outDisList = ioStorInvDisMapper.selectList( + new QueryWrapper().lambda() + .in(IOStorInvDis::getIostorinv_id, outList.stream() + .map(IOStorInv::getIostorinv_id) + .collect(Collectors.toList()) + ) + ); + } + jsonOut.put("vehicle_qty", outDisList.size()); + + JSONObject result = new JSONObject(); + result.put("in", jsonIn); + result.put("out", jsonOut); + return result; + } + + /** + * 实时任务 + * + * @return List { + * 任务实体列 + * } + */ + private List realTask(String storCode) { + List list = iSchBaseTaskService.list( + new QueryWrapper().lambda() + .in(SchBaseTask::getTask_status, TaskStatus.CREATE.getCode() + , TaskStatus.ISSUED.getCode(), TaskStatus.EXECUTING.getCode() + ) + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + ); + list.stream().forEach(item -> { + if (item.getTask_status().equals(TaskStatus.CREATE.getCode())) { + item.setTask_status(TaskStatus.CREATE.getName()); + } + if (item.getTask_status().equals(TaskStatus.ISSUED.getCode())) { + item.setTask_status(TaskStatus.ISSUED.getName()); + } + if (item.getTask_status().equals(TaskStatus.EXECUTING.getCode())) { + item.setTask_status(TaskStatus.EXECUTING.getName()); + } + }); + + return list; + } + + /** + * 未完成单据 + * + * @return List{ + * 出入库实体类 + * } + */ + private List unIos(String storCode) { + + List list = iOutBillService.list( + new QueryWrapper().lambda() + .eq(IOStorInv::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(IOStorInv::getStor_code, storCode) + .ne(IOStorInv::getBill_status, IOSEnum.BILL_STATUS.code("完成")) + ); + list.stream().forEach(item -> { + item.setIo_type(item.getIo_type().equals(IOSEnum.IO_TYPE.code("入库"))?"入库":"出库"); + if (item.getBill_status().equals(IOSEnum.BILL_STATUS.code("生成"))) { + item.setBill_status("生成"); + } + if (item.getBill_status().equals(IOSEnum.BILL_STATUS.code("分配中"))) { + item.setBill_status("分配中"); + } + if (item.getBill_status().equals(IOSEnum.BILL_STATUS.code("分配完"))) { + item.setBill_status("分配完"); + } + item.setBill_type("dict.getLabel()"); + }); + return list; + } + + @Override + public JSONObject getReportData() { + String storCode = "FJ"; // 默认为富佳仓库 + JSONObject result = new JSONObject(); + + // 1. 构建统计数据 + JSONObject stats = new JSONObject(); + + // 总任务数 + QueryWrapper taskWrapper = new QueryWrapper<>(); + taskWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(SchBaseTask::getTask_status,TaskStatus.FINISHED.getCode()) + ; + stats.put("totalTasks", iSchBaseTaskService.count(taskWrapper)); + + // 今日任务数 + QueryWrapper todayTaskWrapper = new QueryWrapper<>(); + todayTaskWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(SchBaseTask::getTask_status,TaskStatus.FINISHED.getCode()) + .ge(SchBaseTask::getCreate_time, DateUtil.beginOfDay(DateUtil.date())) + .le(SchBaseTask::getCreate_time, DateUtil.endOfDay(DateUtil.date())); + int todayTasks = iSchBaseTaskService.count(todayTaskWrapper); + stats.put("todayTasks", todayTasks); + + // 昨日任务数(计算日环比) + QueryWrapper yesterdayTaskWrapper = new QueryWrapper<>(); + yesterdayTaskWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(SchBaseTask::getTask_status,TaskStatus.FINISHED.getCode()) + .ge(SchBaseTask::getCreate_time, DateUtil.beginOfDay(DateUtil.offsetDay(DateUtil.date(), -1))) + .le(SchBaseTask::getCreate_time, DateUtil.endOfDay(DateUtil.offsetDay(DateUtil.date(), -1))); + int yesterdayTasks = iSchBaseTaskService.count(yesterdayTaskWrapper); + double dayOnDay = yesterdayTasks > 0 ? ((double)(todayTasks - yesterdayTasks) / yesterdayTasks) * 100 : 0; + stats.put("dayOnDay", NumberUtil.round(dayOnDay, 1)); + + // 本月任务数 + QueryWrapper monthTaskWrapper = new QueryWrapper<>(); + monthTaskWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(SchBaseTask::getTask_status,TaskStatus.FINISHED.getCode()) + .ge(SchBaseTask::getCreate_time, DateUtil.beginOfMonth(DateUtil.date())) + .le(SchBaseTask::getCreate_time, DateUtil.endOfMonth(DateUtil.date())); + stats.put("monthTasks", iSchBaseTaskService.count(monthTaskWrapper)); + + // 月均任务数(简单计算:总任务数/12) + // int totalTasks = iSchBaseTaskService.count(taskWrapper); + // stats.put("monthlyAvg", Math.round((double)totalTasks / 12)); + + // 库位数量 + QueryWrapper structattrWrapper = new QueryWrapper<>(); + structattrWrapper.lambda().eq(Structattr::getStor_code, storCode) + .eq(Structattr::getIs_used, Boolean.TRUE); + stats.put("locationCount", iStructattrService.count(structattrWrapper)); + + // 在库数量 + int inventoryCount = iStructattrService.count( + new QueryWrapper().lambda() + .eq(Structattr::getStor_code, storCode) + .eq(Structattr::getIs_used, Boolean.TRUE) + .isNotNull(Structattr::getStoragevehicle_code)); + stats.put("inventoryCount", inventoryCount); + + // 入库数量 + int inboundCount = iSchBaseTaskService.count( + new QueryWrapper().lambda() + .eq(SchBaseTask::getConfig_code,"STInTask") + .eq(SchBaseTask::getIs_delete, Boolean.FALSE) + .eq(SchBaseTask::getTask_status,TaskStatus.FINISHED.getCode())); + stats.put("inboundCount", inboundCount); + // 出库数量 + int outboundCount = iSchBaseTaskService.count( + new QueryWrapper().lambda() + .eq(SchBaseTask::getConfig_code,"STOutTask") + .eq(SchBaseTask::getIs_delete, Boolean.FALSE) + .eq(SchBaseTask::getTask_status,TaskStatus.FINISHED.getCode())); + stats.put("outboundCount", outboundCount); + // 运转数量 + int operationCount = iSchBaseTaskService.count( + new QueryWrapper().lambda() + .eq(SchBaseTask::getConfig_code,"MoveTask") + .eq(SchBaseTask::getIs_delete, Boolean.FALSE) + .eq(SchBaseTask::getTask_status,TaskStatus.FINISHED.getCode())); + stats.put("operationCount", operationCount); + + result.put("stats", stats); + + // 2. 构建历史数据(最近7天) + List historyList = new ArrayList<>(); + DateTime dateTime = DateUtil.offsetDay(DateUtil.date(), -6); + List dateList = DateUtil.rangeToList(dateTime, DateUtil.date(), DateField.DAY_OF_YEAR).stream() + .map(DateTime::toString) + .map(row -> row.substring(0, 10)) + .collect(Collectors.toList()); + + for (String date : dateList) { + JSONObject item = new JSONObject(); + item.put("date", date); + + // 查询该日入库任务数 + QueryWrapper inWrapper = new QueryWrapper<>(); + inWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(SchBaseTask::getConfig_code, "STInTask") + .ge(SchBaseTask::getCreate_time, DateUtil.parse(date + " 00:00:00")) + .le(SchBaseTask::getCreate_time, DateUtil.parse(date + " 23:59:59")); + item.put("inbound", iSchBaseTaskService.count(inWrapper)); + + // 查询该日出库任务数 + QueryWrapper outWrapper = new QueryWrapper<>(); + outWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(SchBaseTask::getConfig_code, "STOutTask") + .ge(SchBaseTask::getCreate_time, DateUtil.parse(date + " 00:00:00")) + .le(SchBaseTask::getCreate_time, DateUtil.parse(date + " 23:59:59")); + item.put("outbound", iSchBaseTaskService.count(outWrapper)); + + // 其他搬运任务数 + QueryWrapper otherWrapper = new QueryWrapper<>(); + otherWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .ne(SchBaseTask::getConfig_code, "STOutTask") + .ne(SchBaseTask::getConfig_code, "STInTask") + .ge(SchBaseTask::getCreate_time, DateUtil.parse(date + " 00:00:00")) + .le(SchBaseTask::getCreate_time, DateUtil.parse(date + " 23:59:59")); + item.put("other", iSchBaseTaskService.count(otherWrapper)); + + // 计算总数 + int inbound = iSchBaseTaskService.count(inWrapper); + int outbound = iSchBaseTaskService.count(outWrapper); + int other = iSchBaseTaskService.count(otherWrapper); + item.put("total", inbound + outbound + other); + + historyList.add(item); + } + + result.put("historyList", historyList); + return result; + } + + @Override + public JSONObject getWeeklyData() { + String storCode = "FJ"; // 默认为富佳仓库 + JSONObject result = new JSONObject(); + JSONObject data = new JSONObject(); // 创建data对象用于封装所有数据 + + // 获取当前时间和近3周的时间范围 + DateTime now = DateUtil.date(); + + // 1. 计算周对比数据(本周和上周) + // 本周数据 + DateTime weekStart = DateUtil.beginOfWeek(now, true); // 周一为起始 + DateTime weekEnd = DateUtil.endOfWeek(now, true); + + // 上周数据 + DateTime lastWeekStart = DateUtil.beginOfWeek(DateUtil.offsetWeek(now, -1), true); + DateTime lastWeekEnd = DateUtil.endOfWeek(DateUtil.offsetWeek(now, -1), true); + + // 本周任务统计 + JSONObject currentWeek = new JSONObject(); + QueryWrapper currentWeekWrapper = new QueryWrapper<>(); + currentWeekWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .ge(SchBaseTask::getCreate_time, weekStart) + .le(SchBaseTask::getCreate_time, weekEnd); + Integer totalTasks = iSchBaseTaskService.count(currentWeekWrapper); + currentWeek.put("totalTasks", totalTasks); + + // 本周已完成任务数 + QueryWrapper currentWeekCompletedWrapper = new QueryWrapper<>(); + currentWeekCompletedWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(SchBaseTask::getTask_status, TaskStatus.FINISHED.getCode()) + .ge(SchBaseTask::getCreate_time, weekStart) + .le(SchBaseTask::getCreate_time, weekEnd); + Integer completedTasks = iSchBaseTaskService.count(currentWeekCompletedWrapper); + currentWeek.put("completedTasks", completedTasks); + + // 计算完成率 + double completionRate = totalTasks > 0 ? (double) completedTasks / totalTasks * 100 : 0; + currentWeek.put("completionRate", NumberUtil.round(completionRate, 1)); + + // 本周入库任务数 + QueryWrapper currentWeekInWrapper = new QueryWrapper<>(); + currentWeekInWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(SchBaseTask::getConfig_code, "STInTask") + .ge(SchBaseTask::getCreate_time, weekStart) + .le(SchBaseTask::getCreate_time, weekEnd); + currentWeek.put("inboundTasks", iSchBaseTaskService.count(currentWeekInWrapper)); + + // 本周出库任务数 + QueryWrapper currentWeekOutWrapper = new QueryWrapper<>(); + currentWeekOutWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(SchBaseTask::getConfig_code, "STOutTask") + .ge(SchBaseTask::getCreate_time, weekStart) + .le(SchBaseTask::getCreate_time, weekEnd); + currentWeek.put("outboundTasks", iSchBaseTaskService.count(currentWeekOutWrapper)); + + // 上周任务统计 + JSONObject lastWeek = new JSONObject(); + QueryWrapper lastWeekWrapper = new QueryWrapper<>(); + lastWeekWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .ge(SchBaseTask::getCreate_time, lastWeekStart) + .le(SchBaseTask::getCreate_time, lastWeekEnd); + Integer lastTotalTasks = iSchBaseTaskService.count(lastWeekWrapper); + lastWeek.put("totalTasks", lastTotalTasks); + + // 上周已完成任务数 + QueryWrapper lastWeekCompletedWrapper = new QueryWrapper<>(); + lastWeekCompletedWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(SchBaseTask::getTask_status, TaskStatus.FINISHED.getCode()) + .ge(SchBaseTask::getCreate_time, lastWeekStart) + .le(SchBaseTask::getCreate_time, lastWeekEnd); + Integer lastCompletedTasks = iSchBaseTaskService.count(lastWeekCompletedWrapper); + lastWeek.put("completedTasks", lastCompletedTasks); + + // 计算上周完成率 + double lastCompletionRate = lastTotalTasks > 0 ? (double) lastCompletedTasks / lastTotalTasks * 100 : 0; + lastWeek.put("completionRate", NumberUtil.round(lastCompletionRate, 1)); + + // 上周入库任务数 + QueryWrapper lastWeekInWrapper = new QueryWrapper<>(); + lastWeekInWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(SchBaseTask::getConfig_code, "STInTask") + .ge(SchBaseTask::getCreate_time, lastWeekStart) + .le(SchBaseTask::getCreate_time, lastWeekEnd); + lastWeek.put("inboundTasks", iSchBaseTaskService.count(lastWeekInWrapper)); + + // 上周出库任务数 + QueryWrapper lastWeekOutWrapper = new QueryWrapper<>(); + lastWeekOutWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(SchBaseTask::getConfig_code, "STOutTask") + .ge(SchBaseTask::getCreate_time, lastWeekStart) + .le(SchBaseTask::getCreate_time, lastWeekEnd); + lastWeek.put("outboundTasks", iSchBaseTaskService.count(lastWeekOutWrapper)); + + // 计算周环比 + double totalGrowthRate = lastTotalTasks > 0 + ? ((double)(totalTasks - lastTotalTasks) / lastTotalTasks) * 100 : 0; + + double inboundGrowthRate = lastWeek.getInteger("inboundTasks") > 0 + ? ((double)(currentWeek.getInteger("inboundTasks") - lastWeek.getInteger("inboundTasks")) + / lastWeek.getInteger("inboundTasks")) * 100 : 0; + + double outboundGrowthRate = lastWeek.getInteger("outboundTasks") > 0 + ? ((double)(currentWeek.getInteger("outboundTasks") - lastWeek.getInteger("outboundTasks")) + / lastWeek.getInteger("outboundTasks")) * 100 : 0; + + JSONObject growthRates = new JSONObject(); + growthRates.put("totalGrowthRate", NumberUtil.round(totalGrowthRate, 1)); + growthRates.put("inboundGrowthRate", NumberUtil.round(inboundGrowthRate, 1)); + growthRates.put("outboundGrowthRate", NumberUtil.round(outboundGrowthRate, 1)); + growthRates.put("growthRate", NumberUtil.round(totalGrowthRate, 1)); // 为了兼容前端dashboard2.js中的直接使用 + + // 2. 计算近3周的数据(用于图表) + List weeks = new ArrayList<>(); + List inboundData = new ArrayList<>(); + List outboundData = new ArrayList<>(); + List otherData = new ArrayList<>(); + + // 获取近3周的数据 + for (int i = 2; i >= 0; i--) { + DateTime targetWeekStart = DateUtil.beginOfWeek(DateUtil.offsetWeek(now, -i), true); + DateTime targetWeekEnd = DateUtil.endOfWeek(DateUtil.offsetWeek(now, -i), true); + + // 格式化周标签(MM-DD-MM-DD格式) + String weekLabel = targetWeekStart.toString("MM-dd") + "-" + targetWeekEnd.toString("MM-dd"); + weeks.add(weekLabel); + + // 该周入库任务数 + QueryWrapper weekInWrapper = new QueryWrapper<>(); + weekInWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(SchBaseTask::getConfig_code, "STInTask") + .ge(SchBaseTask::getCreate_time, targetWeekStart) + .le(SchBaseTask::getCreate_time, targetWeekEnd); + inboundData.add(iSchBaseTaskService.count(weekInWrapper)); + + // 该周出库任务数 + QueryWrapper weekOutWrapper = new QueryWrapper<>(); + weekOutWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .eq(SchBaseTask::getConfig_code, "STOutTask") + .ge(SchBaseTask::getCreate_time, targetWeekStart) + .le(SchBaseTask::getCreate_time, targetWeekEnd); + outboundData.add(iSchBaseTaskService.count(weekOutWrapper)); + + // 该周其他任务数(非入库出库任务) + QueryWrapper weekOtherWrapper = new QueryWrapper<>(); + weekOtherWrapper.lambda() + .eq(SchBaseTask::getIs_delete, IOSConstant.IS_DELETE_NO) + .notIn(SchBaseTask::getConfig_code, Arrays.asList("STInTask", "STOutTask")) + .ge(SchBaseTask::getCreate_time, targetWeekStart) + .le(SchBaseTask::getCreate_time, targetWeekEnd); + otherData.add(iSchBaseTaskService.count(weekOtherWrapper)); + } + + // 构造符合前端要求的响应结构 + JSONObject chartData = new JSONObject(); + chartData.put("weeks", weeks); + chartData.put("inboundData", inboundData); + chartData.put("outboundData", outboundData); + chartData.put("otherData", otherData); + + // 将所有数据放入data对象 + data.put("thisWeek", currentWeek); + data.put("lastWeek", lastWeek); + data.put("growthRate", NumberUtil.round(totalGrowthRate, 1)); // 为了兼容前端dashboard2.js中的直接使用 + data.put("growthRates", growthRates); + data.put("currentWeekDate", weekStart.toString("yyyy-MM-dd") + " 至 " + weekEnd.toString("yyyy-MM-dd")); + data.put("lastWeekDate", lastWeekStart.toString("yyyy-MM-dd") + " 至 " + lastWeekEnd.toString("yyyy-MM-dd")); + data.put("weeks", weeks); + data.put("inboundData", inboundData); + data.put("outboundData", outboundData); + data.put("otherData", otherData); + + // 为了兼容两种前端使用方式,将数据同时放在根级别和data对象中 + result.put("success", true); + result.put("data", data); + result.put("thisWeek", currentWeek); + result.put("lastWeek", lastWeek); + result.put("growthRate", NumberUtil.round(totalGrowthRate, 1)); + + return result; + } +}