302 Commits
Author SHA1 Message Date
秦鸿展 07393e43ff 31 2026-09-13 18:26:40 +08:00
秦鸿展 08858f24d6 30 2026-09-13 18:22:47 +08:00
秦鸿展 a994241400 BI月台指标新增字段 2026-09-13 18:21:26 +08:00
王奎兴 a9be03961a feign地址修改; 2026-09-12 17:57:49 +08:00
王奎兴 7f6006ca48 feign地址修改; 2026-09-12 17:20:01 +08:00
赵辉 defc8c3709 31 2026-09-12 10:03:33 +08:00
王奎兴 3c21c2c848 发30 2026-09-12 10:00:45 +08:00
王奎兴 71936b50a7 Merge remote-tracking branch 'origin/dev' into dev 2026-09-12 09:59:56 +08:00
赵辉 9cfffe1224 去除验证码校验 2026-09-12 09:59:39 +08:00
王奎兴 d7574d188a 客户月结报告; 2026-09-12 09:59:30 +08:00
赵辉 1c1b06c1bf 去除验证码校验 2026-09-12 09:24:11 +08:00
rcx 117019c55c 30 2026-09-11 17:46:07 +08:00
rcx 171909b68d 31 2026-09-11 17:44:15 +08:00
赵辉 31c27c16c6 user31 2026-09-11 17:28:40 +08:00
赵辉 8dfe9545aa user30 2026-09-11 17:22:50 +08:00
赵辉 028afb57d7 用户-yms同步新增与修改密码 2026-09-11 15:45:15 +08:00
rcx 37dd374bda fix(wms): 入库单导入"该货主下不存在该料号"误报修复
- 根因:Excel客户名称按USER表user_name解析货主ID,同名双账号(如3519/3525)解析到非档案归属账号,带错误shipperId查物料必为空
- 料号匹配:主查询(料号+顶级组织+解析shipperId)为空时,去掉货主条件按档案shipper_name与Excel客户名称等值兜底匹配,命中后以档案货主纠正单据shipperId/shipperCode/shipperName
- 新增pickMaterialByCode挑选:料号精确等值优先于LIKE模糊命中,避免子串料号截胡
- SKU校验:档案条码barCode为空时跳过比对,档案有值时仍严格校验
- 校验失败输出诊断日志并附解析shipperId/登录topOrganizationId,区分货主错配(原因A)与组织口径(原因B)
2026-09-11 15:14:46 +08:00
秦鸿展 d69af9ef20 30 2026-09-10 16:49:24 +08:00
秦鸿展 9704dbfb07 导入物料组织覆盖问题 2026-09-10 16:20:59 +08:00
秦鸿展 3276792bb5 告警修改 2026-09-10 13:20:07 +08:00
秦鸿展 b57db4b8cc 30 2026-09-10 11:49:34 +08:00
秦鸿展 8a31ed9974 BI月台指标检测修改 2026-09-10 11:46:34 +08:00
秦鸿展 9777156959 31 2026-09-09 23:46:09 +08:00
秦鸿展 ba5d78b36c 准时率统计修改 2026-09-09 23:40:33 +08:00
秦鸿展 dea39a1a4b 30 2026-09-09 22:05:47 +08:00
秦鸿展 0493030620 BI月台作业准时率计算 2026-09-09 22:02:31 +08:00
秦鸿展 3907b126d9 31 2026-09-09 19:42:39 +08:00
秦鸿展 7b76d5c203 BI接口业务类型恢复 2026-09-09 19:38:47 +08:00
秦鸿展 f02a2d52f2 OMS调整接口开发 2026-09-09 16:24:05 +08:00
秦鸿展 a3c0f15e39 30 2026-09-09 15:19:29 +08:00
秦鸿展 d2a6a5a4f6 BI启动报错 2026-09-09 15:12:55 +08:00
秦鸿展 752216ad45 31 2026-09-09 14:22:58 +08:00
秦鸿展 6a8661da63 BI模块修改 2026-09-09 13:59:55 +08:00
秦鸿展 2b6777abe4 BI模块修改 2026-09-09 13:47:46 +08:00
rcx fd3a9afddf 发31 2026-09-08 17:52:42 +08:00
rcx 0358c694ea 发30 2026-09-08 17:46:49 +08:00
rcx 009dc5b3a6 feat(wms): 盘点单详情investigetionManageDetailList返回批次号、入库日期、生产日期、过期日期 2026-09-08 17:35:29 +08:00
rcx 178b7697af fix(oms): 出库单列表totalOutboundQuantity恒为null,queryList改为SQL汇总一级明细出库数量
原Java循环逐单调getInfoChildren汇总(N+1)被注释后无赋值导致恒为null;改为关联子查询coalesce(sum(outbound_quantity),0),仅统计level=1且del_flag=1,口径与详情页一致;/list与/list1共用同一语句同时生效,无需DDL
2026-09-08 15:08:00 +08:00
rcx 32364baba8 发测试 2026-09-08 13:38:54 +08:00
rcx 6c6a491ff8 feat(oms/wms): WMS上架状态回传OMS入库业务单,修复入库状态不同步
- WMS上架推进入库状态后回传OMS(OmsServiceFeign新增returnInResult)
- OMS回传接收加固:无登录上下文判空、已入库同步issueStatus=4
- 已入库联动同步预约入库单(入库业务单)状态
2026-09-08 13:37:43 +08:00
rcx a496fc235b fix(oms): 新增/编辑入库单以业务类型为准同步单据类型,修复退货入库被写成普通入库及编辑类型不生效 2026-09-08 10:53:23 +08:00
rcx 06202b652a 发测试 2026-09-08 09:32:47 +08:00
rcx 5d748a9ac6 fix(oms): 入库业务单新增时用businessType回填单据类型,修复退货单被写成普通入库 2026-09-08 09:28:27 +08:00
王奎兴 361a91d701 发31 2026-09-07 19:02:39 +08:00
王奎兴 3471874139 退货入库; 2026-09-07 18:58:33 +08:00
王奎兴 a38e28bb97 退货入库; 2026-09-07 18:57:51 +08:00
rcx ab7a4e265f Merge branch 'dev-ty1.2' into dev
# Conflicts:
#	mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/BmsServiceFeign.java
#	mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/OmsServiceFeign.java
#	mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/WlhyServiceFeign.java
#	mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/WmsServiceFeign.java
#	mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/YmsServiceFeign.java
#	mhd-common/mhd-common-core/src/main/java/com/mhd/common/core/feign/ThirdPartyServiceFeign.java
#	mhd_oms/src/main/java/com/mhd/oms/infrastructure/feign/ThirdPartyServiceFeign.java
#	mhd_wms/src/main/resources/bootstrap.yml
2026-09-07 18:03:18 +08:00
rcx 976ab715af 发30 2026-09-07 18:02:10 +08:00
王奎兴 6856899afc Merge remote-tracking branch 'origin/dev' into dev 2026-09-07 17:23:10 +08:00
王奎兴 6d7be15d97 发31 2026-09-07 17:23:01 +08:00
rcx 0babc7dbf8 Merge remote-tracking branch 'origin/dev' into dev 2026-09-07 17:19:31 +08:00
王奎兴 1a80f715dd 恢复xxl 2026-09-07 17:16:11 +08:00
rcx 7742d11d1c 发31 2026-09-07 17:15:05 +08:00
rcx c4df9b3403 Merge remote-tracking branch 'origin/dev' into dev 2026-09-07 17:08:20 +08:00
rcx 7ed34df81e 发30 2026-09-07 17:07:16 +08:00
王奎兴 0ace96ea9f 恢复xxl 2026-09-07 17:06:27 +08:00
王奎兴 34014f40a5 Merge branch 'dev-ty1.2' into dev 2026-09-07 16:57:14 +08:00
rcx acbf7c9fdf fix: 退货入库单下发时执行入库单类型被固定写成普通入库 2026-09-07 16:56:17 +08:00
rcx 9e1c5d8570 fix(oms): 预约单下发不再推送WMS,修复执行单下发后WMS出现预约单+执行单两张单;三个下发入口补充软删单拦截 2026-09-07 15:44:44 +08:00
秦鸿展 98b5ce81cc 委托方导出功能 2026-09-07 14:28:29 +08:00
rcx b6a69411f1 发 测试 2026-09-07 14:25:29 +08:00
rcx 34b27eabd2 fix(oms): 出入库导入行级必填校验+页签解析失败完整堆栈日志
一、行级必填校验(ReservationStockInOrderImpl)
- 入库导入:新增校验空【產品編號】/空【支/件数】的明细行,给出明确提示
- 出库导入:新增校验空【支/件数】的明细行
- 修复原因:明细行件数为空时 new BigDecimal(null) 抛无信息的 NullPointerException,页面只显示 页签[x]:java.lang.NullPointerException 无法定位

二、页签解析失败完整堆栈日志
- 出/入库多页签解析第一阶段捕获异常时,将完整堆栈打入服务端日志(LOGGER.error),便于远程定位页签内具体报错行号
2026-09-07 14:18:34 +08:00
rcx 1c4b520f5a Merge remote-tracking branch 'origin/dev-bms-260826' into dev-ty1.2 2026-09-07 13:50:28 +08:00
王奎兴 32459fd443 xxl-job配置; 2026-09-07 11:33:14 +08:00
王奎兴 625b195183 发测试 2026-09-07 10:21:36 +08:00
王奎兴 0c95470300 Merge branch 'dev-ty1.2' into dev-bms-260826 2026-09-07 10:19:22 +08:00
王奎兴 ba3a540a94 Merge branch 'dev' into dev-bms-260826
# Conflicts:
#	mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/BmsServiceFeign.java
#	mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/OmsServiceFeign.java
#	mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/WlhyServiceFeign.java
#	mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/WmsServiceFeign.java
#	mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/YmsServiceFeign.java
#	mhd-common/mhd-common-core/src/main/java/com/mhd/common/core/feign/ThirdPartyServiceFeign.java
#	mhd_oms/src/main/java/com/mhd/oms/infrastructure/feign/ThirdPartyServiceFeign.java
#	mhd_wms/src/main/resources/bootstrap.yml
2026-09-07 10:00:28 +08:00
王奎兴 a484e81b32 发测试 2026-09-07 09:23:22 +08:00
rcx 627f63cbd3 fix(oms,wms): 入库执行单取消下发完整回退WMS数据,修复重复下发数据致取消阻断
一、取消下发回退不完整/阻断:原OMS跨库仅软删4张表,收货任务/收货作业单/质检单/电子签名残留;且getWShOrder在重复下发产生多张同号收货单时抛TooManyResultsException,取消下发整体回滚失败。WMS新增/stockInOrderApi/omsCancelInOrder:单事务软删下发链路全部8张表(入库单/明细、收货单/明细、收货任务、收货作业单/明细、质检单),幂等可重试,已开始收货报错拦截;OMS执行单取消下发改调该接口并清理待签名电子签名;预约单取消下发补齐WMS清理

二、下发失败被静默吞掉:omsInOrderAdd/omsInOrderAddDetail原返回void、Feign降级静默,OMS已下发但WMS无数据。改返回AjaxResult、fallback报错,执行单/预约单下发逐一校验返回值,失败回滚下发标记(WMS侧幂等,重下发安全)

三、下发防重原子化:issueOrder下发标记改条件更新(issue_time IS NULL)+影响行数校验,关闭并发/双击重复推送WMS的窗口
2026-09-07 09:20:37 +08:00
王奎兴 f6c454765e jobid查询接口; 2026-09-05 19:21:17 +08:00
王奎兴 25102b5207 jobid查询接口; 2026-09-05 19:14:34 +08:00
王奎兴 6708353379 jobid查询接口; 2026-09-05 19:08:42 +08:00
王奎兴 20d11dc9b2 jobid查询接口; 2026-09-05 19:00:02 +08:00
王奎兴 fa7db5df0c jobid查询接口; 2026-09-05 18:52:30 +08:00
王奎兴 cb1a1ec9a6 jobid查询接口; 2026-09-05 18:48:19 +08:00
王奎兴 3abac7b3d6 jobid查询接口; 2026-09-05 18:44:25 +08:00
秦鸿展 3c81e37e32 30 2026-09-05 18:39:15 +08:00
王奎兴 cecfe3e777 jobid查询接口; 2026-09-05 18:28:34 +08:00
王奎兴 104fc8bbef code 2026-09-05 18:19:25 +08:00
王奎兴 d9e5cede10 code 2026-09-05 17:59:18 +08:00
王奎兴 1689da1911 token 2026-09-05 17:51:37 +08:00
王奎兴 c043da88e4 groupid 2026-09-05 17:41:01 +08:00
王奎兴 d10a81fc42 自动添加xxl任务; 2026-09-05 17:37:33 +08:00
王奎兴 10bba6fd9e 物料仓租修改; 2026-09-05 16:59:46 +08:00
王奎兴 bc09b77695 添加计费字段; 2026-09-05 16:55:10 +08:00
秦鸿展 a7a387f215 南岐GPS接口对接增加字段 2026-09-05 16:29:38 +08:00
王奎兴 4d0ea8522e 发31 2026-09-05 15:52:37 +08:00
王奎兴 700eb3ee95 发30 2026-09-05 15:45:09 +08:00
王奎兴 fd62a7aff5 发31 2026-09-05 15:41:11 +08:00
王奎兴 b05c87e291 Merge branch 'dev' into dev-bms-260826
# Conflicts:
#	mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/BmsServiceFeign.java
#	mhd_oms/src/main/java/com/mhd/oms/domain/reservationStockInOrder/repository/facade/IReservationStockInOrderService.java
#	mhd_oms/src/main/java/com/mhd/oms/domain/reservationStockInOrder/repository/mapper/ReservationStockInOrderMapper.java
#	mhd_oms/src/main/resources/mapper/reservationStockInOrder/ReservationStockInOrderMapper.xml
#	mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/po/MaterialInventoryPO.java
#	mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/todo/MaterialInventoryDO.java
#	mhd_wms/src/main/java/com/mhd/wms/interfaces/dto/materialInventory/MaterialInventoryDTO.java
#	mhd_wms/src/main/resources/mapper/materialInventory/MaterialInventoryMapper.xml
2026-09-05 15:14:35 +08:00
王奎兴 7790850da3 自动生成job 2026-09-05 14:51:25 +08:00
秦鸿展 8ddf20f3e7 南岐GPS接口 2026-09-05 14:39:04 +08:00
秦鸿展 9e186bc034 南岐GPS接口 2026-09-05 14:38:49 +08:00
rcx c0065074d3 Merge branch 'dev-ty1.2' into dev 2026-09-05 13:53:01 +08:00
rcx be8ba45067 fix(oms,wms): 出库单下发WMS失败被静默吞掉,修复WMS出库拣货不显示OMS单据
- 建单/下发前统一校验(数量>0、已关联库存、WMS可用库存),错误汇总一次性提示
- 新增WMS库存校验接口,库存不足在OMS侧拦截不再推送WMS;拦截重复下发
- 检查WMS自动分配/生成拣货单返回值,失败报错并补偿清理WMS残留单据
- 下发状态逐单提交,避免批量部分失败造成两边状态撕裂
- 预约单多仓下发增加逐仓预校验
- 修复取消下发不删拣货单、无库存脏单无法取消两个历史问题
2026-09-05 13:42:02 +08:00
rcx c0bcedbdc3 fix(wms,oms): JSON日期反序列化兼容yyyy-MM-dd纯日期格式
- 库存查询日期只保留年月日后,前端回传纯日期(如 createTime=2026-07-06)触发
  @JsonFormat("yyyy-MM-dd HH:mm:ss") 严格格式反序列化 InvalidFormatException
- 新增 FlexibleDateDeserializer:字段 @JsonFormat 模式优先严格解析,失败后兼容
  yyyy-MM-dd HH:mm:ss / yyyy-MM-dd HH:mm / yyyy-MM-dd / 时间戳 / ISO-8601
- 仅在 OMS/WMS 通过 Module Bean 注册生效,不改动 BaseVOEntity 全局行为
2026-09-05 13:38:06 +08:00
王奎兴 84558fe2f6 xxl配置类; 2026-09-05 10:46:24 +08:00
秦鸿展 b46483263b 31 2026-09-05 10:36:12 +08:00
秦鸿展 4755d4e62b 30 2026-09-05 10:30:36 +08:00
王奎兴 3081460143 Merge branch 'dev' into dev_sfjk_0827 2026-09-05 10:23:32 +08:00
王奎兴 6c6afe4342 联查合同接口开发; 2026-09-05 10:22:24 +08:00
王奎兴 befdb82b53 联查合同接口开发; 2026-09-04 19:16:57 +08:00
王奎兴 5a329904c3 正式合同定时任务; 2026-09-04 15:40:19 +08:00
王奎兴 c25a9a5f68 发30 2026-09-04 14:06:48 +08:00
王奎兴 6b3912aae9 发30 2026-09-04 13:59:37 +08:00
王奎兴 12e43080b8 发31 2026-09-04 13:55:07 +08:00
王奎兴 77e0c77fbf 发31 2026-09-04 12:21:17 +08:00
王奎兴 03978d23d7 发30 2026-09-04 12:17:28 +08:00
王奎兴 ee7dbfcfb9 发30 2026-09-04 11:55:45 +08:00
王奎兴 94d05cca9c 发31 2026-09-04 11:52:53 +08:00
王奎兴 9fd3d34610 逻辑删除查询条件; 2026-09-04 11:51:10 +08:00
王奎兴 edb7a5343d 逻辑删除查询条件; 2026-09-04 11:37:06 +08:00
王奎兴 6108606daa bms改造; 2026-09-04 11:11:39 +08:00
rcx 762c49cd0f 发 测试 2026-09-04 11:10:20 +08:00
rcx 1b8511c092 feat(oms): 出库/入库业务单导入支持多页签——每个工作表独立生成一张单据
一、行为变更(ReservationStockInOrderImpl)
- 出库导入(parseExcelFile)/入库导入(parseStockInExcel):不再固定读单一页签,枚举Excel全部工作表,每个页签独立解析为一张单据(5个页签=5张单5个单号)
- 全部回滚策略:任一页签校验失败整体不落库,报错按页签聚合列出(页签[x]:原因;页签[y]:原因)
- 空页签(无货品/客户明细)自动跳过;单页签文件行为与原先完全一致
- 出库单号改到落库阶段逐张生成:多页签下校验阶段统一生成会因全部未入库而取到相同单号
- 入库模板OrderNO增加本文件内跨页签查重:各页签都未入库时库查重互相不可见,防两页签同OrderNO重复落库
- 页签解析异常message为空时兜底用异常类名,避免报错显示null
- 方法级@Transactional(rollbackFor=Exception.class):顺带修复旧入库导入因this自调用导致事务代理失效的问题

二、接口与返回结构(IReservationStockInOrderService / ReservationStockInOrderApi)
- 两个导入方法返回类型改为List<SheetImportResultDTO>(每页签一条:页签名/单号/物料行数/数量合计/解析数据)
- /parseExcel与/parse-excel返回:result=第一个页签解析数据(兼容老前端)、results=全部页签结果、msg=成功导入N张单:单号1、单号2…
- 入库接口不传sheetName为多页签模式,显式传页签名保持单页签老行为
2026-09-04 10:55:18 +08:00
rcx ce3bb036fa 发 测试 2026-09-04 10:50:48 +08:00
rcx 71515e74f5 Merge branch 'refs/heads/dev' into dev-ty1.2 2026-09-04 10:48:46 +08:00
rcx 7d3c229b9a feat(oms): 新增出仓委托单,支持新增/编辑/审核/下发WMS/完成入库
- 订单状态五态:1待审核(新增默认,驳回单编辑后自动回此态) 2已审核(下发后保持,用下发状态区分) 3已驳回 4进行中(预留) 5已入库
- 下发走WMS出库通知单链路(stockOutOrInInsert),返回强校验防吞错;完成入库限已审核+已下发
- 订单类型未传默认普通出库;明细新增单价unitPrice;列表支持invoiceNo/settlementCurrency查询,时间参数只传年月日
- 必填:客户/业务员/仓库/商品名称/申报数量;编辑删除限待审核/已驳回且未下发
- 附达梦建表脚本oms_out_entrust_order(主表+明细表,最终版)
2026-09-04 10:47:29 +08:00
rcx a549ac1507 发 31 2026-09-03 18:32:25 +08:00
rcx bf3f0a2bfe 发 30 2026-09-03 18:25:37 +08:00
rcx 35b026c8bd Merge branch 'refs/heads/dev-ty1.2' into dev 2026-09-03 18:23:32 +08:00
rcx 0eb47793a8 fix(库存): OMS/WMS库存查询入库时间/更新时间只保留年月日,去掉时分秒
- 入库时间(createTime)/更新时间(updateTime)继承自全系统基类 BaseVOEntity,默认序列化带时分秒;库存查询导出复用 /list 接口,前端取 JSON 值直接进 Excel,导出后无法在 Excel 里修改格式
- 两个库存 PO 覆盖 getCreateTime/getUpdateTime 并收窄 @JsonFormat 为 yyyy-MM-dd,仅影响 OMS/WMS 库存查询的显示与导出,不改动 BaseVOEntity 全局行为
- 页面列表/详情回显同步变为纯年月日;排序仍按原始时间值,逻辑不变
2026-09-03 18:00:52 +08:00
rcx fee891dad0 fix(oms): 下发出库单增加幂等拦截,已存在有效执行单时禁止重复下发
问题:单据下发过并取消后重新下发(或重复点下发),issueOrder 会再生成新执行单并原样复制业务明细uniqueId插入,撞执行明细表唯一约束(如CONS134279709)导致下发失败。
修复:issueOrder 生成执行单前先查同业务单号下是否已存在有效执行单(del_flag=1),存在则提示「已下发,请勿重复下发;如需重新下发请先取消下发」。
配套DB变更(单独由DBA执行):执行明细表唯一键CONS134279709改为函数唯一索引,仅对DEL_FLAG=1的行约束UNIQUE_ID唯一,软删行不再占用唯一键。
2026-09-03 17:44:16 +08:00
rcx 34496de969 发测试 2026-09-03 16:04:04 +08:00
rcx 7a3021f82a feat(wms,oms):出库排名统计改造为聚合排名——商品按物料合并/线路按线路+物料合并,按出库数量降序
- 商品tab:一行=一个货品(跨出库单汇总),返回 货号/货名/件数/毛重(吨)/体积(CBM)/退保总额(占位null),忽略线路筛选参数

- 线路tab:一行=线路×货品(按线路+物料编码合并),额外返回 线路编码/线路,仅统计带线路的单(排除line_code为空的历史单);商品/线路筛选参数均生效

- 单据级字段(种类/仓单编号/入库单号/客户编号/收货人/出仓日期)合并后无单值可对应,不再返回

- 件数=SUM(CHECK_QUANTITY)复核确认的实际出库量(与入库排名实际上架量SHELVES_QUANTITY对称),按件数降序,编码次之

- 合计行:件数/毛重吨/体积 全量合计;商品tab含totalOrderCount(去重出库单数),线路tab含totalOrderCount+totalLineCount(去重线路数)

- 数据源不变:WMS查本库出库表,OMS跨库查NGWL_TEST_WMS同名表,两边数字一致;接口路径/传参不变
2026-09-03 16:01:17 +08:00
rcx c9c3062789 feat(wms,oms):入库排名统计改为按物料编码合并(一行=一个物料在区间内全部已入库仓单的合计):返回列裁剪为物料编码/货名/规格型号/单位/件数(实际上架量)/毛重(吨)/投保总额(货品档案保额取MAX不跨仓单累加)/体积;仓单编号/客户编号/存货人/入仓日期为单据级字段单值失真,移出返回列仅保留为查询条件;HAVING过滤0件行;合计行投保总额=各物料保额之和(每物料计一次),totalOrderCount=涉及仓单数;OMS/WMS两端口径数字一致;查询参数与返回结构外层不变 2026-09-03 15:57:44 +08:00
rcx 4ad31a870a fix(oms): 物料解析防张冠李戴+导入三类校验合并报错,客户编码只认USER_MEMBER_CODE
一、出库业务单导入(ReservationStockInOrderImpl)
- 物料不存在/客户不存在/批次库存不足三类校验全部收集完成后统一报错,不再分阶段中断
- materialCache 取用统一 trim,防 Excel 编号带空格导致校验被跳过

二、物料三级解析防张冠李戴(MaterialBaseInfoResolveService)
- baseId 按主键查到的档案与明细编码不一致时不再信任 baseId,转库存ID反查/编码+组织兜底,两级落空才退回按ID档案,命中打WARN日志
- 背景:前端把库存ID传进 materialBaseInfoId,撞上编码=名称的脏档案主键后整行物料被覆盖成错误物料

三、客户编码口径(reservationStockOutOrder/ReservationStockOutOrderMapper.xml)
- queryCustomerByCode 只认 USER.USER_MEMBER_CODE,不再放行 USER_SHIPPER.CUSTOMER_NC_CODE

四、明细查询补字段(reserveStockOutOrder/ReserveStockOutOrderMapper.xml)
- resultMap 补 status/issue_status 映射,detailsList 查询补查 a.status, a.issue_status
2026-09-03 15:52:18 +08:00
秦鸿展 9447604c96 31 2026-09-03 12:37:33 +08:00
秦鸿展 1577eddc7b 30 2026-09-03 11:49:38 +08:00
秦鸿展 f5300fdd5d BI查询YMS月台管理列表查询开发接口 2026-09-03 11:39:13 +08:00
秦鸿展 3880755df0 BI查询YMS月台管理列表查询开发接口 2026-09-03 11:35:33 +08:00
秦鸿展 28bf58e8d1 BI查询YMS月台管理列表查询开发接口 2026-09-03 11:28:22 +08:00
秦鸿展 3a5af634dd 30 2026-09-03 00:29:18 +08:00
秦鸿展 4f45a73505 已登录账号APP菜单问题 2026-09-03 00:26:45 +08:00
秦鸿展 b5f710cd33 31 2026-09-02 20:49:09 +08:00
秦鸿展 bfffc6bbf1 App游客时正常展示消息页面 2026-09-02 20:46:31 +08:00
秦鸿展 a8815ade02 App游客时正常展示页面 2026-09-02 20:28:59 +08:00
rcx 57312753ed 发 30 2026-09-02 19:52:47 +08:00
rcx 03ce38ed9b 发 30 2026-09-02 19:46:44 +08:00
rcx b09b07978f fix(oms): 出库导入批次分配优化:批次号优先排序、移除归属校验、库存不足统一报错
c1f483f0 出库导入指定仓库功能的迭代调整:

1. 移除物料仓库归属前置校验(countInventoryByWarehouse):物料档案全仓共享、
   不属于任何仓库,仓库约束收敛到批次分配环节——分不到当前仓批次即报库存不足
2. getkcId 增加分配排序:同物料同仓多条可用库存时,有批次号的优先、空批次
   沉底、同组按库存ID(入库先后);此前无排序随机取第一条,会分到无批次号的
   历史脏数据记录
3. 明细仓库编码/名称兜底:部分历史库存记录只存仓库ID未存编码/名称,按ID
   反查仓库表补齐,修复明细仓库只显示ID不显示名称
4. 库存校验改收集式:全部物料批次比较完成后统一报出不足物料清单,原为逐行
   分配时立即抛错,首个不足即中断、后续物料不参与比较
2026-09-02 19:43:48 +08:00
rcx 87dae51a15 fix(bms): 发票列表兼容pageNum/pageNo参数名,修复翻页始终返回第一页;排序补id兜底防翻页重复 2026-09-02 19:31:01 +08:00
rcx 49ab26a8e9 fix(bms): 发票列表条数显示为0,注册MyBatis-Plus分页拦截器使Page生效 2026-09-02 18:33:50 +08:00
rcx c1f483f0a0 feat(oms): 出库导入支持指定仓库,校验物料库存归属并按仓过滤批次库存
背景:物料档案全仓共享,此前导入不区分仓库,批次库存"查到哪条算哪条",单据可能取到其他仓库的库存且主表仓库为空。

1. /parseExcel 新增可选参数 warehouseId(前端随文件提交当前页面仓库;不传保持原有行为)
2. 新增 countInventoryByWarehouse 归属校验:物料在指定仓库无库存记录时统一报错"物料[xx]在当前仓库[名]没有库存记录,无法从该仓库出库"
3. getkcId 批次库存查询增加仓库过滤,明细批次/仓库/入库单号均取当前仓库
4. 导入单主表回填当前仓库 warehouseId/Code/Name(此前导入单仓库为空)
5. 出库导入两个入口(reservationStockInOrderApi/executionStockInOrderApi 的 parseExcel)同步支持
6. 库存不足报错补充物料编码,便于定位
2026-09-02 18:32:12 +08:00
rcx 6d7e42bcde 发 31 2026-09-02 16:55:19 +08:00
rcx 61572c536a 发 30 2026-09-02 16:48:35 +08:00
rcx 572b25ee88 feat(oms): 运输业务单列表新增预计起运时间范围查询条件(layDateStart/layDateEnd) 2026-09-02 16:16:46 +08:00
rcx 3576d734af fix(oms): 出库业务单导入发票编号不落库,补齐繁简归一化「發/額」映射
模板列头「發票編號/金額/發票張數」归一化时映射表缺「發→发」「額→额」,發票編號列识别失败(invoiceNoCol=-1),客户配送明细落库时发票编号为空、查看不回显,金额列与发票张数同样受影响。补齐映射并在关键列缺失时输出告警日志;历史已导入单据需重新导入。
2026-09-02 15:43:07 +08:00
rcx 028af3dd2f fix(wms,oms):出库排名件数改为复核确认的实际出库量(CHECK_QUANTITY),排序同步调整
- 件数取数由明细 PIECE_COUNT(计划/申报件数)改为 SUM(CHECK_QUANTITY) 复核确认的实际出库量,与入库排名的实际上架量 SHELVES_QUANTITY 取数逻辑对称(已验证现有已出库数据 222/222 全覆盖)

- 排序第一键由出库数量(outbound_quantity,下发时的计划复制值)改为件数(实际出库量)降序,出仓日期/单号次之

- 接口路径/传参/返回字段结构均不变,前端无感;其余为注释同步
2026-09-02 15:41:51 +08:00
rcx 3af78d5d7b feat(wms,oms):出库排名统计改造为仓单×货品明细列表,按出库数量降序排名
- 字段:种类/仓单编号/入库单号/客户编号/收货人/货名/线路/件数/毛重(吨)/体积(CBM)/退保总额(占位null,数据来源待确认)/出仓日期

- 商品/线路各一套接口,路径名不变;商品接口忽略线路筛选参数,线路接口忽略商品筛选参数;含合计行(件数/毛重吨/体积/仓单数)与Excel导出

- 统计口径不变:出库单创建时间区间+已出库状态(9,12,13)+明细level=1主行;排序按出库数量(销售量)降序,件数/出仓日期/单号次之

- 数据源:WMS查本库出库表;OMS跨库查NGWL_TEST_WMS同名表(OMS库出库单状态不流转,跨库与WMS端数字一致)

- 删除原按商品/线路聚合的排名接口(销售量/交易数/金额字段)
2026-09-02 15:25:51 +08:00
rcx e821619a96 fix(库存): 修复OMS/WMS库存查询过期日期不回显
根因:业务只在OMS填写饮用日期,且下发DTO无过期日期字段,饮用日期→过期日期的转换仅存在于收货单生成瞬间,后续环节全部丢失。

- InMaterialDetail(DTO) 补充 expiryDate 字段,打通OMS→WMS传递
- 预约单/执行单下发前:过期日期为空时用饮用日期补齐(治本)
- WMS收货批次属性回写:恢复饮用日期回写并兜底转换为过期日期(加固)
- WMS上架确认:过期日期为空时从收货明细兜底补齐(加固)
2026-09-02 15:05:29 +08:00
rcx 434348f972 fix(oms,wms): 出库单下发WMS失败被静默吞掉,修复WMS出库拣货不显示OMS单据
- OMS建单/下发前校验明细数量>0且已关联库存
- OMS检查WMS自动分配/生成拣货单返回值,失败即报错回滚
- WMS自动分配各类失败场景抛带单号的明确异常,替代NPE
- WMS接单幂等保护,失败重试不产生重复数据
2026-09-02 14:32:13 +08:00
rcx 72cb1d8c07 feat(wms): 库存列表接口新增隐藏可用数量为0的参数
/materialInventoryApi/list 新增可选参数 excludeZeroAllocationQuantity,为 true 时排除可用数量=0 的库存行;与已有 excludeZeroInventoryQuantity 组成两个独立开关,不传时保持原有行为。配合盘点-库存选择页隐藏数量为0物料及出库选库存隐藏0库存需求。
2026-09-02 14:04:15 +08:00
rcx f5a7c8c1f3 发 31 2026-09-02 00:12:59 +08:00
rcx 3989d75f05 发 30 2026-09-01 23:50:34 +08:00
rcx 471b515a45 Merge branch 'refs/heads/dev-ty1.2' into dev
# Conflicts:
#	mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/BmsServiceFeign.java
#	mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/OmsServiceFeign.java
#	mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/WlhyServiceFeign.java
#	mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/WmsServiceFeign.java
#	mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/YmsServiceFeign.java
#	mhd-common/mhd-common-core/src/main/java/com/mhd/common/core/feign/ThirdPartyServiceFeign.java
#	mhd_oms/src/main/java/com/mhd/oms/infrastructure/feign/ThirdPartyServiceFeign.java
#	mhd_oms/src/main/resources/bootstrap.yml
2026-09-01 23:49:08 +08:00
rcx 19e897cae0 发 30 2026-09-01 23:02:30 +08:00
rcx 91bdb99ec2 发 31 2026-09-01 22:50:50 +08:00
rcx 004580e252 发 30 2026-09-01 22:36:10 +08:00
rcx da3c58a366 fix(wms,oms):入库排名统计口径修复:件数改为实际上架量SUM(SHELVES_QUANTITY)(原PIECE_COUNT为建档申报值不随收货上架变化);OMS端数据源跨库改查WMS入库表NGWL_TEST_WMS.stock_in_order/in_material_detail(OMS执行单状态不回传且上架量回填代码未启用,与出库排名1300a4e1跨库修法一致),状态恢复为已入库(5);入仓日期为空兜底建档时间;OMS与WMS两端数字一致;接口路径/参数/返回结构不变 2026-09-01 21:36:33 +08:00
赵辉 1cec762930 user30 2026-09-01 17:35:21 +08:00
赵辉 4deaeaa89d user30 2026-09-01 17:22:26 +08:00
赵辉 1208e02fe7 yms租户同步 2026-09-01 17:14:23 +08:00
王奎兴 2c60dc8f84 bms改造; 2026-09-01 16:58:39 +08:00
秦鸿展 77fb71dab4 30 2026-09-01 16:32:33 +08:00
秦鸿展 cd41c616bb 31 2026-09-01 16:30:23 +08:00
rcx a4f27ab643 feat(oms,wms):库存查询导出新增isExport标识与融合排序 /list 新增可选参数 isExport 标识导出请求(导出显示商品饮用有效期/物料分类/进货日期由查询结果直接返回);isExport=true 时所有查询维度(OMS 6条/WMS 8条)统一启用导出融合排序:到期日倒序空值最后(保留原 orderByExpiryDate 第一优先级,融合不取代)→同到期日内按物料编码升序→0库存靠后→更新时间倒序→MAX(库存ID)兜底保证分页稳定;各语句挂载公共片段 exportOrderByMaterialCode,不传 isExport 保持原有排序;OMS Controller 在 isExport=true 时跳过 Java 按更新时间重排避免覆盖 SQL 导出排序 2026-09-01 16:29:41 +08:00
秦鸿展 c174ea8742 单号编辑变化问题,以及组织查询问题 2026-09-01 15:15:53 +08:00
秦鸿展 2bbd6784c4 31 2026-09-01 14:14:51 +08:00
秦鸿展 923afda21c 大屏月台接口查询修改 2026-09-01 14:09:23 +08:00
rcx bb3ca932d2 feat(oms):出入库导入存在性校验改为收集后统一提示(物料+客户不存在的编号一条消息带全)
调整2d1e4b59引入的校验流程(入库parseStockInExcel/出库parseExcelFile统一):
1. 第一步:物料编号存在性校验——逐编号查库,全部不存在的收集到missingMaterials,不立即报错
2. 第二步:客户编号存在性校验——逐编号查库,全部不存在的收集到missingCustomers,不立即报错
3. 第三步:两步全部完成后统一抛出提示,避免修完物料问题重导一次才暴露客户问题:
   只缺物料:"物料[A、B]不存在,请先维护物料信息,维护完成后重新导入"
   只缺客户:"客户[C]不存在,请先维护客户信息,维护完成后重新导入"
   都缺:"物料[A、B]不存在,请先维护物料信息;客户[C、D]不存在,请先维护客户信息,维护完成后重新导入"
4. 存在性全部通过后才继续多货主/多客户编号/客户与货主一致性等校验及建单;
   入库"多个客户编号"校验相应移到存在性之后,多个编号中缺哪个报哪个
5. 入库客户查询结果存入customerCache复用,一致性校验不再二次查库

漏填编号(空白行)仍即时报错,不属于存在性问题,行为不变
2026-09-01 09:53:51 +08:00
rcx a588264849 fix(wms,oms):入库排名统计查询恒为空修复:OMS执行入库单状态不回传(全停status=1)无法按已入库过滤,改为仅排除已取消(6)/已关闭(7)(与入库明细查询页口径一致);入仓日期为空的历史单筛选与展示均兜底建档时间create_time(与出库明细统计口径一致);WMS仍保持仅已入库(5) 2026-09-01 09:24:20 +08:00
rcx 1300a4e1f8 fix(oms):出库排名OMS端数据源改为跨库查WMS出库表,修复查询结果恒为空
OMS库出库单状态从不流转(预约/执行单全部status=1,无"已出库"数据),原SQL按执行出库单status=9过滤导致查不出任何数据;改为跨库取WMS出库表NGWL_TEST_WMS.stock_out_order/out_material_detail(与OMS库存查询跨库join material_base_info同一模式),口径与WMS端/outboundRankApi完全一致:已出库及之后状态(9,12,13)+明细level=1主行+按出库单创建时间筛选,两边页面数字一致;接口路径/参数/返回结构均不变,其余文件仅更新注释中的数据源说明
2026-09-01 09:10:59 +08:00
rcx bf96f5f005 feat(wms,oms):库存管理新增入库排名统计(按仓单+货品汇总,件数降序,与出库排名tab配对);还原出库排名第一版源码
入库排名统计(新增):WMS /inboundRankApi、OMS /executionInboundRankApi 的 queryMaterialRankList 分页+合计、exportMaterialRank 导出;口径:仅已入库(status=5),明细仅level=1主行,日期区间按入仓日期warehouse_date筛选(必填);一行=仓单内某货品合计(GROUP BY 仓单+货品 ORDER BY 件数DESC);字段:入仓日期/仓单编号/客户编号/存货人/货名/件数/毛重(吨=KG/1000)/投保总额(货品档案保额,组内MAX防重复累加)/体积(CBM)+货号/规格/单位;合计行:totalPieceCount/totalGrossWeightTon/totalInsureAmount/totalVolume/totalOrderCount;登录上下文:日期必填校验+topOrganizationId租户隔离,WMS另注入关联仓库warehouseId。出库排名统计(还原第一版):/outboundRankApi、/executionOutboundRankApi,按货品/线路汇总排名
2026-08-31 17:47:09 +08:00
rcx a983ae639e fix(wms,oms):物料/过期日期查询补查入库日期(create_time)与更新时间,分组页签入库日期显示为空 2026-08-31 15:59:54 +08:00
rcx a74afd837c feat(oms):入库业务单新增页面新增按物料编号查询库存批次最近5条过期日期接口 /reservationMaterialInventoryApi/queryRecentExpiryDates/{materialCode},按物料编号关联 NGWL_TEST_WMS.material_base_info 查 reservation_material_inventory 库存批次,返回过期日期+批次号(date_format 按天去重、到期日倒序、limit 5),供前端下拉回显(也可点日期图标手动选择);非平台租户按登录用户一级组织 top_organization_id 过滤(与 /list 一致);沿用 Api→ApplicationService→DomainService→Facade→Impl→Mapper 分层,新增响应对象 MaterialInventoryExpiryBatchPO(expiryDate/batchNumber) 2026-08-31 15:22:59 +08:00
rcx bc3afbacb1 feat(wms,oms):物料分类:库存列表返回增加 materialClassifyCode/Name 显式映射
(ListTypeHandler,数据关联 material_base_info 带出)
2026-08-31 13:58:02 +08:00
rcx 5ba6a3b73d 发测试 2026-08-31 13:43:11 +08:00
rcx da84b7bb69 feat(wms,oms):库存管理-库存查询新增进货日期、入库日期查询条件,新增进货日期字段贯通收货-上架-库存全链路
需求:OMS/WMS 库存管理-库存查询页增加查询条件:物料分类(后端已支持,无需改动)、
进货日期(新增字段)、入库日期。

一、DDL(sql/20260831_wms_oms_add_purchase_date.sql)
    WMS 收货明细、上架明细、物料库存及 OMS 预留库存共 4 张表新增 PURCHASE_DATE DATE 列,
    附批次明细配置模板、执行前检查与回滚语句;须先执行 DDL 再发布服务。

二、进货日期录入与落库链路(收货→上架→WMS库存→OMS库存)
    1、录入入口为收货批次属性「进货日期」标签(批次规则配置,标签须一字不差),
       ReceiptMaterialDetailImpl 两处标签解析新增分支;
    2、收货明细→上架明细:StockReceiptOrderDomainService 6 处日期拷贝点补 setPurchaseDate(含子项);
    3、上架落库:stockIn/sj 赋值写入 material_inventory;
    4、同步 OMS:MaterialInventoryOmsParamDO 新增字段经 Feign omsEdit 传递,
       写入 reservation_material_inventory;
    5、库存行合并键:批次规则勾选后进货日期参与分行(getParam 映射 + selectListByWhere 全等匹配),
       OMS omsEdit 行匹配同步按进货日期区分,避免不同进货日期误合并到同一行。

三、查询条件(两模块 /list 接口)
    1、进货日期:purchaseDate(单日精确)+ purchaseDateStart/purchaseDateEnd(区间,End 含当天);
    2、入库日期:WMS 新增 createTimeStart/createTimeEnd(按库存行创建时间过滤,与 OMS 现有口径一致);
    3、列表/导出返回新增 purchaseDate 字段,分组查询取 MAX(purchase_date)。

说明:历史库存进货日期为空,按进货日期查询查不到存量数据(无来源可回填),区间查询会排除空值行。
2026-08-31 13:41:19 +08:00
rcx 2d1e4b5912 feat(oms):出入库导入按编号自动匹配名称并校验客户/物料存在性,模板列位改为动态识别
需求:①入库业务单导入模板只填物料编号+客户编号,物料名称/客户名称按编号自动匹配库中数据
②③出库导入校验客户/物料是否已存在,不存在提示"进行维护" ④库中查询结果为空(确无数据)时报错

一、入库导入(ReservationStockInOrderImpl.parseStockInExcel):
1. 物料逐编号查库,缺失料号收集后一次性报错"物料[xx]不存在,请先维护物料信息后再导入"
2. 明细新增客户编号:去重后查库,为空报错"客户[编号]不存在,请先维护客户信息后再导入";
   查到则以该客户为单据货主并自动匹配名称;与物料归属货主不一致时拦截并提示两者编号
3. 明细回填库中物料名称;未填客户编号时维持原"货主取自物料"逻辑,兼容老模板

二、出库导入(parseExcelFile):
1. 物料/客户校验前移到落库前(原为先落库后查客户,报错会留半截数据)
2. 物料查不到收集后报错维护提示;客户查不到报错维护提示,客户ID/名称改取库中匹配值
  (替换仅返回id且查不到时静默存null的queryShipperId)
3. 產品編號/客户編号漏填的行直接报错提示补全

三、模板解析改为按列头文字动态定位列(繁简体归一化),列/行顺序调整不影响解析,
   识别不到时回落老模板固定列位,老模板行为不变:
1. StockInExcelListener(入库):以產品編號为锚点识别列头行,新增客戶編號列支持
2. DynamicDeliveryExcelListener(出库):左右分栏各自锚点识别;送货日期/线号/车次/
   发票张数/小计/纪录合共改按标签文字定位,行号列号不再写死

四、Mapper:ReservationStockOutOrderMapper新增queryCustomerByCode(按组织查询,
   USER_MEMBER_CODE或USER_SHIPPER.CUSTOMER_NC_CODE任一命中),删除queryShipperId
2026-08-31 11:15:41 +08:00
rcx 2781f0d8d9 feat(oms):库存查询导出新增库存为0是否导出参数 /reservationMaterialInventoryApi/list 新增可选参数 excludeZeroInventoryQuantity,前端"库存为0是否导出"选"是"时传 true,SQL 以 HAVING IFNULL(SUM(inventory_quantity),0)>0 排除汇总库存为0的数据;所有查询维度(all/hz/wl/kw/wlkw/wlExpiry)共6条查询在 GROUP BY 后统一挂载公共片段 excludeZeroInventoryHaving,与 sort_order 对0库存的定义保持一致;不传参数时 SQL 不拼接条件,行为与原来完全一致 2026-08-31 10:17:53 +08:00
王奎兴 8a9bbb3cef 本地配置 2026-08-31 09:33:58 +08:00
rcx 155f1866b4 feat(system):库区管理新增库区面积合计查询接口
新增 GET /storageSectionApi/sumStorageRegion,与库区列表 /list 相同筛选条件及组织数据权限,
SQL 端 IFNULL(SUM(storage_region),0) 返回面积合计(平方米),无数据返回0;
抽取 queryList 数据权限为 applyQueryDataPermission 供列表与合计共用,/list 行为不变。
2026-08-28 18:01:44 +08:00
王奎兴 d4446e8482 发30 2026-08-28 17:35:43 +08:00
王奎兴 3349552e10 结算对象改造; 2026-08-28 17:32:02 +08:00
秦鸿展 254a804a17 30 2026-08-28 17:10:05 +08:00
秦鸿展 4c4b8f4e29 大屏月台接口只查询预约日期是当天的 2026-08-28 17:06:28 +08:00
zhaoqing 06cf5ce861 fix(oms):明细表修改 2026-08-28 16:22:28 +08:00
rcx 7a01315ca8 feat(sys):导出字段优化与前端显示保持一致 2026-08-28 16:17:48 +08:00
rcx d0773a40d2 feat(transport):基础数据-运营数据-车辆管理增加导出功能
新增 /vehicle/export 导出接口(GET/POST,兼容前端 download 工具),
复用列表查询条件与数据权限,不分页导出全量数据为 xlsx;
新增 VehicleExportVO 导出对象(40列,状态字段转中文)。
2026-08-28 16:12:56 +08:00
rcx 6349181cc3 feat(sys):费用科目导出增加新vo对象
新增 ExpenseAccountExportVO 导出对象(27列业务字段)
2026-08-28 09:20:11 +08:00
rcx e95feb5c24 Merge branch 'refs/heads/dev' into dev-ty1.2 2026-08-28 09:15:39 +08:00
rcx afa502eeaf feat(system):仓库管理-库位管理新增复制功能
需求:库位管理列表增加复制功能,快速复用已有库位配置新建库位。
实现(仿照bms复制账单copyBill的回显式复制):
一、新增接口 POST /storageLocationApi/copyStorageLocation(StorageLocationApi):
    id判空+try-catch兜底,异常返回"复制库单失败:xxx"
二、StorageLocationApplicationService新增copyStorageLocation:
    取原库位PO后清空唯一性字段(storageLocationId不清空会误走修改分支覆盖原库位、
    storageLocationCode同组织同仓库唯一须用户重填、locationBarCode物理唯一)、
    清空审计字段(提交时Assembler按登录人重写)、状态重置初始值
    (status=1空/openingUp=1启用/nullify=1未作废);其余业务字段保留回显
三、前端拿PO回显到新增页,用户改编码后走现有edit接口(id为空即新增)提交,
    天然复用编码唯一性校验、仓库/库区/库位组回填、库位组计数+1,Domain层零改动
2026-08-28 09:14:03 +08:00
rcx 627376da07 feat(sys):费用科目增加复制、导出功能
1、新增 POST /expenseAccountApi/copy 复制接口(回显式,清空主键及科目编码)
2、新增 GET/POST /expenseAccountApi/export 导出接口(按列表查询条件+数据权限全量导出)
3、新增 ExpenseAccountExportVO 导出对象(27列业务字段)
2026-08-28 09:13:10 +08:00
rcx 5127123236 发31 2026-08-27 16:38:33 +08:00
rcx 7b0d08a132 发30 2026-08-27 16:33:43 +08:00
zhaoqing ccdbbda165 Merge remote-tracking branch 'origin/dev' into dev-ty1.2 2026-08-27 14:27:16 +08:00
zhaoqing e3058d8eb1 fix(oms):明细表修改 2026-08-27 14:24:58 +08:00
rcx 5e3a0d86d0 发测试 2026-08-27 14:21:23 +08:00
rcx 76d95c1b2d feat(oms):入库业务单导入模板OrderNO改为只作业务单号,去掉格式校验并按业务单号查重
需求:导入模板表格OrderNO值回显到字段"业务单号";"入库业务单号"仍由系统
原本逻辑生成;业务单号重复不允许导入并提示"业务单号已存在";模板未填单号
保持现有系统逻辑;业务单号去掉规则校验。
一、导入流程(ReservationStockInOrderImpl.parseStockInExcel):
   1. 模板OrderNO只写入业务单号(business_order_no),不再同时作为入库业务单号
   2. 删除validateTemplateOrderNo格式规则校验(R+年月日+3位序号),模板单号不再限格式
   3. 查重由按in_order_number跨两表改为按business_order_no查入库业务单表,
      Mapper新增existsByBusinessOrderNo;重复时报"业务单号已存在:xxx,请修改
      模板中的OrderNO后重新导入"
   4. 入库业务单号(in_order_number)统一由insertDomain按原逻辑生成
      (R+年月日+当日累加,跨reservation/reserve两表取号防撞)
二、insertDomain业务单号赋值改为仅在为空时默认=入库单号,导入带模板单号时不覆盖;
   模板未填单号保持现有逻辑。页面手工新增(DomainService.insert)与客户预约下发
   (ReserveStockInOrderDomainService)为独立链路,不受影响。
2026-08-27 14:10:38 +08:00
秦鸿展 e4b29858a4 南岐GPS三方接口对接 2026-08-27 13:58:10 +08:00
rcx f1dec2298d 发 31 2026-08-26 18:04:44 +08:00
rcx 7b52764ae1 发 30 2026-08-26 17:52:23 +08:00
rcx 1006860698 Merge remote-tracking branch 'origin/dev' into dev-ty1.2 2026-08-26 17:32:40 +08:00
王奎兴 1e00681289 恢复
xxl-job
2026-08-26 17:28:50 +08:00
rcx 353300b643 feat(wms):出入库导入增加料号与规格型号/SKU码一致性校验
- 按料号查物料档案后逐项比对,任何一项不符直接报对应错误:规格型号/SKU码必须与档案完全一致(trim后比较)
- Excel留空而档案有值、或Excel填值而档案为空,均视为不一致报错(提示Excel:空/档案:空)
- SKU码以档案条码(bar_code)为比对基准;出库料号已填写但货主下查不到时直接报错,不再被SKU码/商品名称静默兜底(与入库对齐
2026-08-26 16:56:27 +08:00
rcx 9eb2f347f7 发测试 2026-08-26 15:55:03 +08:00
rcx 182438e1dc fix(system): 费用科目getInfoByOrgAndCode计费单位中英文均为null时过滤,避免返回null-null 2026-08-26 15:49:58 +08:00
rcx 0be73e9bfe fix(oms): 出入库联合列表(inAndOutList)查询条件修复与补全
1. 修复 ReflectionException:mapper XML <if> 引用 inOrOut/orderNumber 但 DO 无对应字段,
   DO/DTO 新增 inOrOut、orderNumber 查询字段(BeanUtils 同名自动拷贝,前端参数直达 mapper)
2. inOrOut 排除空串与 'all'(前端"全部"约定值),避免拼出 ="all" 查空
3. orderNumber 排除空串并改为 like 模糊匹配(与出库单列表单号搜索口径一致)
4. status 新增精确筛选(0 为有效条件值,按前端约定直传,"全部"时不传)
2026-08-26 15:49:38 +08:00
秦鸿展 5e369bdd99 31 2026-08-26 14:23:36 +08:00
王奎兴 a5a222a78e Merge branch 'dev' into dev-ty1.2
# Conflicts:
#	mhd-modules/mhd-system/src/main/resources/bootstrap.yml
2026-08-26 14:11:45 +08:00
王奎兴 b1840c8965 Merge remote-tracking branch 'origin/dev' into dev 2026-08-26 14:08:38 +08:00
王奎兴 02a0b7aab6 xxl-job 2026-08-26 14:08:24 +08:00
rcx 42ab5160ed 发30 2026-08-26 14:03:42 +08:00
秦鸿展 df66287455 31 2026-08-26 12:55:44 +08:00
秦鸿展 e2ce186aba 30 2026-08-26 12:49:16 +08:00
秦鸿展 9be0de0ae7 园区月台预约车辆显示修改 2026-08-26 12:48:06 +08:00
rcx 4410162f39 发测试 2026-08-26 10:03:46 +08:00
rcx 3060fcd6a4 feat(product):新增附件原始文件名字段SQL并去掉上传错的文件 2026-08-26 09:53:19 +08:00
rcx 7d8e1c3872 feat(wms):复核详情增加合计数;库存分配新增通宇专用可分配库存查询(物料+箱号/卡板+Sheet Ref匹配
1. 复核详情(getInfo/getInfoReviewOrderNumber)头表回填totalReviewedQty/totalPlanQty/materialKindCount,
   口径与列表queryList聚合一致(计划量=Σifnull(material_plan_qty,pending+reviewed))
2. 新增 GET /materialInventoryApi/assignableList,按通宇账号分流(TongyuOrgUtil:组织ID=2830或组织名含"通宇"):
   - 通宇:启用新匹配。双模式入参——传outOrderId+materialDetailId(须成对)则匹配值全部后端自取出库单/明细;
     不传则按平铺参数(materialBaseInfoId/materialCode+boxPalletNo+sheetRefNo,多值串自动拆token OR-LIKE)。
     固定条件:del_flag=1且可分配数量(allocation_quantity)>0,不分页全量返回,不套用用户组织数据权限
   - 非通宇:保持原/list行为,内部直调list();进原查询前清除materialDetailId(新接口专用参数,防泄入
     原查询的a.material_detail_id=上架单明细ID条件导致查空)
3. autoAssign自动分配同步分流:通宇走token匹配,非通宇保持原eq精确匹配不变
4. 新增util/BatchNoTokenUtil(批次属性多值拆分)、util/TongyuOrgUtil;库存批次属性回填抽为
   fillBatchAttributes供/list与可分配查询复用,原逻辑行为不变
2026-08-26 09:30:01 +08:00
秦鸿展 59249be9b0 31 2026-08-26 00:06:41 +08:00
秦鸿展 8fe5e079dd 新增月台预约楼层车辆查询 2026-08-26 00:01:40 +08:00
秦鸿展 d1d55ce52a 30 2026-08-25 23:00:55 +08:00
秦鸿展 b12517e5b5 30 2026-08-25 22:28:57 +08:00
秦鸿展 09d24217b5 统计业务类型 2026-08-25 22:25:12 +08:00
秦鸿展 3c2f252347 统计业务类型 2026-08-25 22:23:18 +08:00
秦鸿展 940e4c56b0 31 2026-08-25 20:24:10 +08:00
秦鸿展 09ab103d1d 30 2026-08-25 20:18:31 +08:00
秦鸿展 8dcb2334d5 BI月台数据改造 2026-08-25 20:11:52 +08:00
rcx 7a5e124f98 fix(oms): 入库模板导入优化异常提示信息 2026-08-25 17:51:54 +08:00
rcx c5a2508a36 发测试 2026-08-25 16:29:15 +08:00
rcx 7353021d92 feat(oms):入库模板导入支持模板自带业务单号,重复拦截并按R+年月日规则生成
1. 模板OrderNO解析兼容无标签新模板(E1直接为单号值)
2. 导入单号优先模板自带:先校验R+年月日+3位序号格式(含年月日有效性),
   再跨reservation/reserve两表查重,重复提示"入库业务单号已存在"
3. 模板无单号时按R+年月日+当日累加生成(insertDomain不再无条件覆盖已设置单号,清理无效的仓库规则生成死代码)
2026-08-25 16:11:19 +08:00
rcx 735cf9af9f feat(oms):入库导入模板饮用日期解析修复,入库单列表联查带出饮用有效日期
1. 入库/执行入库Excel解析监听器:扩充日期格式支持(yyyy年M月d日、d/M/yyyy、M/d/yyyy、dd.MM.yyyy等16种),
   改为严格解析防止宽松解析产生公元1~99年脏数据,新增Excel日期序列号兜底及年份合理性校验,
   解析失败抛出带行号的明确提示而非静默置空
2. parse-excel接口异常时返回具体错误信息(原为空body 500)
3. 入库单列表queryList联查明细表取最早饮用日期(MIN drink_date),PO及resultMap补drinkDate映射
2026-08-25 15:07:04 +08:00
秦鸿展 f2f3fb9c19 30 2026-08-25 11:09:23 +08:00
秦鸿展 a62ed7c60f 31 2026-08-25 11:05:06 +08:00
王奎兴 4168f921f6 查询nc日志排序修改; 2026-08-25 10:52:28 +08:00
王奎兴 1916152775 查询委托方去除组织条件; 2026-08-25 10:41:58 +08:00
rcx 83f4cce451 feat(oms):App端出入库明细跳详情补入库按单号查询,预约单修改限待提交状态
需求:入库/出库明细列表点击跳详情;待提交预约单可修改(暂存/提交均走/edit,与PC共用链路)
1.ReserveStockInOrderApi新增getInfoByInOrderNumber按单号查详情:
  入库明细行只带inOrderNumber(明细表无in_order_id列),对齐出库侧getInfoOutOrderNumber;
  del_flag=1过滤已删单+getOne不抛单号重复
2.ReserveStockInOrderMapper.xml MaterialDetailResult显式映射in_order_number,
  明细行单号不再依赖驼峰自动映射开关(出库侧:363本就有,入库侧补齐)
3.入库/出库DomainService.update加状态闸门:仅status=1(已创建)且issueStatus!=3(未确认)允许修改,
  防已确认下发/已流转单据被改写(仓码确认只置issueStatus=3不改status,原校验缺此维度必然漏防);
  明细为空改报'请添加货物明细'(原getMaterialDetailList().size()空指针500);出库update补@Transactional
2026-08-24 18:12:50 +08:00
王奎兴 f5f792d3f8 发31 2026-08-24 17:03:55 +08:00
王奎兴 dbd61c0519 发30 2026-08-24 16:58:26 +08:00
王奎兴 cb5d933e0a 渗透测试修改; 2026-08-24 16:53:20 +08:00
王奎兴 5298e9d4e3 发30 2026-08-22 19:35:56 +08:00
王奎兴 06f61d7957 发31 2026-08-22 19:32:52 +08:00
王奎兴 4567f00858 发测试 2026-08-22 18:46:36 +08:00
rcx e8328b0159 Merge remote-tracking branch 'origin/dev' into dev 2026-08-22 18:39:12 +08:00
rcx bb3dfaed4a fix(oms):出库业务单线路/车次同步运输业务单,补齐触发入口与拆单时序
出库业务单与运输业务单为两张独立表,仅靠发票号经配送明细关联,
此前无字段同步逻辑,出库单填写的线路/车次不会流到运输业务单。

1. ReservationStockOutOrderDomainService新增syncLineInfoToBusinessOrder:
   按关联链 出库单号->reservation_delivery_details_link.invoiceNumber
   ->business_delivery_details_link.invoiceNumber 定位关联业务单,
   以出库业务单为源头覆盖lineCode/lineName/shuttleNo。
2. 触发入口三个:审核通过(auditState)/下发(issueOrder)/执行并下发
   (executeAndIssue);取消下发不触发。下发与执行并下发不经过审核
   动作,只挂审核入口会漏同步。
3. 同步目标同时覆盖orderId(未拆单业务单/拆单后主单)与subOrderId
   (拆单子单):仅同步subOrderId时,出库先审核而运输单尚未拆单
   (subOrderId为空)会落空,且主单无值导致后续拆单子单拷贝不到。
4. 同步失败仅记error日志,不阻断审核/下发主流程
   (状态更新与同步非同一事务,关联数据异常不应导致接口报错)。

边界:运输业务单手工创建且未录配送明细时无关联链不同步,需页面自行
填写;关联依赖发票号两侧一致,与既有退货回写为同一关联假设。
2026-08-22 18:38:39 +08:00
王奎兴 0eb2f323c7 发31 2026-08-22 18:29:36 +08:00
王奎兴 a35030e2f5 登录报三方异常处理; 2026-08-22 18:27:56 +08:00
王奎兴 d1b1b27ab0 发30 2026-08-22 17:46:47 +08:00
王奎兴 4a6a2798e8 发31 2026-08-22 17:36:27 +08:00
rcx 9068fa0e40 feat(wms):WMS出库单增加线路车次字段,App出库单详情显示线路/车次
背景:App出库单详情调wmsApi/stockOutOrderApi/getInfo,数据在WMS
stock_out_order表,该表未加线路/车次字段导致不回显。

1. mhd-api StockOutOrder(OMS下发Feign DTO)加lineCode/lineName/shuttleNo,
   OMS执行单下发为copyProperties同名拷贝,自动透传,OMS代码零改动。
2. WMS stock_out_order表加三列(脚本追加);entity/PO加字段,
   Feign接收按JSON字段名对齐落库;Mapper XML resultMap与显式列清单补列,
   getInfo(getById自动)与列表均可返回。
3. 部署需重新打包mhd_oms与mhd_wms(OMS依赖的mhd-api jar变更)。
   存量已下发WMS的老单无值,OMS/WMS跨库无法批量刷数,新下发单自动带。
2026-08-22 17:23:48 +08:00
rcx 99cb355640 发测试 2026-08-22 16:50:12 +08:00
rcx c9520c65be feat(wms):上架默认库位填充限定谷丰组织(id=2842)生效 2026-08-22 16:38:18 +08:00
rcx 2a4e39f755 Merge remote-tracking branch 'origin/dev-ty1.2' into dev-ty1.2 2026-08-22 14:10:07 +08:00
rcx a08d8a7fd5 feat(oms):出库导入校验送貨線號/車次并回填线路名称
1. 出库导入(预约/执行两侧)增加validateLineAndShuttle校验:
   送貨線號/車次缺失直接报错;线路须在WMS线路管理存在(route_code精确匹配);
   车次须在该线路车次清单(shuttle_json)内;校验通过回填lineName,
   线路/车次一并落lineCode/lineName/shuttleNo。
2. WmsServiceFeign新增listLineManagement(复用WMS /lineManagementApi/listByApp)
   供OMS跨库查线路,附降级实现;顺带移除FeignClient写死的本机url,恢复Nacos服务发现。
3. 执行侧DynamicDeliveryExcelListener(关键字版)补送貨線號/車次独立成行的解析,
   原实现仅支持与送貨日期同行取值,该模板为分行布局导致解析不到。
2026-08-22 14:08:22 +08:00
rcx ab7219e600 feat(wms):上架管理PC上传页默认填充指定上架库位(维他奶仓→2层冷凍A區A01),仅回显不落库 2026-08-22 14:06:56 +08:00
王奎兴 1b3b1a6394 Merge branch 'dev' into dev-ty1.2
# Conflicts:
#	mhd_wms/src/main/resources/bootstrap.yml
2026-08-22 13:59:22 +08:00
王奎兴 072a4f7d1f 退货验证发票号是否重复;维他奶导入单号添加校验; 2026-08-22 11:17:42 +08:00
王奎兴 63a19fbbcd 退货入库单编辑后才显示数量; 2026-08-22 10:26:54 +08:00
rcx 5fc491f612 fix(oms,wms):执行单重复下发致WMS收货明细翻倍修复,executeAndIssue获取仓库信息失败修复
一、收货明细翻倍(executionStockInOrderApi/issueOrder下发后,WMS收货管理详情明细×2):
根因:执行单下发接口无防重 + WMS接收端无幂等。executeAndIssue已自动下发过的执行单
仍可被手动再次下发(issueTime已置但无校验),重复推送使WMS插入同号入库主单+第二份
in_material_detail,再生成第二张同号收货单(收货单号"RV-"+入库单号确定性生成),
收货管理详情按单号查明细即双倍。四层防御:
1. OMS执行单issueOrder补防重:issueTime非空拒绝下发(与executeAndIssue对齐),
   inOrderIds去重防同请求重复推送
2. WMS omsOrderAdd主单幂等:同单号有效单(del_flag=1)已存在则跳过插入
3. WMS omsOrderAddDetail明细幂等:按oms_in_material_detail判重,已存在行不再插入;
   未传ID的行保持原插入行为
4. WMS autoAuditOmsInOrder取单补orderByAsc(in_order_id)消除同号取单随机性;
   auditApprove生成收货单前按收货单号查重过滤,已生成过的单不再重复生成
二、executeAndIssue报"获取仓库信息失败":
1. 明细行warehouseId=0时兜底改用主表仓库(与下发issueOrder兜底逻辑对齐),
   不再拿无效仓库ID调system服务
2. executeAndIssue/issueOrder两处报错增强为带仓库ID与错误信息,便于定位
存量已翻倍数据(重复入库单/明细/收货单)需另行软删清理,本次仅防新增
2026-08-21 18:21:03 +08:00
rcx b209d8cb6d fix(oms):出库明细materialBaseInfoId自动纠正,杜绝脏物料ID落库
问题:客户端推单把materialInventoryId传进materialBaseInfoId字段(或漏传为0),
出库明细存脏ID,编辑/保存报"物料基础信息不存在"(如DCCK034存了库存ID477/444,
真实物料ID应为233/231)。预约端校验原先静默跳过,脏数据持续入库(7-15起200+行)。

一、新增MaterialBaseInfoResolveService三级解析:
    baseId直查 → 库存ID反查纠正 → 物料编码+组织兜底,全部失败返回null
二、三处setMaterialDetailInfo改为纠正+回填(预约/业务/执行出库单):
    查不到抛错带定位信息(baseId/库存ID/物料编码/单号);
    纠正后的ID随保存落库,旧脏单重新保存即自愈;
    预约端由静默跳过改为抛错
三、两处pushOutOrderDetail推送入口(预约/业务)加纠正,
    反查不到拒绝保存,脏数据不再入库

注:存量脏数据修复SQL另行手动执行;下发复制链(预约→业务→执行)不走校验,
2026-08-21 18:20:22 +08:00
rcx 06aae196c7 发测试 2026-08-21 16:59:56 +08:00
rcx e91c5801eb 发测试 2026-08-21 15:54:46 +08:00
rcx 8704df738c Merge remote-tracking branch 'origin/dev' into dev-ty1.2 2026-08-21 15:47:44 +08:00
rcx 000092952f feat(oms,wms):出库/运输单据增加线路车次字段,线路管理支持车次清单维护
1. OMS四张表(business_document_order/execute_order/
   reservation_stock_out_order/execution_stock_out_order)加
   LINE_CODE/LINE_NAME/SHUTTLE_NO三列,entity/DO/DTO/PO同步加字段,
   出库两链路Mapper XML补列与resultMap。
   线路编码/名称冗余存line_management的route_code/route_name
   (两库不互通无法join);SHUTTLE_NO为字符串(如4或31-4),
   前端拼"编码-车次"显示(31-4车次);不复用route(存tms_route.id)
   和train_no(入库链路占用)。
   下发链路(预约单->执行单,业务单->执行单)同名拷贝自动透传。
2. WMS line_management加SHUTTLE_JSON列(车次序号JSON数组),
   四层加字段+XML;DTO加反序列化器兼容前端传数组,
   PO加序列化器返回原样输出数组。
3. LineManagementImpl.update组织/仓库名称空串兜底,
   修复编辑线路后组织名称被刷成空串、列表查不到的问题。
4. 新增建表脚本sql/20260820_oms_outbound_transport_add_line_shuttle.sql(达梦语法)。
2026-08-21 15:28:50 +08:00
rcx 22f2b66ffa feat(oms,wms):库存导出按到期日分组排序并合并同物料同到期日库存(仅wlExpiry查询生效)
需求:库存查询导出按商品到期日分组归类、相同到期日相邻显示,相同物料相同
到期日合并库存。仅在 queryRule=wlExpiry(物料+到期日汇总查询)导出时生效,
列表显示不变;同时回退 8fedb6d6 对列表默认排序的改动。

一、列表默认查询还原(回退 8fedb6d6):OMS queryList 恢复 order by a.update_time desc,
    Api 恢复 update_time 内存重排(空值最后),两侧列表排序与改动前一致。
二、导出排序(OMS/WMS queryListByWlExpiry):/list 新增可选参数 orderByExpiryDate,
    仅 queryRule=wlExpiry 时生效:
    1. wlExpiry + orderByExpiryDate=true(导出):到期日倒序(日期近的在前)、空值最后、
       0库存靠后、同到期日按更新时间倒序;排序在SQL层,跨页/全量导出分组不被打散;
       OMS Api 传参时跳过 update_time 内存重排,避免覆盖SQL排序
    2. 仅 wlExpiry 不带参数:排序保持原样(sort_order+更新时间倒序)
    3. 其他查询方式:不受影响,参数被忽略
三、合并:wlExpiry 查询 GROUP BY 物料+到期日+货主(货主隔离),相同物料相同到期日
    合并一行,库存/可用/冻结数量及净重/毛重/体积/面积 SUM 汇总(复用现成查询,未改)
四、前端导出:/list + queryRule=wlExpiry + orderByExpiryDate=true + 当前查询条件(不分页)
2026-08-21 15:13:28 +08:00
rcx 3aec384ba0 fix(wms):出库委托单导入出仓日期丢失、升列与单价回填修复
- HeaderListener:parseDate升级为parseDateSmart,兼容Excel日期序列号(20000~60000≈1954~2064年,
  POI DateUtil.getJavaDate转换)及yyyy/M/d、yyyy.M.d、yyyy年M月d日等多格式文本;
  出仓日期/完成时间/下单日期三处调用点同步切换;解析失败记入dateParseErrors不再静默
- ExcelParseService:导入校验汇总dateParseErrors,日期单元格有值但格式不识别时阻断导入并
  明确提示(为空仍放行,与原行为一致);单价回填从料号分支移至料号/SKU/名称三分支统一回填块,
  补齐SKU、商品名称匹配场景
- ItemListener:升列新增normalizeNumeric归一化,去千分位逗号、空值统一存null、纯数字去尾零,
  落库值恒为"纯数字或null",前端合计不再产生NaN/拼接
2026-08-21 14:56:12 +08:00
rcx 27f3fceb6e feat(wms):出入库导入增加料号与规格型号/SKU码一致性校验
- 料号命中物料档案后:规格型号/SKU码必须与档案一致,Excel留空且档案有值同样报错(提示Excel:空);档案值为空则跳过该项
- SKU以档案条码(bar_code)为基准;错误行一次性汇总提示,拒绝导入
- 出库料号已填写但货主下查不到时直接报错,不再被SKU码/商品名称静默兜底(与入库行为对齐)
2026-08-21 14:42:25 +08:00
王奎兴 3e5ed28d69 发30 2026-08-20 18:14:37 +08:00
王奎兴 78d40d35be 发31 2026-08-20 18:13:30 +08:00
rcx 8fedb6d6ed feat(oms):库存查询/导出按商品到期日分组排序 2026-08-20 17:54:23 +08:00
王奎兴 b8de1c00eb 发31 2026-08-20 17:41:09 +08:00
王奎兴 bf080609fe 发30 2026-08-20 17:39:44 +08:00
王奎兴 b415ed6fa0 出库预约业务类型查询; 2026-08-20 17:34:43 +08:00
王奎兴 694ed699ec 单号生成规则包含已删除; 2026-08-20 17:26:56 +08:00
王奎兴 0b26692a34 发31 2026-08-20 15:35:31 +08:00
王奎兴 c2af421c7f 发30 2026-08-20 15:31:40 +08:00
王奎兴 57f87baf85 发30 2026-08-20 15:30:59 +08:00
王奎兴 e23edadde6 发30 2026-08-20 14:59:53 +08:00
王奎兴 530254e000 发31 2026-08-20 14:57:14 +08:00
rcx 33d085f9b9 fix(oms):入库链路计划数量翻倍修复
明细batchUpdate软删条件缺陷(入/出库共6个明细实现类):软删旧明细要求本次提交
   至少一行走更新分支,"删光旧物料全部换新物料"时提交列表全为新增行,旧行漏软删
   (del_flag仍=1),下发后执行单计划数量=旧+新叠加。改为库内有明细即对账软删
2026-08-20 14:45:13 +08:00
王奎兴 21fcc24e2e 发31 2026-08-20 14:20:07 +08:00
王奎兴 64350f565b 列表查询id不对; 2026-08-20 14:16:23 +08:00
rcx ff2b1965f7 fix(oms):入库业务单单号重复与入库链路计划数量翻倍修复
一、单号重复:业务单与客户预约两套取号计数器互不可见致撞号。
取号统一为跨两表MAX+1+存在性校验,保存前校验预取号占用,下发不再换号,Mapper新增existsByOrderNumber。
二、计划数量翻倍:编辑明细为软删(del_flag=2留存),下发查明细未过滤del_flag,
软删行被复制进下游单据并计入头表数量(执行单数量≈业务单×2+修改差值)。
1. 预约→业务单下发、执行单推WMS:明细查询补del_flag=1过滤
2. executeAndIssue(执行并下发):补防重复下发校验(issueTime非空拒绝)
3. pushInOrder/pushInOrderDetail:加幂等,防Feign重试插重复单
2026-08-20 14:06:50 +08:00
王奎兴 7a769071e4 发30 2026-08-20 10:48:34 +08:00
王奎兴 b05f994e74 发31 2026-08-20 10:35:34 +08:00
rcx ef13d890be fix(oms):入库执行单列表单号搜索查不到,businessNumber死条件改单号模糊互查;库存查询货主过滤移回主表并修正委托方误判
- 入库执行单列表:businessNumber条件原为BUSINESS_NUMBER精确等值,该列无写入恒空致全条件查询0条,改为业务单号/入库单号OR模糊互查

- 入库执行单列表:inOrderNumber精确等值改模糊like;删除重复的createByName条件;补businessInOrderNumber过滤(业务单号/入库单号互查)
- 库存查询:货主过滤(shipperId/Code/Name/shipperInfo)从物料表b移回库存主表a,避免LEFT JOIN退化INNER JOIN丢行
- 库存查询:委托方判定由组织层级改为UserPo.shipperEnterpriseName,避免二级组织运营人员(2841/2842)被误判强制按本人userId过滤
2026-08-20 10:17:16 +08:00
rcx cc943d164b fix(wms):入库单导入表头字段错位、备注丢失
- 取值向右查找时误把下一个标签/选项说明当值(承运方←Tel:、生产商←客户名称:、监管方式←送货地址:、送货地址←查验选项),现遇疑似标签即停止查找
- 备注:xxx同格书写为全角冒号,原仅按半角:拆分导致备注丢失,现全角/半角兼容
- 表头列遍历上界改用getLastCellNum,防空单元格漏扫尾部列"
2026-08-20 10:06:50 +08:00
rcx 77420e9617 feat(sys):合同附件增加原始文件名存储字段,配合上传v2回显原名称
需求背景:合同管理-上传的文件需显示原始文件名(原显示 LOCAL:D:\upload\{uuid}.pdf 之类存储键)。
附件此前仅存 fileKey,原始文件名在上传落盘时已被 UUID 重命名丢失,回显无原名可用。
改动内容:
1. contract_manage 表新增 4 个附件原始文件名字段(见 sql/20260818_contract_attachment_original_name.sql):
   contract_original_name    合同正本附件原始文件名
   integrity_agreement_name  廉政协议附件原始文件名
   security_protocol_name    安全协议附件原始文件名
   other_attachments_name    其他附件原始文件名(多文件与 other_attachments_url 同分隔符、同顺序)
2. ContractManage / ContractManagePO / ContractManageDO / ContractManageDTO
   四个对象同步新增对应 4 个 name 字段,保存、详情、列表全链路透传。
   Assembler 与 ContractManageImpl 为 BeanUtils 同名拷贝、Mapper XML 为 SELECT a.*,均无需改动。"
 "配套依赖:
- mhd-product 新增 /menu/upload/v2 返回 { fileKey, originalName }(另一提交)
- 前端上传后成对保存 fileKey 与 originalName,回显显示 name 字段
兼容说明:
- 存量合同 name 字段为空,前端回退显示 fileKey 末段(uuid 名),属预期兼容行为
- 纯新增字段,旧代码不读写,不影响既有接口
2026-08-19 18:08:59 +08:00
rcx fa51580985 feat(product):附件上传v2返回原始文件名,下载接口支持inline预览与中文文件名 2026-08-19 18:04:57 +08:00
rcx 362f1b5e2f fix(bms):科目编码重复致新增账单失败,税率查询按编码+名称取唯一;复制账单改生成新计费流水
- getTaxRate 增加 SUBJECT_NAME 匹配,费用科目编码重复(如1003同时有运输处理费/报关费3)时不再抛 TooManyResultsException,查不到税率兜底0
- addBill、开票/NC推送共4处调用点同步调整;计费流水号为空时只处理自身明细,防空指针
- copyBill 明细清空计费流水id并按原流水映射生成新JFLS流水号,防止复制提交后把原账单的流水改挂到新账单(原账单明细丢失)
- insertAndReturnId 币种空值兜底CNY(与addCost对齐)
2026-08-19 18:02:23 +08:00
rcx b859aa99d3 fix(oms):入库业务单导入件数未回显计划数量
- 导入模版去掉Qty(cases)列(5列布局),饮用日期列位前移
- 明细计划数量由Qty箱数改为"件数"取值,件数同步落库PIECE_COUNT用于列表回显
- 物料在当前登录组织下不存在时返回明确提示,替代货主列表取值越界导致的500
2026-08-19 17:59:19 +08:00
rcx 83f05dd11d feat(bms):新增费用统计分析报表,科目按模板类别归并、仓储拆散租/固定出租两行、行级占营收比,含34列导出与智能分析归因 2026-08-19 15:36:53 +08:00
rcx dd0bf32c35 feat(gateway):登录验证码改为纯数字,字符源限定0-9
需要将网关yml文件中 math 改成 char
security:
  captcha:
    enabled: true
    type: char   # math=算术题,char=纯数字
2026-08-19 14:28:45 +08:00
rcx 7ed12c9405 feat(bms):应收账单添加费用币种空值兜底CNY、费用明细增加流水备注保存与显示 2026-08-19 11:02:52 +08:00
rcx 72e445ea16 fix(oms):出库添加货物查询不到商品,库存查询改租户+货主维度并出库列表补货主旁路
1.ReservationMaterialInventoryApplicationService.queryList原对非南光组织用户强制organization_id=用户组织且top=2827,委托方(如仓码2841货主)库存被滤空;改为按一级组织(租户)过滤,委托方自动叠加登录货主shipperId查自己的货,库存数据已核实在库
2.ReserveStockOutOrderApplicationService.queryList/detailsList增加货主直查旁路(与入库queryList对齐),委托方按shipperId查询不被组织过滤拦截
2026-08-18 18:09:53 +08:00
rcx f50a3e690a fix(oms):出库预约落库回填仓库/组织/货主,状态默认已创建
与入库侧同款修复:insert按仓库档案回填组织/一级组织/仓库编码名称(兜底2841),委托方回填shipperId/customerId,status默认1已创建(配合App订单状态只读);补fastjson import
2026-08-18 18:09:41 +08:00
rcx 8692b38671 fix(oms):入库预约App提交后各端不显示,落库回填仓库/组织/货主并支持货主维度查询
根因:App提交的预约单organization_id/shipper_id落库为空、warehouse_code全空,列表按组织与仓码过滤必然查不到
1.ReserveStockInOrderDomainService.insert:
  -新增明细非空校验,App不传明细时返回'请添加货物明细'而非NPE
  -按warehouseId查仓库档案回填组织(仓码2841/谷丰2842按仓库归属)/一级组织(2827)/仓库编码名称,仓库缺失时兜底登录人一级组织+2841
  -委托方(有货主档案)未传shipper时回填登录人userId至shipperId/customerId
  -订单状态默认'新增'/status默认1(配合App端订单状态改只读)
2.ReserveStockInOrderApplicationService:
  -detailsList增加货主直查旁路(对齐queryList既有逻辑),委托方按shipperId查询不再被组织过滤拦截
  -inAndOutList原无任何数据权限,委托方登录强制注入本人shipperId,防止传错组织(如通宇2830)导致列表滤空并防越权
2026-08-18 18:09:34 +08:00
rcx f77d89fa7f fix(thirdParty):短信验证码发送失败误包成成功响应,失败原因取result并返回error
SmsApi.sms发送失败分支原返回AjaxResult.success(发送失败)且getMessage()取的是默认文案,现改为AjaxResult.error并携带乐信通返回的失败原因,App端可感知发送失败原因
注:收不到验证码的主因(网关白名单/租户域名映射)为Nacos与DB配置项,不在代码仓库
2026-08-18 18:09:25 +08:00
rcx 0ff327a071 fix(auth):App注册接口空实现,补全手机号+短信验证码注册链路
1.AppTokenController.register改为透传完整LoginDTO
2.TokenLoginApplicationService.register原为TODO空壳(仅格式校验直接返回成功),现补全:注册短信验证码校验(SMS_REGISTER redis)→域名解析一级组织→按手机号查重→复用registerAddUser创建用户(含默认角色/YMS同步)→注册成功清验证码并记录日志
3.配套网关白名单(/auth/app/register,/auth/app/getOrganizationByPath/**)需在Nacos发布,不入库
2026-08-18 18:09:18 +08:00
rcx 6661c016e8 fix(auth):密码登录账号/密码错误无提示,登录配置读取与全局异常兜底
1.SysPasswordService.validate登录重试配置(login/maxRetryCount,lockTime)读取增加try/catch与空值保护,三方服务异常或配置缺失时降级默认值(错5次锁30分钟)并warn日志,不再NPE/NumberFormatException导致msg为空
2.GlobalExceptionHandler的RuntimeException/Exception分支对e.getMessage()为空兜底为'系统繁忙,请稍后重试',保证App端始终能拿到可展示的错误信息
2026-08-18 18:08:49 +08:00
rcx bca8b5fbed fix(oms):入库执行单业务单号不随下发复制,执行并下发入口补充有效运输业务单回填 2026-08-18 11:36:21 +08:00
rcx e489015e3e fix(oms):下发过滤已删除明细,执行单不再残留已取消运输业务单号 2026-08-18 10:19:40 +08:00
472 changed files with 24143 additions and 1340 deletions
@@ -3,6 +3,7 @@ package com.mhd.system.api;
import com.mhd.common.core.constant.ServiceNameConstants;
import com.mhd.common.core.domain.dto.BusinessDataPushDTO;
import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.system.api.domain.BusinessDocumentFeign;
import com.mhd.system.api.factory.RemoteBmsFeignFallbackFactory;
import com.mhd.system.api.feign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
@@ -18,7 +19,8 @@ import java.util.Set;
/**
* bms财务结算系统feign调用接口
*/
@FeignClient(contextId = "BmsService", value = ServiceNameConstants.BMS_SERVICE, url = "http://10.33.0.109:8019",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
// 目标地址由 nacos 配置项 feign.url.mhd-bms-service 指定,未配置时按服务名走服务发现
@FeignClient(contextId = "BmsService", value = ServiceNameConstants.BMS_SERVICE, url = "http://10.102.192.32:8019",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
public interface BmsServiceFeign {
@@ -68,4 +70,17 @@ public interface BmsServiceFeign {
*/
@PostMapping("/businessDocumentApi/wholeRentSynchronizationZXF")
public AjaxResult wholeRentSynchronizationZXF(@RequestBody BusinessDataPushDTO businessDataPushDTO);
@PostMapping("/businessDocumentApi/feignSave")
public AjaxResult feignSaveBusinessDocument(@RequestBody BusinessDocumentFeign businessDocumentFeign);
/**
* 根据结算对象id查询结算对象参数列表
*/
@GetMapping("/settlementCustomersParametersApi/getListBySettlementCustomersId")
AjaxResult getListBySettlementCustomersId(@RequestParam("settlementCustomersId") Long settlementCustomersId);
@PostMapping("/settlementCustomersApi/updatePushContractManageJobId")
AjaxResult updatePushContractManageJobId(@RequestParam("settlementCustomersId") Long settlementCustomersId,
@RequestParam("jobId") Integer jobId);
}
@@ -12,11 +12,13 @@ import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@FeignClient(contextId = "OmsService", value = ServiceNameConstants.OMS_SERVICE, url = "http://10.33.0.99:8017",fallbackFactory = RemoteOmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
// 目标地址由 nacos 配置项 feign.url.mhd-oms-service 指定,未配置时按服务名走服务发现
@FeignClient(contextId = "OmsService", value = ServiceNameConstants.OMS_SERVICE,url = "http://10.102.192.33:8017",fallbackFactory = RemoteOmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
public interface OmsServiceFeign {
@@ -28,6 +30,10 @@ public interface OmsServiceFeign {
@PostMapping(value = "/reservationStockInOrderApi/pushInOrderDetail")
public AjaxResult pushInOrderDetail(@RequestBody InMaterialDetailTZPD inMaterialDetail);
@ApiOperation("wms回传入库结果:按入库单号同步入库状态(3-收货中 4-上架中 5-已入库)到OMS入库业务单,type非1为入库")
@GetMapping(value = "/executionStockInOrderApi/returnResult")
public AjaxResult returnInResult(@RequestParam("orderNumber") String orderNumber, @RequestParam("status") Long status, @RequestParam("type") String type);
@ApiOperation("wms推送入库单")
@PostMapping(value = "/reservationStockOutOrderApi/pushOutOrder")
@@ -12,6 +12,8 @@ import com.mhd.common.core.domain.entity.R;
import com.mhd.common.core.domain.po.SysMultistageDictPo;
import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.system.api.domain.ContainerFeignPO;
import com.mhd.system.api.domain.ContractManageFeignDTO;
import com.mhd.system.api.domain.ContractManageParametersFeign;
import com.mhd.system.api.domain.SysDictData;
import com.mhd.system.api.factory.RemoteSystemFeignFallbackFactory;
import com.mhd.system.api.feign.FeignAutoConfiguration;
@@ -237,6 +239,12 @@ public interface SystemServiceFeign {
@GetMapping("/warehouseApi/getInfo/{warehouseId}")
public AjaxResult getWarehouseInfoByWarehouseId(@PathVariable("warehouseId") Long warehouseId);
/**
* 根据warehouseId获取仓库及下库区及库位树(与前端 warehouseApi/getWarehouseChildren 一致,用于PC上架页默认库位回显)
*/
@GetMapping("/warehouseApi/getWarehouseChildren")
public AjaxResult getWarehouseChildren(@RequestParam("warehouseId") Long warehouseId);
/**
* 根据storageSectionId获取对应库区信息
*/
@@ -384,4 +392,13 @@ public interface SystemServiceFeign {
public AjaxResult matchForMaterialImport(@RequestParam("keyword") String keyword,
@RequestParam("organizationId") Long organizationId);
@ApiOperation("保存临时合同管理")
@PostMapping("/contractManageApi/feignSave")
public AjaxResult feignSave(@RequestBody ContractManageFeignDTO contractManageFeignDTO);
@ApiOperation("Feign保存合约信息规则维护")
@PostMapping("/contractManageParametersApi/feignSave")
public AjaxResult feignSaveContractParameters(@RequestBody ContractManageParametersFeign contractManageParametersFeign);
}
@@ -26,7 +26,8 @@ import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@FeignClient(contextId = "commonWlhyServiceFeign",value = ServiceNameConstants.WLHY_SERVICE,url = "http://10.33.0.129:8014",configuration = FeignAutoConfiguration.class)
// 目标地址由 nacos 配置项 feign.url.mhd-wlhy-service 指定,未配置时按服务名走服务发现
@FeignClient(contextId = "commonWlhyServiceFeign",value = ServiceNameConstants.WLHY_SERVICE,url = "http://10.102.192.31:8014",configuration = FeignAutoConfiguration.class)
public interface WlhyServiceFeign {
/**
@@ -21,7 +21,8 @@ import java.util.Map;
* @description: TODO
* @date 2024/5/10 8:58
**/
@FeignClient(contextId = "remoteWmsServiceFeign",value = ServiceNameConstants.WMS_SERVICE, url = "http://10.33.0.109:8016",fallbackFactory = RemoteWmsFallbackFactory.class, configuration = FeignAutoConfiguration.class)
// 目标地址由 nacos 配置项 feign.url.mhd-wms-service 指定,未配置时按服务名走服务发现
@FeignClient(contextId = "remoteWmsServiceFeign",value = ServiceNameConstants.WMS_SERVICE,url = "http://10.102.192.32:8016",fallbackFactory = RemoteWmsFallbackFactory.class, configuration = FeignAutoConfiguration.class)
public interface WmsServiceFeign {
/**
@@ -92,15 +93,15 @@ public interface WmsServiceFeign {
/**
* oms下发单保存
* oms下发单保存(返回执行结果,失败时OMS据此回滚下发标记,避免"OMS已下发/WMS无数据"的阻断)
*/
@ApiOperation("oms下发单保存")
@PostMapping(value = "/stockInOrderApi/omsInOrderAdd")
public void omsInOrderAdd(@RequestBody StockInOrder stockInOrder);
public AjaxResult omsInOrderAdd(@RequestBody StockInOrder stockInOrder);
@ApiOperation("oms取消下发单保存")
@PostMapping(value = "/stockOutOrderApi/cancelExecutionOrder")
public void omsCancelExecutionOrder(@RequestBody StockOutOrder stockOutOrder);
public AjaxResult omsCancelExecutionOrder(@RequestBody StockOutOrder stockOutOrder);
@ApiOperation("oms取消下发单自动分配")
@PostMapping(value = "/stockOutOrderApi/omsAutoAssign")
@@ -111,11 +112,27 @@ public interface WmsServiceFeign {
public AjaxResult execution(@RequestBody com.mhd.system.api.domain.StockOutOrderDTO stockOutOrderDTO);
/**
* oms下发单明细保存
* oms下发前库存校验:可用库存不足时返回错误,OMS据此在推送前拦截下发,
* 避免单据先落库、分配才失败而在WMS残留"已审核"状态的垃圾单
*/
@ApiOperation("oms下发前库存校验")
@PostMapping(value = "/stockOutOrderApi/checkOmsAssignInventory")
public AjaxResult checkOmsAssignInventory(@RequestBody List<OutMaterialDetail> outMaterialDetails);
/**
* oms下发单明细保存(返回执行结果,明细保存含自动审核链路,失败时OMS据此回滚)
*/
@ApiOperation("oms下发单明细保存")
@PostMapping(value = "/stockInOrderApi/omsInOrderAddDetail")
public void omsInOrderAddDetail(@RequestBody List<InMaterialDetail> inMaterialDetails);
public AjaxResult omsInOrderAddDetail(@RequestBody List<InMaterialDetail> inMaterialDetails);
/**
* oms取消下发入库执行单:WMS侧一个事务内逻辑删除下发链路产生的全部数据
* (入库单/明细、收货单/明细、收货任务、收货作业单/明细、质检单),已开始收货则报错拦截
*/
@ApiOperation("oms取消下发入库单清理")
@PostMapping(value = "/stockInOrderApi/omsCancelInOrder")
public AjaxResult omsCancelInOrder(@RequestBody StockInOrder stockInOrder);
@ApiOperation("oms查询物料列表")
@GetMapping("/materialBaseInfoApi/queryList")
@@ -151,4 +168,12 @@ public interface WmsServiceFeign {
@ApiOperation("OMS批量签名-同步WMS入/出库单签名字段")
@PostMapping("/stockSignatureApi/batchSign")
public AjaxResult batchSignWms(@RequestBody SignatureBatchFeignDTO dto);
/**
* OMS查询WMS线路管理列表(按线路编码模糊查,OMS侧自行精确匹配),
* 出库导入时校验送貨線號/車次并回填线路名称
*/
@ApiOperation("OMS查询WMS线路管理列表")
@GetMapping("/lineManagementApi/listByApp")
public AjaxResult listLineManagement(@RequestParam("routeCode") String routeCode);
}
@@ -0,0 +1,24 @@
package com.mhd.system.api;
import com.mhd.common.core.constant.ServiceNameConstants;
import com.mhd.common.core.domain.entity.R;
import com.mhd.common.core.domain.dto.YmsSysUserDTO;
import com.mhd.system.api.domain.YmsLoginUser;
import com.mhd.system.api.factory.RemoteBmsFeignFallbackFactory;
import com.mhd.system.api.factory.RemoteXxlJobFeignFallbackFactory;
import com.mhd.system.api.feign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
// 目标地址由 nacos 配置项 feign.url.xxl-job-service 指定,未配置时按服务名走服务发现
@FeignClient(contextId = "XxlJobService", value = ServiceNameConstants.XXL_JOB_SERVICE,url = "http://10.102.192.32:8020",fallbackFactory = RemoteXxlJobFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
public interface XxlJobServiceFeign {
@GetMapping("/user/openFeignTest")
public R<Map<String, Object>> openFeignTest(@RequestParam("param") String string);
}
@@ -9,7 +9,8 @@ import com.mhd.system.api.feign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
@FeignClient(contextId = "YmsService", value = ServiceNameConstants.YMS_SERVICE,url = "http://10.33.0.99:8018",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
// 目标地址由 nacos 配置项 feign.url.mhd-yms-service 指定,未配置时按服务名走服务发现
@FeignClient(contextId = "YmsService", value = ServiceNameConstants.YMS_SERVICE,url = "http://10.102.192.32:8018",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
public interface YmsServiceFeign {
/**
@@ -0,0 +1,203 @@
package com.mhd.system.api.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.mhd.common.core.annotation.Excel;
import com.mhd.common.core.web.domain.BaseVOEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 业务单据对象 business_document
*
* @author gen
* @date 2024-06-21
*/
@Data
public class BusinessDocumentFeign extends BaseVOEntity{
private static final long serialVersionUID = 1L;
@ApiModelProperty("业务单据记录id")
@TableId(type = IdType.AUTO)
private Long businessDocumentId;
@ApiModelProperty("一级组织表ID")
@Excel(name = "一级组织表ID")
private Long topOrganizationId;
@ApiModelProperty("组织表ID")
@Excel(name = "组织表ID")
private Long organizationId;
@ApiModelProperty("组织名称")
@Excel(name = "组织名称")
private String organizationName;
@ApiModelProperty("业务流水")
@Excel(name = "业务流水")
private String businessFlow;
@ApiModelProperty("状态(1-未生效,2-已生效,3-已计费,4-已驳回,5-已作废")
@Excel(name = "状态", readConverterExp = "状态(1-未生效,2-已生效,3-已计费,4-已驳回,5-已作废")
private Integer businessState;
@ApiModelProperty("来源模块")
@Excel(name = "来源模块")
private String belongModule;
@ApiModelProperty("来源模块code")
@Excel(name = "来源模块code")
private String belongModuleCode;
@ApiModelProperty("数据来源(1-系统生成,2-手工录入)")
@Excel(name = "数据来源", readConverterExp = "1=-系统生成,2-手工录入")
private Integer dataSources;
@ApiModelProperty("单据类型id")
@Excel(name = "单据类型id")
private Long documentTypeId;
@ApiModelProperty("单据类型code")
@Excel(name = "单据类型code")
private String documentTypeCode;
@ApiModelProperty("单据类型")
@Excel(name = "单据类型")
private String documentType;
@ApiModelProperty("原始业务单号")
@Excel(name = "原始业务单号")
private String originalBusinessNum;
@ApiModelProperty("项目编码")
@Excel(name = "项目编码")
private String projectCode;
@ApiModelProperty("项目名称")
@Excel(name = "项目名称")
private String projectName;
@ApiModelProperty("合同编号")
@Excel(name = "合同编号")
private String contractNumber;
@ApiModelProperty("合同名称")
@Excel(name = "合同名称")
private String contractName;
@ApiModelProperty("账单金额")
@Excel(name = "账单金额")
private BigDecimal billAmount;
@ApiModelProperty("预计费金额")
@Excel(name = "预计费金额")
private BigDecimal estimatedCost;
@ApiModelProperty("一级费用科目code")
@Excel(name = "一级费用科目code")
private String firstSubjectCode;
@ApiModelProperty("一级费用科目(结算项)")
@Excel(name = "一级费用科目")
private String firstSubjectName;
@ApiModelProperty("二级费用科目code")
@Excel(name = "二级费用科目code")
private String secondSubjectCode;
@ApiModelProperty("二级费用科目(结算项)")
@Excel(name = "二级费用科目")
private String secondSubjectName;
@ApiModelProperty("结算对象id")
@Excel(name = "结算对象id")
private Long settlementCustomersId;
@ApiModelProperty("结算对象code")
@Excel(name = "结算对象code")
private String settlementCustomersCode;
@ApiModelProperty("结算对象")
@Excel(name = "结算对象")
private String settlementEntity;
@ApiModelProperty("计费时间")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@Excel(name = "计费时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date billingTime;
@ApiModelProperty("单据表单json数据")
@Excel(name = "单据表单json数据")
private String documentFormJson;
@ApiModelProperty("合同管理id")
@Excel(name = "合同管理id", readConverterExp = "合同管理id")
private Long contractManageId;
@ApiModelProperty("项目管理id")
@Excel(name = "项目管理id", readConverterExp = "项目管理id")
private Long projectManageId;
@ApiModelProperty("原始流水号")
@Excel(name = "原始流水号")
private String originalSerialNumber;
@ApiModelProperty("费用类型code(服务项)")
@Excel(name = "费用类型code(服务项)")
private String serviceItemsCode;
@ApiModelProperty("费用类型")
@Excel(name = "费用类型")
private String serviceItemsName;
@ApiModelProperty("费用类型id(服务项)")
@Excel(name = "费用类型id(服务项)")
private Long serviceItemsManageId;
@ApiModelProperty("单据备注")
@Excel(name = "单据备注")
private String remark;
@ApiModelProperty("审核备注")
@Excel(name = "审核备注")
private String reviewRemarks;
@ApiModelProperty("结算方式(1-现金,2-油卡)")
private Integer settlementWay;
@ApiModelProperty("结算币种")
private String settlementCurrency;
@ApiModelProperty("业务员ID")
private String salesmanId;
@ApiModelProperty("业务员名称")
private String salesmanName;
@ApiModelProperty("税率")
private Double taxRate;
@ApiModelProperty("收款帐户")
private String paymentAccountId;
@ApiModelProperty("收款帐户名称")
private String paymentAccountName;
@ApiModelProperty("计费单位")
private String billingUnit;
@ApiModelProperty("计费单位2")
private String billingUnit2;
@ApiModelProperty("计费数量")
private BigDecimal billingNum;
@ApiModelProperty("计费单价")
private BigDecimal billingUnitPrice;
@ApiModelProperty("nc科目编码")
private String ncSubjectCode;
@ApiModelProperty("是否推送")
private Integer isPush;
@ApiModelProperty("仓库类型")
private String warehouseType;
}
@@ -0,0 +1,110 @@
package com.mhd.system.api.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.mhd.common.core.annotation.Excel;
import com.mhd.common.core.web.domain.BaseVOEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* 合同管理详情对象 contract_manage_detail
*
* @author gen
* @date 2024-06-07
*/
@Data
public class ContractManageDetailFeignDTO extends BaseVOEntity{
private static final long serialVersionUID = 1L;
@ApiModelProperty("合同管理详情id")
private Long contractManageDetailId;
@ApiModelProperty("合同管理id")
@Excel(name = "合同管理id")
private Long contractManageId;
@ApiModelProperty("一级组织表ID")
@Excel(name = "一级组织表ID")
private Long topOrganizationId;
@ApiModelProperty("组织表ID")
@Excel(name = "组织表ID")
private Long organizationId;
@ApiModelProperty("组织名称")
@Excel(name = "组织名称")
private String organizationName;
@ApiModelProperty("结算科目类型(1-通用,2-临时)")
@Excel(name = "结算科目类型", readConverterExp = "1=-通用,2-临时")
private Integer subjectType;
@ApiModelProperty("一级费用科目code")
@Excel(name = "一级费用科目code")
private String firstSubjectCode;
@ApiModelProperty("一级费用科目")
@Excel(name = "一级费用科目")
private String firstSubjectName;
@ApiModelProperty("二级费用科目code")
@Excel(name = "二级费用科目code")
private String secondSubjectCode;
@ApiModelProperty("二级费用科目")
@Excel(name = "二级费用科目")
private String secondSubjectName;
@ApiModelProperty("计费策略id")
@Excel(name = "计费策略id")
private Long accountingStrategyId;
@ApiModelProperty("计费策略")
@Excel(name = "计费策略")
private String accountingStrategy;
@ApiModelProperty("计费策略编码")
@Excel(name = "计费策略编码")
private String accountingStrategyCode;
@ApiModelProperty("计费周期")
@Excel(name = "计费周期")
private String billingAttributes;
@ApiModelProperty("计费周期编码")
@Excel(name = "计费周期编码")
private String billingAttributesCode;
@ApiModelProperty("合同有效日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@Excel(name = "合同有效日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date subjectEffectiveDate;
@ApiModelProperty("合同到期日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@Excel(name = "合同到期日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date subjectExpirationDate;
@ApiModelProperty(name = "组织ID集合")
private List<Long> organizationIdList;
@ApiModelProperty(name = "权限菜单ID")
private Long permissionMenuId;
@ApiModelProperty("单据类型code")
private String documentTypeCode;
@ApiModelProperty("单据类型")
private String documentType;
@ApiModelProperty("费用类别编码")
private String serviceItemsCode;
@ApiModelProperty("费用类别名称")
private String serviceItemsName;
}
@@ -0,0 +1,228 @@
package com.mhd.system.api.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.mhd.common.core.annotation.Excel;
import com.mhd.common.core.web.domain.BaseVOEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* 合同管理对象 contract_manage
*
* @author gen
* @date 2024-06-07
*/
@Data
public class ContractManageFeignDTO extends BaseVOEntity{
private static final long serialVersionUID = 1L;
@ApiModelProperty("合同管理id")
private Long contractManageId;
@ApiModelProperty("一级组织表ID")
@Excel(name = "一级组织表ID")
private Long topOrganizationId;
@ApiModelProperty("组织表ID")
@Excel(name = "组织表ID")
private Long organizationId;
@ApiModelProperty("组织名称")
@Excel(name = "组织名称")
private String organizationName;
@ApiModelProperty("原始合同编号")
@Excel(name = "原始合同编号")
private String originalContractNumber;
@ApiModelProperty("合同编号")
@Excel(name = "合同编号")
private String contractNumber;
@ApiModelProperty("合同名称")
@Excel(name = "合同名称")
private String contractName;
@ApiModelProperty("是否通用合同(1-是,2-否)")
@Excel(name = "是否通用合同", readConverterExp = "1=-是,2-否")
private Integer commonContractFlag;
@ApiModelProperty("合同类型(1-仓储,2-运输,3-其他)")
@Excel(name = "合同类型", readConverterExp = "1=-仓储,2-运输,3=-其他")
private Integer contractType;
@ApiModelProperty("合同状态(1-未生效,2-使用中,3-已过期,4-已作废)")
@Excel(name = "合同状态", readConverterExp = "1=-未生效,2-使用中,3-已过期,4-已作废")
private Integer contractState;
@ApiModelProperty("签订单位")
@Excel(name = "签订单位")
private String signingUnit;
@ApiModelProperty("结算主体")
@Excel(name = "结算主体")
private String settlementCustomers;
@ApiModelProperty("结算主体id")
@Excel(name = "结算主体id")
private Long settlementCustomersId;
@ApiModelProperty("结算主体code")
@Excel(name = "结算主体code")
private String settlementCustomersCode;
@ApiModelProperty("我司身份(1-甲方,2-乙方)")
@Excel(name = "我司身份", readConverterExp = "1=-甲方,2-乙方")
private Integer ourIdentity;
@ApiModelProperty("账期(1-每月,2-双月,3-季度)")
@Excel(name = "账期", readConverterExp = "1=-每月,2-双月,3-季度")
private Integer accountingPeriod;
@ApiModelProperty("合同签订日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@Excel(name = "合同签订日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date signingDate;
@ApiModelProperty("合同生效日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@Excel(name = "合同生效日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date effectiveDate;
@ApiModelProperty("合同到期日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@Excel(name = "合同到期日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date expirationDate;
@ApiModelProperty("备注")
@Excel(name = "备注")
private String remark;
@ApiModelProperty("合同正本附件地址")
@Excel(name = "合同正本附件地址")
private String contractOriginalUrl;
@ApiModelProperty("廉政协议附件地址")
@Excel(name = "廉政协议附件地址")
private String integrityAgreementUrl;
@ApiModelProperty("安全协议附件地址")
@Excel(name = "安全协议附件地址")
private String securityProtocolUrl;
@ApiModelProperty("其他附件地址")
@Excel(name = "其他附件地址")
private String otherAttachmentsUrl;
@ApiModelProperty("合同正本附件原始文件名")
private String contractOriginalName;
@ApiModelProperty("廉政协议附件原始文件名")
private String integrityAgreementName;
@ApiModelProperty("安全协议附件原始文件名")
private String securityProtocolName;
@ApiModelProperty("其他附件原始文件名(多文件时与 otherAttachmentsUrl 同分隔符、同顺序)")
private String otherAttachmentsName;
@ApiModelProperty("我司签订人")
@Excel(name = "我司签订人")
private String ourSinged;
@ApiModelProperty("我司签订人联系方式")
@Excel(name = "我司签订人联系方式")
private String ourSingedPhone;
@ApiModelProperty("我司经办人")
@Excel(name = "我司经办人")
private String ourOperator;
@ApiModelProperty("我司经办人联系方式")
@Excel(name = "我司经办人联系方式")
private String ourOperatorPhone;
@ApiModelProperty("对方签订人")
@Excel(name = "对方签订人")
private String theySinged;
@ApiModelProperty("对方签订人联系方式")
@Excel(name = "对方签订人联系方式")
private String theySingedPhone;
@ApiModelProperty("对方经办人")
@Excel(name = "对方经办人")
private String theyOperator;
@ApiModelProperty("对方经办人联系方式")
@Excel(name = "对方经办人联系方式")
private String theyOperatorPhone;
@ApiModelProperty(name = "组织ID集合")
private List<Long> organizationIdList;
@ApiModelProperty(name = "权限菜单ID")
private Long permissionMenuId;
@ApiModelProperty(name = "合同科目详情列表")
private List<ContractManageDetailFeignDTO> contractManageDetailDTOList;
@ApiModelProperty("合同管理ids")
private String contractManageIds;
@ApiModelProperty(name = "合同签订查询条件开始日期")
private String signingDateStart;
@ApiModelProperty(name = "合同签订查询条件结束日期")
private String signingDateEnd;
@ApiModelProperty(name = "合同生效查询条件开始日期")
private String effectiveDateStart;
@ApiModelProperty(name = "合同生效查询条件结束日期")
private String effectiveDateEnd;
@ApiModelProperty(name = "合同到期查询条件开始日期")
private String expirationDateStart;
@ApiModelProperty(name = "合同到期查询条件结束日期")
private String expirationDateEnd;
@ApiModelProperty(name = "结算主体基础数据id(可以是用户id也可以是企业id)")
private String settlementId;
@ApiModelProperty(name = "是否自动出账(1-是,2-否)")
private Integer automaticFlag;
@ApiModelProperty("出账日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date paymentDate;
@ApiModelProperty(name = "出账天数")
private Integer billDays;
@ApiModelProperty(name = "主体类型(1-货主,2-客户,3-承运商)")
private Integer principalType;
@ApiModelProperty("客户类型")
private String customerType;
@ApiModelProperty("客户类型编码")
private String customerTypeCode;
@ApiModelProperty(name = "是否包含运输费用(1-包含,2-包含)")
private Integer transportationCosts;
@ApiModelProperty("客户类型")
private String contractRentType;
@ApiModelProperty("客户类型编码")
private String contractRentTypeName;
@ApiModelProperty("仓库温层类型")
private String warehouseTempLayerType;
@ApiModelProperty("仓库温层类型编码")
private String warehouseTempLayerTypeName;
}
@@ -0,0 +1,52 @@
package com.mhd.system.api.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.mhd.common.core.annotation.Excel;
import com.mhd.common.core.web.domain.BaseVOEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 合约信息规则维护表
*
* @author gen
* @date 2024-06-07
*/
@Data
public class ContractManageParametersFeign extends BaseVOEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("合同管理id")
@TableId(type = IdType.AUTO)
private Long id;
@ApiModelProperty("合同管理id")
private Long contractManageId;
@ApiModelProperty("一级组织表ID")
@Excel(name = "一级组织表ID")
private Long topOrganizationId;
@ApiModelProperty("组织表ID")
@Excel(name = "组织表ID")
private Long organizationId;
@ApiModelProperty("组织名称")
@Excel(name = "组织名称")
private String organizationName;
@ApiModelProperty("合同编号")
@Excel(name = "合同编号")
private String contractNumber;
@ApiModelProperty("合同名称")
@Excel(name = "合同名称")
private String contractName;
@ApiModelProperty("合约信息规则维护")
@Excel(name = "合约信息规则维护表")
private String parametersJson;
}
@@ -375,6 +375,11 @@ public class InMaterialDetail extends BaseVOEntity {
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date drinkDate;
@ApiModelProperty("过期日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date expiryDate;
@ApiModelProperty("来货验收是否合格;1 合格,0 不合格")
@Excel(name = "来货验收是否合格;1 合格,0 不合格")
private String arrivalAcceptQualified;
@@ -250,4 +250,20 @@ public class MaterialBaseInfoFeign extends BaseVOEntity{
@ApiModelProperty(name = "是否正常物料: 0-是 1-否")
private Integer isNormalMaterial;
@ApiModelProperty("每月仓租")
@Excel(name = "每月仓租")
private BigDecimal monthlyWarehouseRent;
@ApiModelProperty("半月仓租")
@Excel(name = "半月仓租")
private BigDecimal halfMonthWarehouseRent;
@ApiModelProperty("每月仓租单位")
@Excel(name = "每月仓租单位")
private String monthlyWarehouseRentUnit;
@ApiModelProperty("半月仓租单位")
@Excel(name = "半月仓租单位")
private String halfMonthWarehouseRentUnit;
}
@@ -83,6 +83,8 @@ public class MaterialInventoryOmsParamDO extends BaseVOEntity {
private Date productionDate;
//过期日期
private Date expiryDate;
//进货日期
private Date purchaseDate;
private String executioOrderNumber;
@@ -405,4 +405,13 @@ public class StockOutOrder extends BaseVOEntity {
@Excel(name = "车次")
private Integer trainNo;
@ApiModelProperty("线路编码(取自WMS线路管理)")
private String lineCode;
@ApiModelProperty("线路名称(冗余存储)")
private String lineName;
@ApiModelProperty("车次序号(显示: 线路编码-车次, 如31-4)")
private String shuttleNo;
}
@@ -0,0 +1,30 @@
package com.mhd.system.api.domain;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 仓库联动树节点(Feign侧)
* 镜像system端WarehouseLinkagePO/StorageSectionLinkagePO/StorageLocationLinkagePO三级结构:仓库→库区→库位
*
* @author rcx
* @date 2026-08-22
*/
@Data
public class WarehouseLinkageFeignPO implements Serializable {
private static final long serialVersionUID = 1L;
/** 仓库id / 库区id(storageSectionId) / 库位id(storageLocationId) */
private Long id;
/** warehouseCode / storageCode / storageLocationCode */
private String code;
/** warehouseName / storageName / storageLocationName */
private String name;
/** 下级节点:仓库下为库区列表,库区下为库位列表,库位无children */
private List<WarehouseLinkageFeignPO> children;
}
@@ -6,6 +6,7 @@ import com.mhd.common.core.domain.po.OrganizationPo;
import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.system.api.BmsServiceFeign;
import com.mhd.system.api.ProductServiceFeign;
import com.mhd.system.api.domain.BusinessDocumentFeign;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
@@ -66,6 +67,21 @@ public class RemoteBmsFeignFallbackFactory implements FallbackFactory<BmsService
public AjaxResult wholeRentSynchronizationZXF(BusinessDataPushDTO businessDataPushDTO) {
return null;
}
@Override
public AjaxResult feignSaveBusinessDocument(BusinessDocumentFeign businessDocumentFeign) {
return AjaxResult.error("保存业务单据失败");
}
@Override
public AjaxResult getListBySettlementCustomersId(Long settlementCustomersId) {
return AjaxResult.error(throwable.getMessage());
}
@Override
public AjaxResult updatePushContractManageJobId(Long settlementCustomersId, Integer jobId) {
return AjaxResult.error(throwable.getMessage());
}
};
}
}
}
@@ -41,6 +41,11 @@ public class RemoteOmsFeignFallbackFactory implements FallbackFactory<OmsService
return null;
}
@Override
public AjaxResult returnInResult(String orderNumber, Long status, String type) {
return null;
}
@Override
public AjaxResult pushOutOrder(StockOutOrderTZPD stockOutOrder) {
return null;
@@ -13,6 +13,8 @@ import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.system.api.AssociationWarehouseFeign;
import com.mhd.system.api.SystemServiceFeign;
import com.mhd.system.api.domain.ContainerFeignPO;
import com.mhd.system.api.domain.ContractManageFeignDTO;
import com.mhd.system.api.domain.ContractManageParametersFeign;
import com.mhd.system.api.domain.SysDictData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -20,6 +22,7 @@ import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@@ -184,6 +187,11 @@ public class RemoteSystemFeignFallbackFactory implements FallbackFactory<SystemS
return AjaxResult.error("根据仓库ID获取仓库信息失败");
}
@Override
public AjaxResult getWarehouseChildren(@RequestParam("warehouseId") Long warehouseId) {
return AjaxResult.error("根据仓库ID获取仓库及下库区及库位树失败");
}
@Override
public AjaxResult getStorageSectionInfoByStorageSectionId(@PathVariable("storageSectionId") Long storageSectionId) {
return AjaxResult.error("根据库区ID获取库区信息失败");
@@ -312,6 +320,16 @@ public class RemoteSystemFeignFallbackFactory implements FallbackFactory<SystemS
public AjaxResult matchForMaterialImport(String keyword, Long organizationId) {
return AjaxResult.error("物料分类匹配失败");
}
@Override
public AjaxResult feignSave(ContractManageFeignDTO contractManageFeignDTO) {
return AjaxResult.error("新增合同失败");
}
@Override
public AjaxResult feignSaveContractParameters(ContractManageParametersFeign contractManageParametersFeign) {
return AjaxResult.error("保存合同参数失败");
}
};
}
}
@@ -70,12 +70,16 @@ public class RemoteWmsFallbackFactory implements FallbackFactory<WmsServiceFeign
}
@Override
public void omsInOrderAdd(StockInOrder stockInOrder) {
public AjaxResult omsInOrderAdd(StockInOrder stockInOrder) {
//返回错误而非静默成功:下发主单保存失败必须让OMS感知并回滚下发标记,
//否则OMS显示已下发、WMS无数据,下游收货流程无从进行(流程阻断)
return AjaxResult.error("WMS入库单保存失败(服务降级):" + cause.getMessage());
}
@Override
public void omsCancelExecutionOrder(StockOutOrder stockOutOrder) {
public AjaxResult omsCancelExecutionOrder(StockOutOrder stockOutOrder) {
//返回错误而非静默成功,让OMS侧补偿清理能感知失败并记录告警
return AjaxResult.error("WMS取消下发清理失败:" + cause.getMessage());
}
@Override
@@ -89,7 +93,21 @@ public class RemoteWmsFallbackFactory implements FallbackFactory<WmsServiceFeign
}
@Override
public void omsInOrderAddDetail(List<InMaterialDetail> inMaterialDetails) {
public AjaxResult checkOmsAssignInventory(List<OutMaterialDetail> outMaterialDetails) {
//校验接口本身不可用时返回错误,OMS会阻止下发(不能在WMS状态未知时盲目推送)
return AjaxResult.error("WMS库存校验服务不可用:" + cause.getMessage());
}
@Override
public AjaxResult omsInOrderAddDetail(List<InMaterialDetail> inMaterialDetails) {
//返回错误而非静默成功:明细保存(含自动审核)失败必须让OMS感知并回滚下发标记
return AjaxResult.error("WMS入库单明细保存失败(服务降级):" + cause.getMessage());
}
@Override
public AjaxResult omsCancelInOrder(StockInOrder stockInOrder) {
//返回错误而非静默成功,让OMS侧取消下发能感知清理失败并整体回滚(取消下发可重试,WMS清理幂等)
return AjaxResult.error("WMS取消下发清理失败:" + cause.getMessage());
}
@Override
@@ -125,6 +143,12 @@ public class RemoteWmsFallbackFactory implements FallbackFactory<WmsServiceFeign
log.error("WMS批量签名调用失败");
return AjaxResult.error("WMS批量签名调用失败:" + cause.getMessage());
}
@Override
public AjaxResult listLineManagement(String routeCode) {
log.error("OMS查询WMS线路管理失败, routeCode:{}", routeCode);
return AjaxResult.error("WMS线路管理查询失败:" + cause.getMessage());
}
};
}
}
@@ -0,0 +1,44 @@
package com.mhd.system.api.factory;
import com.mhd.common.core.domain.dto.BusinessDataPushDTO;
import com.mhd.common.core.domain.entity.R;
import com.mhd.common.core.domain.po.OrganizationPo;
import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.system.api.BmsServiceFeign;
import com.mhd.system.api.ProductServiceFeign;
import com.mhd.system.api.XxlJobServiceFeign;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @Description
* @Author zg
* @Version 1.0
* @Date 2023/11/3 14:37
*/
@Component
public class RemoteXxlJobFeignFallbackFactory implements FallbackFactory<XxlJobServiceFeign> {
private static final Logger log = LoggerFactory.getLogger(RemoteXxlJobFeignFallbackFactory.class);
@Override
public XxlJobServiceFeign create(Throwable throwable) {
log.error("系统服务调用失败:{}", throwable.getMessage());
return new XxlJobServiceFeign() {
@Override
public R<Map<String, Object>> openFeignTest(String string) {
return null;
}
};
}
}
@@ -130,9 +130,11 @@ public class TokenLoginApplicationService {
//验证码登录校验验证码
String key = CacheConstants.SMS_LOGIN + userPhone;
Object object = redisService.getCacheObject(key) == null ? "":redisService.getCacheObject(key);
if(!ObjectUtil.equal(String.valueOf(object),verificationCode) && !ObjectUtil.equal(verificationCode, SmsConstants.universalCode)){
recordLogininfor(userPhone, Constants.LOGIN_FAIL, "验证码错误", 1);
throw new ServiceException("验证码错误");
if (loginDTO.getWechatFlag() != 1) {
// if (!ObjectUtil.equal(String.valueOf(object), verificationCode) && !ObjectUtil.equal(verificationCode, SmsConstants.universalCode)) {
// recordLogininfor(userPhone, Constants.LOGIN_FAIL, "验证码错误", 1);
throw new ServiceException("验证码错误");
// }
}
//获取包名对应的业务(存在需要查询业务,不存在保存为无业务模式)
@@ -168,7 +170,13 @@ public class TokenLoginApplicationService {
throw new ServiceException("对不起,您的账号:" + userPhone + " 已停用");
}
//校验验证码是否正确
sysPasswordService.validate(userPo, verificationCode, 1, null);
if (loginDTO.getWechatFlag() != 1) {
// if (!ObjectUtil.equal(String.valueOf(object), verificationCode) && !ObjectUtil.equal(verificationCode, SmsConstants.universalCode)) {
// recordLogininfor(userPhone, Constants.LOGIN_FAIL, "验证码错误", 1);
throw new ServiceException("验证码错误");
// }
}
// sysPasswordService.validate(userPo, verificationCode, 1, null);
}
return userInfo;
}
@@ -179,34 +187,67 @@ public class TokenLoginApplicationService {
}
/**
* 注册
* App注册(手机号 + 注册短信验证码 + 密码)
*/
public void register(String username, String password) {
// 用户名或密码为空 错误
if (StringUtils.isAnyBlank(username, password)) {
throw new ServiceException("用户/密码必须填写");
public void register(LoginDTO loginDTO) {
String userPhone = loginDTO.getUserPhone(); // LoginDTO.java:27-28
String verificationCode = loginDTO.getVerificationCode(); // LoginDTO.java:30-31
String password = loginDTO.getPassword();
// 1. 基础校验
if (StringUtils.isAnyBlank(userPhone, verificationCode, password)) {
throw new ServiceException("手机号、验证码、密码必须填写");
}
if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|| username.length() > UserConstants.USERNAME_MAX_LENGTH) {
throw new ServiceException("账户长度必须在2到20个字符之间");
}
if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
throw new ServiceException("密码长度必须在5到20个字符之间");
if (!RegexUtil.isPhoneLegal(userPhone)) {
throw new DigitalLogisticsException(UserError.USER_PHONE_REG_ERROR);
}
// 注册用户信息
SysUser sysUser = new SysUser();
sysUser.setUserName(username);
sysUser.setNickName(username);
sysUser.setPassword(SecurityUtils.encryptPassword(password));
//TODO app注册没写
/*R<?> registerResult = userServiceFeign.registerUserInfo(sysUser, SecurityConstants.INNER);
// 2. 校验注册短信验证码(发送时写入 redis,见 SmsApplicationService.java:112, :165
String key = CacheConstants.SMS_REGISTER + userPhone; // "sms_register:" CacheConstants.java:51
Object cache = redisService.getCacheObject(key);
if (cache == null || !ObjectUtil.equal(String.valueOf(cache), verificationCode)) {
recordLogininfor(userPhone, Constants.LOGIN_FAIL, "注册验证码错误", 1);
throw new ServiceException("验证码错误或已过期");
}
// 3. 域名 → 一级组织(与 loginByVerificationCode :103-110 一致)
String path = loginDTO.getPath();
AjaxResult topOrganizationIdR = organizationServiceFeign.getOrganizationByPath(path);
if (ObjectUtil.isNull(topOrganizationIdR) || ObjectUtil.isNull(topOrganizationIdR.get("data"))) {
throw new ServiceException("组织不存在,请检查登录域名");
}
SysTenantsPo sysTenantsPo = JSON.parseObject(JSON.toJSONString(topOrganizationIdR.get("data")), SysTenantsPo.class);
Long topOrganizationId = sysTenantsPo.getOrganizationId();
Long selectedOrgId = loginDTO.getOrganizationId(); // LoginDTO.java:37-38App 选择组织时传
Long organizationId = (selectedOrgId != null) ? selectedOrgId : topOrganizationId;
// 4. 手机号已注册则直接提示(UserMapper.xml:199 按 user_phone+top_organization_id 查)
UserDTO query = new UserDTO();
query.setUserPhone(userPhone);
query.setTopOrganizationId(topOrganizationId);
R<LoginUser> exist = userServiceFeign.getUserByTopOrganizationUserPhone(query, SecurityConstants.INNER);
if (exist != null && exist.getData() != null && exist.getData().getUserPo() != null) {
throw new ServiceException("该手机号已注册,请直接登录");
}
// 5. 创建用户(复用验证码登录自动注册的同一链路:
// auth feign UserServiceFeign.java:47-48 → user-center UserAPI.java:332-336
// → UserApplicationService.registerAddUser:1820-1927,内部已处理默认角色/YMS同步)
UserDTO userDTO = new UserDTO();
userDTO.setUserPhone(userPhone);
userDTO.setUserPassword(SecurityUtils.encryptPassword(password));
userDTO.setTopOrganizationId(topOrganizationId);
userDTO.setOrganizationId(organizationId);
userDTO.setOrganizationName(StringUtils.isNotBlank(loginDTO.getOrganizationName())
? loginDTO.getOrganizationName() : sysTenantsPo.getOrganizationName());
R<LoginUser> registerResult = userServiceFeign.registerAddUser(userDTO, SecurityConstants.INNER);
if (R.FAIL == registerResult.getCode()) {
throw new ServiceException(registerResult.getMsg());
}*/
// recordLogininfor(username, Constants.REGISTER, "注册成功");
}
// 6. 注册成功后清掉验证码并记日志
redisService.deleteObject(key);
recordLogininfor(userPhone, Constants.REGISTER, "注册成功", 2);
}
/**
@@ -40,4 +40,7 @@ public class LoginDTO {
@ApiModelProperty(name = "组织名称(司机选择组织时传入)")
private String organizationName;
@ApiModelProperty(name = "微信标识")
private Integer wechatFlag;
}
@@ -72,9 +72,9 @@ public class AppTokenController extends BaseController {
@PostMapping("register")
public R<?> register(@RequestBody LoginDTO loginDTO) {
// 用户注册
tokenLoginApplicationService.register(loginDTO.getUsername(), loginDTO.getPassword());
return R.ok();
// 用户注册(手机号+短信验证码+密码)
tokenLoginApplicationService.register(loginDTO);
return R.ok("注册成功");
}
/**
@@ -28,6 +28,7 @@ import com.mhd.common.redis.service.RedisService;
import com.mhd.common.security.utils.password.PasswordUtil;
import com.mhd.system.api.RemoteLogService;
import com.mhd.system.api.domain.SysLogininforPo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -39,6 +40,7 @@ import javax.annotation.Resource;
* @description: 登录密码方法
* @date 2023/12/26 15:33
**/
@Slf4j
@Component
public class SysPasswordService
{
@@ -76,29 +78,31 @@ public class SysPasswordService
if (retryCount == null) {
retryCount = 0;
}
//TODO 从三方配置表中取值
SysDepartInterfaceInfoDTO sysDepartInterfaceInfoDTO = new SysDepartInterfaceInfoDTO();
sysDepartInterfaceInfoDTO.setTopOrganizationId(userPo.getTopOrganizationId());
sysDepartInterfaceInfoDTO.setInterfaceType("login");
sysDepartInterfaceInfoDTO.setInterfaceTypeKey("maxRetryCount");
List<SysDepartInterfaceInfoPo> sysDepartInterfaceInfoPos = thirdPartyServiceFeign.queryListByFeign(sysDepartInterfaceInfoDTO);
if (sysDepartInterfaceInfoPos.isEmpty()){
String errMsg = "缺少登录信息配置,请联系管理员";
recordLogininfor(userPo, Constants.LOGIN_FAIL, errMsg, 1);
throw new ServiceException(errMsg);
//TODO 从三方配置表中取值(失败时用默认值,保证登录错误提示可用)
int maxRetryCount = 5; // 默认:密码错5次锁定
long lockTime = 30L; // 默认:锁定30分钟
try {
SysDepartInterfaceInfoDTO dto = new SysDepartInterfaceInfoDTO();
dto.setTopOrganizationId(userPo.getTopOrganizationId());
dto.setInterfaceType("login");
dto.setInterfaceTypeKey("maxRetryCount");
List<SysDepartInterfaceInfoPo> pos = thirdPartyServiceFeign.queryListByFeign(dto);
if (pos != null && !pos.isEmpty()) {
maxRetryCount = Integer.parseInt(pos.get(0).getInterfaceTypeValue());
} else {
log.warn("组织[{}]缺少登录配置 login/maxRetryCount,使用默认值{}", userPo.getTopOrganizationId(), maxRetryCount);
}
dto.setInterfaceTypeKey("lockTime");
pos = thirdPartyServiceFeign.queryListByFeign(dto);
if (pos != null && !pos.isEmpty()) {
lockTime = Long.parseLong(pos.get(0).getInterfaceTypeValue());
} else {
log.warn("组织[{}]缺少登录配置 login/lockTime,使用默认值{}", userPo.getTopOrganizationId(), lockTime);
}
} catch (Exception e) {
log.warn("读取登录配置失败,使用默认重试策略", e);
}
SysDepartInterfaceInfoPo sysDepartInterfaceInfoPo = sysDepartInterfaceInfoPos.get(0);
Integer maxRetryCount = Integer.valueOf(sysDepartInterfaceInfoPo.getInterfaceTypeValue());
sysDepartInterfaceInfoDTO.setInterfaceTypeKey("lockTime");
sysDepartInterfaceInfoPos = thirdPartyServiceFeign.queryListByFeign(sysDepartInterfaceInfoDTO);
if (sysDepartInterfaceInfoPos.isEmpty()){
String errMsg = "缺少登录信息配置,请联系管理员";
recordLogininfor(userPo, Constants.LOGIN_FAIL, errMsg, 1);
throw new ServiceException(errMsg);
}
sysDepartInterfaceInfoPo = sysDepartInterfaceInfoPos.get(0);
Long lockTime = Long.valueOf(sysDepartInterfaceInfoPo.getInterfaceTypeValue());
if (retryCount >= maxRetryCount){
if (retryCount >= maxRetryCount) {
String errMsg = String.format("密码输入错误%s次,帐户锁定%s分钟", maxRetryCount, lockTime);
recordLogininfor(userPo, Constants.LOGIN_FAIL, errMsg, 1);
throw new ServiceException(errMsg);
+1 -1
View File
@@ -52,4 +52,4 @@ sso:
AwIDAQAB
-----END PUBLIC KEY-----
# SSO登录成功后跳转的前端页面地址
redirect-url: "https://diglog.namkwong.com.mo/main_app/sso"
redirect-url: "https://diglog.namkwong.com.mo/main_app/sso"
@@ -4,6 +4,12 @@ import com.mhd.bi.domain.biPlatformRealData.repository.po.RealPlatformVehiclePO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealPlatformOverviewPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealVehicleStatusPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealTaskLoadPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealBusinessTypeStatPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealPlatformFloorStatPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealPlatformInfoPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealOnTimeStatPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealTaskTypeStatPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealMonthlyTaskDurationPO;
import java.util.List;
@@ -23,12 +29,30 @@ public interface BiPlatformRealDataMapper {
/** 今日作业任务数 */
Long countTodayTasks();
/** 车辆状态统计(今日) */
/** 车辆状态统计(今日,含叫号状态合计 callStatusTotal = 已签到+已叫号+已过号 */
RealVehicleStatusPO queryVehicleStatusToday();
/** 本月任务量 */
Long countMonthTasks();
/** 本月任务平均时长 / 完成率统计 */
RealMonthlyTaskDurationPO queryMonthTaskStats();
/** 任务负荷(今日 24 小时) */
List<RealTaskLoadPO> queryTaskLoadToday();
/** 今日预约-业务类型统计(按 businessName 分组统计今日预约数量) */
List<RealBusinessTypeStatPO> queryTodayBusinessTypeStats();
/** 今日作业准时率统计(total 今日预约总数;onTimeCount 今日准时车辆数) */
RealOnTimeStatPO queryTodayOnTimeStats();
/** 任务类型统计(本月 YMS QMS_MAIN_BUSINESS 预约按业务类型 BUSINESS_NAME 分组) */
List<RealTaskTypeStatPO> queryMonthTransportOrderByType();
/** 月台监控-按楼层统计月台车辆(楼层、月台数量、今日预约车辆、当前作业车辆) */
List<RealPlatformFloorStatPO> queryPlatformFloorStats();
/** 月台管理列表(YMS qms_window_info,含机构/鹤位/状态/楼层/UUID 等完整信息) */
List<RealPlatformInfoPO> queryPlatformInfoList();
}
@@ -0,0 +1,20 @@
package com.mhd.bi.domain.biPlatformRealData.repository.po;
import lombok.Data;
import java.io.Serializable;
/**
* 今日预约-业务类型统计(真实数据,来自 TEST_NGWL_YMS.QMS_MAIN_BUSINESS
* 按业务类型分组统计今日预约数量
*/
@Data
public class RealBusinessTypeStatPO implements Serializable {
private static final long serialVersionUID = 1L;
/** 业务类型名称(qms_main_business.businessName */
private String businessName;
/** 今日预约数量 */
private Long count;
}
@@ -0,0 +1,29 @@
package com.mhd.bi.domain.biPlatformRealData.repository.po;
import lombok.Data;
import java.io.Serializable;
/**
* 本月任务平均时长 / 完成率统计(真实数据,来自 TEST_NGWL_YMS.QMS_MAIN_BUSINESS
* avgDurationHours:累计 (EXIT_TIME - ENTRY_TIME) 小时数(仅 ENTRY_TIME/EXIT_TIME 都不为空时参与)
* durationCount:参与平均时长统计的有效条数
* completedCount:已出场(EXIT_TIME IS NOT NULL)且预约日期在本月的数量
* totalCount:本月(TARGET_DATE)预约总数量
*/
@Data
public class RealMonthlyTaskDurationPO implements Serializable {
private static final long serialVersionUID = 1L;
/** 平均作业时长(小时) */
private Double avgDurationHours;
/** 参与平均时长统计的有效条数 */
private Long durationCount;
/** 本月已出场数量 */
private Long completedCount;
/** 本月预约总数量(按 TARGET_DATE 统计) */
private Long totalCount;
}
@@ -0,0 +1,20 @@
package com.mhd.bi.domain.biPlatformRealData.repository.po;
import lombok.Data;
import java.io.Serializable;
/**
* 今日作业准时率统计(真实数据,来自 TEST_NGWL_YMS.QMS_MAIN_BUSINESS
* 准时率 = 今日实际进场时间在预约日期当天的车辆数 / 今日预约总车辆数 × 100%
*/
@Data
public class RealOnTimeStatPO implements Serializable {
private static final long serialVersionUID = 1L;
/** 今日预约总车辆数 */
private Long total;
/** 今日准时车辆数(已进场且实际进场日期 = 预约日期) */
private Long onTimeCount;
}
@@ -0,0 +1,26 @@
package com.mhd.bi.domain.biPlatformRealData.repository.po;
import lombok.Data;
import java.io.Serializable;
/**
* 月台监控-月台车辆统计(按楼层分组)
* 数据来源:TEST_NGWL_YMS.QMS_WINDOW_INFO(楼层+月台)与 QMS_MAIN_BUSINESS(当日预约/作业)
*/
@Data
public class RealPlatformFloorStatPO implements Serializable {
private static final long serialVersionUID = 1L;
/** 楼层(从叫号月台 WINDOW_NAME 解析,如 S1-01 -> F1 */
private String floorNo;
/** 该楼层的月台数量 */
private Long platformCount;
/** 今日预约车辆数(按 WINDOW_NAME 关联楼层) */
private Long todayReservedVehicles;
/** 当前作业车辆数(FLOW_STATUS IN 5,6,7,8,9,10 */
private Long operatingVehicles;
}
@@ -0,0 +1,59 @@
package com.mhd.bi.domain.biPlatformRealData.repository.po;
import lombok.Data;
import java.io.Serializable;
/**
* 月台管理列表(真实数据,来自 TEST_NGWL_YMS.QMS_WINDOW_INFO
* 对应 YMS qms_window_info 月台/鹤位窗口表
*/
@Data
public class RealPlatformInfoPO implements Serializable {
private static final long serialVersionUID = 1L;
/** 主键 */
private Long id;
/** 机构代码(org_no */
private String orgNo;
/** 机构名称(dept_name */
private String deptName;
/** 鹤位号/月台号(window_number */
private String windowNumber;
/** 鹤位名称/月台名称(window_name */
private String windowName;
/** 优先级别(priority_set */
private String prioritySet;
/** 鹤位状态/月台状态(running_status */
private String runningStatus;
/** uuid */
private String uuid;
/** 鹤位屏名字集合/绑定月台屏(berr_names */
private String berrNames;
/** 最新业务状态(new_flow_status */
private String newFlowStatus;
/** 月台图片(guide_images */
private String guideImages;
/** 月台楼层(platform_floor */
private String platformFloor;
/** 作业状态 0-空闲 1-占用(job_status */
private Integer jobStatus;
/** 创建人(create_by */
private String createBy;
/** 创建时间(create_time */
private String createTime;
}
@@ -0,0 +1,20 @@
package com.mhd.bi.domain.biPlatformRealData.repository.po;
import lombok.Data;
import java.io.Serializable;
/**
* 任务类型统计(真实数据,来自 NGWL_TEST_YMS.QMS_MAIN_BUSINESS 预约登记表)
* 按本月预约车辆数(TARGET_DATE 在当前月)以 BUSINESS_NAME 业务类型分组
*/
@Data
public class RealTaskTypeStatPO implements Serializable {
private static final long serialVersionUID = 1L;
/** 业务类型名称(qms_main_business.BUSINESS_NAME */
private String businessName;
/** 该业务类型本月预约车辆数 */
private Long count;
}
@@ -16,6 +16,12 @@ public class RealVehicleStatusPO implements Serializable {
/** 当前未签到 */
private Long notCheckedIn;
/** 当前等待作业(FLOW_STATUS IN '1','2','3' 已预约/已签到/已叫号) */
private Long waiting;
/** 叫号状态合计:已签到 + 已叫号 + 已过号(FLOW_STATUS IN '2','3','4',预约日期为今天) */
private Long callStatusTotal;
/** 当前作业中 */
private Long operatingVehicles;
@@ -1,22 +1,38 @@
package com.mhd.bi.domain.biPlatformRealData.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mhd.bi.domain.biPlatformRealData.repository.mapper.BiPlatformRealDataMapper;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealPlatformVehiclePO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealPlatformOverviewPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealVehicleStatusPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealTaskLoadPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealBusinessTypeStatPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealPlatformFloorStatPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealPlatformInfoPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealOnTimeStatPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealTaskTypeStatPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealMonthlyTaskDurationPO;
import com.mhd.bi.domain.biPlatformSurveillance.repository.mapper.BiPlatformSurveillanceReportMapper;
import com.mhd.bi.domain.biSnapshotSync.repository.mapper.BiSnapshotWriteMapper;
import com.mhd.bi.domain.biSnapshotSync.support.BiSnapshotWeeklyRandomDataBuilder;
import com.mhd.bi.interfaces.facadeApi.biReport.vo.platform.PlatformSurveillanceVO;
import com.mhd.bi.interfaces.facadeApi.biReport.vo.platform.PlatformVehicleItemVO;
import com.mhd.bi.interfaces.facadeApi.biReport.vo.platform.TaskLoadItemVO;
import com.mhd.bi.interfaces.facadeApi.biReport.vo.platform.TaskTypeStatisticsVO;
import com.mhd.bi.interfaces.facadeApi.biReport.vo.platform.TaskTypeCountItemVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
/**
@@ -30,6 +46,128 @@ public class BiPlatformRealDataService {
@Resource
private BiPlatformRealDataMapper biPlatformRealDataMapper;
@Resource
private BiSnapshotWriteMapper biSnapshotWriteMapper;
@Resource
private BiPlatformSurveillanceReportMapper biPlatformSurveillanceReportMapper;
@Resource
private ObjectMapper objectMapper;
private static final String REMARK_REALTIME = "realtime platform-surveillance";
/**
* 实时组装月台监测数据;仅当真实业务数据(YMS 库查出的字段)发生变化时,
* 才覆盖更新 BI_PLATFORM_SURVEILLANCE_SNAPSHOT 快照表最新一条后返回。
* 写死字段变化不触发更新;表中尚无记录时插入一条兜底。
*/
public PlatformSurveillanceVO buildRealTimePlatformSurveillanceAndPersist() {
PlatformSurveillanceVO vo = buildRealTimePlatformSurveillance();
try {
String fullJson = objectMapper.writeValueAsString(vo);
String fingerprint = realDataFingerprint(vo);
String latestJson = biPlatformSurveillanceReportMapper.selectLatestPayloadJson();
if (StringUtils.isNotBlank(latestJson)) {
PlatformSurveillanceVO latestVo = objectMapper.readValue(latestJson.trim(), PlatformSurveillanceVO.class);
if (Objects.equals(fingerprint, realDataFingerprint(latestVo))) {
log.info("BI 月台监测真实业务数据未变化,跳过落库");
return vo;
}
}
int rows = biSnapshotWriteMapper.updateLatestPlatformSurveillanceSnapshot(fullJson, REMARK_REALTIME);
if (rows < 1) {
// 表中无最新记录可更新,插入一条兜底
biSnapshotWriteMapper.insertPlatformSurveillanceSnapshot(fullJson, REMARK_REALTIME);
}
log.info("BI 月台监测实时数据已落库(覆盖最新, 数据变化), affectedRows={}", rows);
} catch (Exception e) {
log.error("BI 月台监测实时数据落库失败", e);
}
return vo;
}
/**
* 提取月台监测的「真实业务数据」指纹(仅含 YMS 库查出的字段),用于判断业务数据是否变化。
* 写死/随机字段不纳入指纹,故其变化不会触发更新。
*/
private String realDataFingerprint(PlatformSurveillanceVO vo) throws Exception {
Map<String, Object> fp = new LinkedHashMap<>();
if (vo.getPlatformOverview() != null) {
fp.put("totalPlatforms", vo.getPlatformOverview().getTotalPlatforms());
fp.put("operatingPlatforms", vo.getPlatformOverview().getOperatingPlatforms());
fp.put("currentOccupancyRate", vo.getPlatformOverview().getCurrentOccupancyRate());
fp.put("todayTasks", vo.getPlatformOverview().getTodayTasks());
}
if (vo.getEfficiencyStatistics() != null) {
fp.put("platformOnTimeRate", vo.getEfficiencyStatistics().getPlatformOnTimeRate());
}
if (vo.getVehicleStatus() != null) {
fp.put("todayReservedVehicles", vo.getVehicleStatus().getTodayReservedVehicles());
fp.put("notCheckedIn", vo.getVehicleStatus().getNotCheckedIn());
fp.put("waiting", vo.getVehicleStatus().getWaiting());
fp.put("callStatusTotal", vo.getVehicleStatus().getCallStatusTotal());
fp.put("operatingVehicles", vo.getVehicleStatus().getOperatingVehicles());
fp.put("completedVehicles", vo.getVehicleStatus().getCompletedVehicles());
}
if (vo.getMonthlyTaskStatistics() != null) {
fp.put("taskTotal", vo.getMonthlyTaskStatistics().getTaskTotal());
fp.put("avgTaskDuration", vo.getMonthlyTaskStatistics().getAvgTaskDuration());
fp.put("taskCompletionRate", vo.getMonthlyTaskStatistics().getTaskCompletionRate());
}
if (vo.getVehicles() != null) {
List<Map<String, Object>> vehicles = new ArrayList<>();
for (PlatformVehicleItemVO v : vo.getVehicles()) {
Map<String, Object> m = new LinkedHashMap<>();
m.put("appointmentTime", v.getAppointmentTime());
m.put("workFloor", v.getWorkFloor());
m.put("plateNumber", v.getPlateNumber());
m.put("status", v.getStatus());
m.put("platformId", v.getPlatformId());
m.put("platformDuration", v.getPlatformDuration());
vehicles.add(m);
}
fp.put("vehicles", vehicles);
}
if (vo.getTaskLoadStatistics() != null) {
List<Map<String, Object>> loads = new ArrayList<>();
for (TaskLoadItemVO item : vo.getTaskLoadStatistics()) {
Map<String, Object> m = new LinkedHashMap<>();
m.put("time", item.getTime());
m.put("taskCount", item.getTaskCount());
loads.add(m);
}
fp.put("taskLoadStatistics", loads);
}
if (vo.getTaskTypeStatistics() != null) {
Map<String, Object> m = new LinkedHashMap<>();
m.put("totalTasks", vo.getTaskTypeStatistics().getTotalTasks());
if (vo.getTaskTypeStatistics().getTaskTypes() != null) {
List<Map<String, Object>> types = new ArrayList<>();
for (TaskTypeCountItemVO it : vo.getTaskTypeStatistics().getTaskTypes()) {
Map<String, Object> tm = new LinkedHashMap<>();
tm.put("type", it.getType());
tm.put("count", it.getCount());
types.add(tm);
}
m.put("taskTypes", types);
}
fp.put("taskTypeStatistics", m);
}
// 月台告警统计:按业务要求恒为 0,纳入指纹以确保旧快照(可能含非0写死值)能被覆盖更新为 0
if (vo.getPlatformAlarmStatistics() != null) {
fp.put("todayAlarmTotal", vo.getPlatformAlarmStatistics().getTodayAlarmTotal());
fp.put("handled", vo.getPlatformAlarmStatistics().getHandled());
fp.put("unhandled", vo.getPlatformAlarmStatistics().getUnhandled());
}
return objectMapper.writeValueAsString(fp);
}
public List<RealPlatformVehiclePO> queryVehicles() {
try {
return biPlatformRealDataMapper.queryVehicles();
@@ -67,12 +205,34 @@ public class BiPlatformRealDataService {
}
}
/**
* 今日作业准时率 = 今日准时车辆数 / 今日有入场时间的车辆数 × 100%(四舍五入取整)
* 准时:实际进入时间 < 预约日期(TARGET_DATE) + 预约结束时间(BOOKING_END_TIME)。
* 分子分母都只统计有入场时间(ENTRY_TIME 不为空)的车辆;分母为 0 时返回 0。
*/
public int calcTodayOnTimeRate() {
try {
RealOnTimeStatPO po = biPlatformRealDataMapper.queryTodayOnTimeStats();
if (po == null) return 0;
long total = po.getTotal() == null ? 0L : po.getTotal();
long onTime = po.getOnTimeCount() == null ? 0L : po.getOnTimeCount();
if (total <= 0) return 0;
// 四舍五入到整数百分比(取整)
return (int) Math.round(((double) onTime / (double) total) * 100.0);
} catch (Exception e) {
log.error("【YMS】计算今日作业准时率失败: {}", e.getMessage(), e);
return 0;
}
}
public RealVehicleStatusPO queryVehicleStatusToday() {
try {
RealVehicleStatusPO po = biPlatformRealDataMapper.queryVehicleStatusToday();
if (po == null) po = new RealVehicleStatusPO();
if (po.getTodayReservedVehicles() == null) po.setTodayReservedVehicles(0L);
if (po.getNotCheckedIn() == null) po.setNotCheckedIn(0L);
if (po.getWaiting() == null) po.setWaiting(0L);
if (po.getCallStatusTotal() == null) po.setCallStatusTotal(0L);
if (po.getOperatingVehicles() == null) po.setOperatingVehicles(0L);
if (po.getCompletedVehicles() == null) po.setCompletedVehicles(0L);
return po;
@@ -81,10 +241,14 @@ public class BiPlatformRealDataService {
RealVehicleStatusPO po = new RealVehicleStatusPO();
po.setTodayReservedVehicles(0L);
po.setNotCheckedIn(0L);
po.setWaiting(0L);
po.setCallStatusTotal(0L);
po.setOperatingVehicles(0L);
po.setCompletedVehicles(0L);
return po;
}
}
public long countMonthTasks() {
@@ -97,6 +261,21 @@ public class BiPlatformRealDataService {
}
}
/**
* 本月任务平均时长 / 完成率统计:
* - avgDurationHours:累计(EXIT_TIME - ENTRY_TIME)小时数 / 参与数;任一为空的行不参与
* - taskCompletionRate:已完成(EXIT_TIME 不为空 且 预约日期在本月)/ 本月预约总数量 ×100
* 任一查询失败均返回 null,由调用方决定是否覆盖模板值。
*/
public RealMonthlyTaskDurationPO queryMonthTaskStats() {
try {
return biPlatformRealDataMapper.queryMonthTaskStats();
} catch (Exception e) {
log.error("【YMS】统计本月任务平均时长/完成率失败: {}", e.getMessage(), e);
return null;
}
}
public List<RealTaskLoadPO> queryTaskLoadToday() {
try {
return biPlatformRealDataMapper.queryTaskLoadToday();
@@ -107,14 +286,85 @@ public class BiPlatformRealDataService {
}
/**
* 实时组装月台监测数据:真实字段查 YMS 库,写死字段保留静态值。
* 任务类型统计:本月(YMS QMS_MAIN_BUSINESS)预约车辆按业务类型 BUSINESS_NAME 分组;
* 返回 {totalTasks: 当月总预约车辆数, taskTypes: [{type: 业务类型名称, count: 该类型预约数}]}
* 按真实 BUSINESS_NAME 分组输出,不固定兜底类别;当月无数据或查询失败时返回空列表。
*/
public TaskTypeStatisticsVO queryMonthTransportOrderByType() {
List<TaskTypeCountItemVO> items = new ArrayList<>();
int total = 0;
try {
List<RealTaskTypeStatPO> rows = biPlatformRealDataMapper.queryMonthTransportOrderByType();
if (rows != null) {
for (RealTaskTypeStatPO r : rows) {
if (r == null || r.getBusinessName() == null || r.getCount() == null) {
continue;
}
int c = r.getCount().intValue();
items.add(TaskTypeCountItemVO.builder().type(r.getBusinessName()).count(c).build());
total += c;
}
}
} catch (Exception e) {
log.error("【YMS】统计当月预约业务类型失败: {}", e.getMessage(), e);
// 查询失败返回空列表(前端会显示空)
items = new ArrayList<>();
total = 0;
}
return TaskTypeStatisticsVO.builder().totalTasks(total).taskTypes(items).build();
}
/**
* 今日预约-业务类型统计:按业务类型分组统计今日预约数量。
* 例:今日预约航空打板 3 条 → 返回 {businessName:"航空打板", count:3}
*/
public List<RealBusinessTypeStatPO> queryTodayBusinessTypeStats() {
try {
return biPlatformRealDataMapper.queryTodayBusinessTypeStats();
} catch (Exception e) {
log.error("【YMS】统计今日业务类型失败: {}", e.getMessage(), e);
return java.util.Collections.emptyList();
}
}
/**
* 月台监控-按楼层统计月台车辆:返回 {floorNo, platformCount, todayReservedVehicles, operatingVehicles}
* 实时查 YMS 库,不落库。
*/
public List<RealPlatformFloorStatPO> queryPlatformFloorStats() {
try {
return biPlatformRealDataMapper.queryPlatformFloorStats();
} catch (Exception e) {
log.error("【YMS】月台监控按楼层统计失败: {}", e.getMessage(), e);
return java.util.Collections.emptyList();
}
}
/**
* 月台管理列表:实时查 YMS qms_window_info,返回月台/鹤位窗口完整信息,不落库。
*/
public List<RealPlatformInfoPO> queryPlatformInfoList() {
try {
return biPlatformRealDataMapper.queryPlatformInfoList();
} catch (Exception e) {
log.error("【YMS】查询月台管理列表失败: {}", e.getMessage(), e);
return java.util.Collections.emptyList();
}
}
/**
* 实时组装月台监测数据:原则「有真实数据源用真实,无真实数据源用写死模拟」。
* 有真实数据源的字段(vehicles / platformOverview / vehicleStatus / taskLoad / taskTotal
* 无条件用 YMS 真实查询覆盖,真实为空即为空,不用模板兜底;
* 无真实数据源的字段(效率统计、趋势、告警、任务类型等)保留模板写死值。
* 每次调用实时查库,不经过快照表。
*/
public PlatformSurveillanceVO buildRealTimePlatformSurveillance() {
Random r = new Random();
// 固定种子生成写死模板:仅用于没有真实数据源的字段
Random r = new Random(20260825L);
PlatformSurveillanceVO template = BiSnapshotWeeklyRandomDataBuilder.platformSurveillance(r);
// 作业车辆列表
// 作业车辆列表:真实数据源,无条件覆盖(真实为空则返回空列表,不用模板模拟)
List<RealPlatformVehiclePO> realVehicles = queryVehicles();
List<PlatformVehicleItemVO> vehicleVos = new ArrayList<>();
for (RealPlatformVehiclePO rv : realVehicles) {
@@ -127,56 +377,93 @@ public class BiPlatformRealDataService {
v.setPlatformDuration(rv.getPlatformDuration() == null ? 0 : rv.getPlatformDuration().intValue());
vehicleVos.add(v);
}
if (!vehicleVos.isEmpty()) {
template.setVehicles(vehicleVos);
}
template.setVehicles(vehicleVos);
// 月台实时概览
RealPlatformOverviewPO ov = queryPlatformOverview();
// 月台实时概览:真实数据源,无条件覆盖
// 总月台 / 占用月台 来源:YMS 月台管理列表 QMS_WINDOW_INFOJOB_STATUS=1 表示占用)
// 月台占用率 = 占用 / 全部月台
// 今日作业任务 来源:QMS_MAIN_BUSINESS 按预约时间 TARGET_DATE = 今天 的全部数量
// 作业准时率 = 今日(实际进场时间在预约日期当天的车辆数) / 今日预约车辆总数 × 100%
if (template.getPlatformOverview() != null) {
template.getPlatformOverview().setTotalPlatforms(ov.getTotalPlatforms().intValue());
template.getPlatformOverview().setOperatingPlatforms(ov.getOperatingPlatforms().intValue());
int total = ov.getTotalPlatforms().intValue();
int occ = ov.getOperatingPlatforms().intValue();
List<RealPlatformInfoPO> platformList = queryPlatformInfoList();
int total = platformList == null ? 0 : platformList.size();
int occ = 0;
if (platformList != null) {
for (RealPlatformInfoPO p : platformList) {
if (p != null && p.getJobStatus() != null && p.getJobStatus() == 1) {
occ++;
}
}
}
double rate = total > 0 ? Math.round(((double) occ / total) * 100.0 * 100.0) / 100.0 : 0d;
int todayTasks = (int) countTodayTasks();
template.getPlatformOverview().setTotalPlatforms(total);
template.getPlatformOverview().setOperatingPlatforms(occ);
template.getPlatformOverview().setCurrentOccupancyRate(rate);
}
long todayTasks = countTodayTasks();
if (template.getPlatformOverview() != null) {
template.getPlatformOverview().setTodayTasks((int) todayTasks);
template.getPlatformOverview().setTodayTasks(todayTasks);
// 作业准时率(实际进入时间 < 预约日期+预约结束时间 的车辆 / 今日预约总数 × 100% 取整)
if (template.getEfficiencyStatistics() != null) {
template.getEfficiencyStatistics().setPlatformOnTimeRate(calcTodayOnTimeRate());
}
}
// 作业车辆状态统计
RealVehicleStatusPO vs = queryVehicleStatusToday();
// 作业车辆状态统计:真实数据源,无条件覆盖(含等待作业 waiting = 今日预约且 已预约/已签到/已叫号)
if (template.getVehicleStatus() != null) {
RealVehicleStatusPO vs = queryVehicleStatusToday();
template.getVehicleStatus().setTodayReservedVehicles(vs.getTodayReservedVehicles().intValue());
template.getVehicleStatus().setNotCheckedIn(vs.getNotCheckedIn().intValue());
template.getVehicleStatus().setWaiting(vs.getWaiting().intValue());
// 叫号状态合计:已签到 + 已叫号 + 已过号(一个字段表示三个状态的总数量)
template.getVehicleStatus().setCallStatusTotal(vs.getCallStatusTotal().intValue());
template.getVehicleStatus().setOperatingVehicles(vs.getOperatingVehicles().intValue());
template.getVehicleStatus().setCompletedVehicles(vs.getCompletedVehicles().intValue());
}
// 本月任务统计 taskTotal
long monthTasks = countMonthTasks();
// 本月任务统计:taskTotal / avgTaskDuration / taskCompletionRate 均有真实数据源,无条件覆盖;YoY 保留模板写死值
if (template.getMonthlyTaskStatistics() != null) {
template.getMonthlyTaskStatistics().setTaskTotal((int) monthTasks);
template.getMonthlyTaskStatistics().setTaskTotal((int) countMonthTasks());
RealMonthlyTaskDurationPO monthStats = queryMonthTaskStats();
if (monthStats != null) {
if (monthStats.getAvgDurationHours() != null) {
// 保留两位小数
double v = monthStats.getAvgDurationHours();
template.getMonthlyTaskStatistics().setAvgTaskDuration(Math.round(v * 100.0) / 100.0);
}
Long total = monthStats.getTotalCount();
Long completed = monthStats.getCompletedCount();
if (total != null && total > 0 && completed != null) {
int rate = (int) Math.round(((double) completed.longValue() / total.longValue()) * 100.0);
template.getMonthlyTaskStatistics().setTaskCompletionRate(rate);
} else {
template.getMonthlyTaskStatistics().setTaskCompletionRate(0);
}
}
}
// 任务负荷(今日 24 小时)
// 任务负荷:有真实数据源;真实为空则清空不用模板模拟,有数据则按真实小时覆盖
List<RealTaskLoadPO> loads = queryTaskLoadToday();
if (!loads.isEmpty() && template.getTaskLoadStatistics() != null) {
if (loads.isEmpty()) {
template.setTaskLoadStatistics(new ArrayList<>());
} else if (template.getTaskLoadStatistics() != null) {
Map<Integer, Long> hourMap = new HashMap<>();
for (RealTaskLoadPO l : loads) {
hourMap.put(l.getHourOfDay(), l.getTaskCount());
}
for (TaskLoadItemVO item : template.getTaskLoadStatistics()) {
Integer hour = parseHourFromLabel(item.getTime());
if (hour != null) {
Long n = hourMap.get(hour);
if (n != null) item.setTaskCount(n.intValue());
if (hour != null && hourMap.containsKey(hour)) {
item.setTaskCount(hourMap.get(hour).intValue());
}
}
}
// 任务类型统计:本月 YMS QMS_MAIN_BUSINESS 按业务类型 BUSINESS_NAME 分组(真实业务数据)
template.setTaskTypeStatistics(queryMonthTransportOrderByType());
return template;
}
@@ -87,7 +87,9 @@ public final class BiReportSnapshotMapperSupport {
.platformOverview(PlatformOverviewVO.builder()
.totalPlatforms(0).currentOccupancyRate(0d).operatingPlatforms(0).todayTasks(0).build())
.vehicleStatus(VehicleStatusVO.builder()
.todayReservedVehicles(0).notCheckedIn(0).operatingVehicles(0).completedVehicles(0).build())
.todayReservedVehicles(0).notCheckedIn(0).waiting(0)
.callStatusTotal(0)
.operatingVehicles(0).completedVehicles(0).build())
.avgOperationDurationTrend(Collections.emptyList())
.monthlyTaskStatistics(MonthlyTaskStatisticsVO.builder()
.taskTotal(0).taskTotalYoY(0).avgTaskDuration(0d).avgDurationYoY(0)
@@ -13,6 +13,8 @@ public interface BiSnapshotWriteMapper {
int insertPlatformSurveillanceSnapshot(@Param("payloadJson") String payloadJson, @Param("remark") String remark);
int updateLatestPlatformSurveillanceSnapshot(@Param("payloadJson") String payloadJson, @Param("remark") String remark);
int insertOverallOperationSnapshot(@Param("payloadJson") String payloadJson, @Param("remark") String remark);
int insertWarehouseOperationZoneSnapshot(@Param("payloadJson") String payloadJson, @Param("remark") String remark);
@@ -16,7 +16,7 @@ import com.mhd.bi.interfaces.facadeApi.biReport.vo.warehouseZone.WarehouseOperat
import com.mhd.bi.interfaces.facadeApi.biReport.vo.platform.PlatformVehicleItemVO;
import com.mhd.bi.domain.biPlatformRealData.service.BiPlatformRealDataService;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealPlatformVehiclePO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealPlatformOverviewPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealPlatformInfoPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealVehicleStatusPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealTaskLoadPO;
import lombok.RequiredArgsConstructor;
@@ -150,7 +150,8 @@ public class BiReportSnapshotSyncService {
/** TODO 业务落地:部分字段改为真实数据,其余仍写死。 */
protected PlatformSurveillanceVO buildPlatformSurveillanceSnapshot() {
Random r = new Random();
// 固定种子,保证写死字段每次生成值一致(真实字段随后实时覆盖)
Random r = new Random(20260825L);
// 写死数据复用原 builder
PlatformSurveillanceVO template = BiSnapshotWeeklyRandomDataBuilder.platformSurveillance(r);
@@ -172,18 +173,31 @@ public class BiReportSnapshotSyncService {
}
// ====== 真实数据:月台实时概览 ======
RealPlatformOverviewPO ov = biPlatformRealDataService.queryPlatformOverview();
// 总月台 / 占用月台 来源:YMS 月台管理列表 QMS_WINDOW_INFOJOB_STATUS=1 表示占用)
// 月台占用率 = 占用 / 全部月台
// 今日作业任务 来源:QMS_MAIN_BUSINESS 按预约时间 TARGET_DATE = 今天 的全部数量
if (template.getPlatformOverview() != null) {
template.getPlatformOverview().setTotalPlatforms(ov.getTotalPlatforms().intValue());
template.getPlatformOverview().setOperatingPlatforms(ov.getOperatingPlatforms().intValue());
int total = ov.getTotalPlatforms().intValue();
int occ = ov.getOperatingPlatforms().intValue();
List<RealPlatformInfoPO> platformList = biPlatformRealDataService.queryPlatformInfoList();
int total = platformList == null ? 0 : platformList.size();
int occ = 0;
if (platformList != null) {
for (RealPlatformInfoPO p : platformList) {
if (p != null && p.getJobStatus() != null && p.getJobStatus() == 1) {
occ++;
}
}
}
double rate = total > 0 ? Math.round(((double) occ / total) * 100.0 * 100.0) / 100.0 : 0d;
int todayTasks = (int) biPlatformRealDataService.countTodayTasks();
template.getPlatformOverview().setTotalPlatforms(total);
template.getPlatformOverview().setOperatingPlatforms(occ);
template.getPlatformOverview().setCurrentOccupancyRate(rate);
}
long todayTasks = biPlatformRealDataService.countTodayTasks();
if (template.getPlatformOverview() != null) {
template.getPlatformOverview().setTodayTasks((int) todayTasks);
template.getPlatformOverview().setTodayTasks(todayTasks);
// 作业准时率 = 实际进入时间 < 预约日期+预约结束时间 的车辆 / 今日预约总数 × 100%(取整)
if (template.getEfficiencyStatistics() != null) {
template.getEfficiencyStatistics().setPlatformOnTimeRate(biPlatformRealDataService.calcTodayOnTimeRate());
}
}
// ====== 真实数据:作业车辆状态统计 ======
@@ -191,14 +205,36 @@ public class BiReportSnapshotSyncService {
if (template.getVehicleStatus() != null) {
template.getVehicleStatus().setTodayReservedVehicles(vs.getTodayReservedVehicles().intValue());
template.getVehicleStatus().setNotCheckedIn(vs.getNotCheckedIn().intValue());
template.getVehicleStatus().setWaiting(vs.getWaiting().intValue());
// 叫号状态合计:已签到 + 已叫号 + 已过号(一个字段表示三个状态的总数量)
template.getVehicleStatus().setCallStatusTotal(vs.getCallStatusTotal() == null ? 0 : vs.getCallStatusTotal().intValue());
template.getVehicleStatus().setOperatingVehicles(vs.getOperatingVehicles().intValue());
template.getVehicleStatus().setCompletedVehicles(vs.getCompletedVehicles().intValue());
}
// ====== 真实数据:本月任务统计 taskTotal ======
// ====== 真实数据:本月任务统计 taskTotal / avgTaskDuration / taskCompletionRate ======
long monthTasks = biPlatformRealDataService.countMonthTasks();
if (template.getMonthlyTaskStatistics() != null) {
template.getMonthlyTaskStatistics().setTaskTotal((int) monthTasks);
com.mhd.bi.domain.biPlatformRealData.repository.po.RealMonthlyTaskDurationPO monthStats =
biPlatformRealDataService.queryMonthTaskStats();
if (monthStats != null) {
if (monthStats.getAvgDurationHours() != null) {
double v = monthStats.getAvgDurationHours();
template.getMonthlyTaskStatistics().setAvgTaskDuration(Math.round(v * 100.0) / 100.0);
}
Long total = monthStats.getTotalCount();
Long completed = monthStats.getCompletedCount();
if (total != null && total > 0 && completed != null) {
int rate = (int) Math.round(((double) completed.longValue() / total.longValue()) * 100.0);
template.getMonthlyTaskStatistics().setTaskCompletionRate(rate);
} else {
template.getMonthlyTaskStatistics().setTaskCompletionRate(0);
}
}
}
// ====== 真实数据:任务负荷(今日 24 小时) ======
@@ -217,6 +253,9 @@ public class BiReportSnapshotSyncService {
}
}
// ====== 真实数据:任务类型统计(本月 YMS QMS_MAIN_BUSINESS 按业务类型 BUSINESS_NAME 分组) ======
template.setTaskTypeStatistics(biPlatformRealDataService.queryMonthTransportOrderByType());
return template;
}
@@ -155,10 +155,13 @@ public final class BiSnapshotWeeklyRandomDataBuilder {
VehicleStatusVO vs = VehicleStatusVO.builder()
.todayReservedVehicles(Math.max(0, jitterInt(Baseline.P_RESV, r)))
.notCheckedIn(Math.max(0, jitterInt(Baseline.P_NC, r)))
.waiting(Math.max(0, jitterInt(Baseline.P_WAIT, r)))
.callStatusTotal(Math.max(0, jitterInt(Baseline.P_CALL_STATUS_TOTAL, r)))
.operatingVehicles(Math.max(0, jitterInt(Baseline.P_OPV, r)))
.completedVehicles(Math.max(0, jitterInt(Baseline.P_DONE, r)))
.build();
List<AvgOperationDurationTrendItemVO> avgTrend = new ArrayList<>();
for (int i = 0; i < Baseline.P_MO_LABELS.length; i++) {
avgTrend.add(AvgOperationDurationTrendItemVO.builder()
@@ -185,13 +188,8 @@ public final class BiSnapshotWeeklyRandomDataBuilder {
.build());
}
// 月台告警:按业务要求,全部置 0(趋势/统计/分布均不生成写死数据)
List<PlatformAlarmTrendItemVO> alarmTrend = new ArrayList<>();
for (int i = 0; i < Baseline.P_AL_D.length; i++) {
alarmTrend.add(PlatformAlarmTrendItemVO.builder()
.date(Baseline.P_AL_D[i])
.alarmCount(Math.max(0, jitterInt(Baseline.P_AL_C[i], r)))
.build());
}
List<TaskTypeCountItemVO> typeCounts = new ArrayList<>();
for (int i = 0; i < Baseline.P_TT_TYPES.length; i++) {
@@ -201,18 +199,11 @@ public final class BiSnapshotWeeklyRandomDataBuilder {
.build());
}
int todayAl = Math.max(0, jitterInt(Baseline.P_AL_TOT, r));
int handled = Math.max(0, jitterInt(Baseline.P_AL_H, r));
handled = Math.min(handled, todayAl);
int unhandled = Math.max(0, todayAl - handled);
int todayAl = 0;
int handled = 0;
int unhandled = 0;
List<PlatformAlarmDistItemVO> dist = new ArrayList<>();
for (int i = 0; i < Baseline.P_DIST_T.length; i++) {
dist.add(PlatformAlarmDistItemVO.builder()
.type(Baseline.P_DIST_T[i])
.count(Math.max(0, jitterInt(Baseline.P_DIST_C[i], r)))
.build());
}
return PlatformSurveillanceVO.builder()
.vehicles(vehicles)
@@ -415,6 +406,8 @@ public final class BiSnapshotWeeklyRandomDataBuilder {
static final int P_TASKS = 418;
static final int P_RESV = 2_850;
static final int P_NC = 920;
static final int P_WAIT = 680;
static final int P_CALL_STATUS_TOTAL = 680;
static final int P_OPV = 1_180;
static final int P_DONE = 1_420;
static final String[] P_MO_LABELS = {"5月", "6月", "7月"};
@@ -4,6 +4,9 @@ import com.mhd.bi.domain.biSnapshotSync.service.BiReportSnapshotSyncService;
import com.mhd.bi.domain.biComprehensive.service.BiComprehensiveReportService;
import com.mhd.bi.domain.biPlatformSurveillance.service.BiPlatformSurveillanceReportService;
import com.mhd.bi.domain.biPlatformRealData.service.BiPlatformRealDataService;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealBusinessTypeStatPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealPlatformFloorStatPO;
import com.mhd.bi.domain.biPlatformRealData.repository.po.RealPlatformInfoPO;
import com.mhd.bi.domain.biOverallOperation.service.BiOverallOperationReportService;
import com.mhd.bi.domain.biHkMacaoServiceZone.service.BiHkMacaoServiceZoneReportService;
import com.mhd.bi.domain.biTransportOperationZone.service.BiTransportOperationZoneReportService;
@@ -12,12 +15,17 @@ import com.mhd.bi.domain.biWarehouseTransport.service.BiWarehouseTransportReport
import com.mhd.bi.interfaces.facadeApi.biReport.dto.BiApiResult;
import com.mhd.bi.interfaces.facadeApi.biReport.vo.ComprehensiveSituationVO;
import com.mhd.bi.interfaces.facadeApi.biReport.vo.WarehouseTransportSituationVO;
import com.mhd.bi.interfaces.facadeApi.biReport.vo.gps.NanqiGpsLocationVO;
import com.mhd.bi.interfaces.facadeApi.biReport.vo.overall.OverallOperationSituationVO;
import com.mhd.bi.interfaces.facadeApi.biReport.vo.platform.PlatformSurveillanceVO;
import com.mhd.bi.interfaces.facadeApi.biReport.vo.hkMacaoZone.HkMacaoServiceZoneVO;
import com.mhd.bi.interfaces.facadeApi.biReport.vo.transportZone.TransportOperationZoneVO;
import com.mhd.bi.interfaces.facadeApi.biReport.vo.warehouseZone.WarehouseOperationZoneVO;
import com.mhd.common.core.exception.ServiceException;
import com.mhd.common.core.feign.ThirdPartyServiceFeign;
import com.mhd.common.core.web.controller.BaseController;
import com.mhd.common.core.web.domain.AjaxResult;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
@@ -30,6 +38,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* BI 报表接口
*/
@@ -67,6 +77,12 @@ public class BiReportApi extends BaseController {
@Autowired
private BiReportSnapshotSyncService biReportSnapshotSyncService;
@Autowired
private ThirdPartyServiceFeign thirdPartyServiceFeign;
@Autowired
private ObjectMapper objectMapper;
@ApiOperation(value = "手动触发快照同步", notes = "立即写入各 BI 快照表(综合、仓储运输、月台监测、总体运营、仓储运营专区、运输运营专区、港澳服务专区);须带 X-BI-Access-Token")
@PostMapping("/snapshotSync/run")
@@ -96,14 +112,67 @@ public class BiReportApi extends BaseController {
}
/**
* 月台监测指标查询(实时查 YMS 库,真实字段 + 写死字段)
* 月台监测指标查询(实时查 YMS 库,真实字段 + 写死字段),并落库一条到 BI_PLATFORM_SURVEILLANCE_SNAPSHOT
*/
@ApiOperation(value = "月台监测指标查询", notes = "每次调用实时查询 YMS 库组装;部分字段为真实业务数据,部分为写死数据")
@ApiOperation(value = "月台监测指标查询", notes = "每次调用实时查询 YMS 库组装并写入快照表;部分字段为真实业务数据,部分为写死数据")
@GetMapping("/platformSurveillance")
public BiApiResult<PlatformSurveillanceVO> platformSurveillance() {
PlatformSurveillanceVO data = biPlatformRealDataService.buildRealTimePlatformSurveillance();
PlatformSurveillanceVO data = biPlatformRealDataService.buildRealTimePlatformSurveillanceAndPersist();
return BiApiResult.ok(data);
}
/**
* 今日预约-业务类型统计(实时查 YMS 库,不落库)
*/
@ApiOperation(value = "今日预约业务类型统计", notes = "按业务类型分组统计今日预约数量,实时查询 YMS 库,不落库;例:航空打板 3 条")
@GetMapping("/businessTypeStats")
public BiApiResult<List<RealBusinessTypeStatPO>> businessTypeStats() {
List<RealBusinessTypeStatPO> data = biPlatformRealDataService.queryTodayBusinessTypeStats();
return BiApiResult.ok(data);
}
/**
* 月台监控-按楼层统计月台车辆(实时查 YMS 库,不落库)
*/
@ApiOperation(value = "月台车辆统计(按楼层)", notes = "按楼层统计月台数量、今日预约车辆、当前作业车辆,实时查询 YMS 库,不落库")
@GetMapping("/platformFloorStats")
public BiApiResult<List<RealPlatformFloorStatPO>> platformFloorStats() {
List<RealPlatformFloorStatPO> data = biPlatformRealDataService.queryPlatformFloorStats();
return BiApiResult.ok(data);
}
/**
* 月台管理列表(实时查 YMS qms_window_info,不落库)
*/
@ApiOperation(value = "月台管理列表", notes = "实时查询 YMS 库 qms_window_info,返回月台/鹤位窗口列表(机构、月台号、月台名称、楼层、绑定屏、UUID、状态等),不落库")
@GetMapping("/platformInfoList")
public BiApiResult<List<RealPlatformInfoPO>> platformInfoList() {
List<RealPlatformInfoPO> data = biPlatformRealDataService.queryPlatformInfoList();
return BiApiResult.ok(data);
}
/**
* 南岐GPS:根据车牌号查询车辆实时定位
* (内部经 ThirdPartyServiceFeign 转发到 mhd_thrid_party 的 /nanqiGps/queryByPlate,逻辑仍在 mhd_thrid_party
*/
@ApiOperation(value = "根据车牌号查询车辆实时定位(南岐GPS)", notes = "按车牌号查询南岐GPS车辆实时定位,实时转发到三方服务")
@GetMapping("/nanqiGps/queryByPlate")
public BiApiResult<NanqiGpsLocationVO> nanqiQueryByPlate(
@ApiParam(value = "车牌号")
@RequestParam(value = "carPlate") String carPlate) {
if (carPlate == null || carPlate.trim().isEmpty()) {
throw new ServiceException("车牌号不能为空");
}
AjaxResult result = thirdPartyServiceFeign.nanqiQueryByPlate(carPlate);
Object codeObj = result == null ? null : result.get("code");
if (codeObj == null || !"200".equals(String.valueOf(codeObj))) {
String msg = result == null ? "南岐GPS查询失败" : String.valueOf(result.get("msg"));
throw new ServiceException(msg);
}
Object data = result.get("data");
NanqiGpsLocationVO vo = objectMapper.convertValue(data, NanqiGpsLocationVO.class);
return BiApiResult.ok(vo);
}
//园区物流大屏接口----------------------------------------------------------------------
/**
* 总体运营态势
@@ -0,0 +1,248 @@
package com.mhd.bi.interfaces.facadeApi.biReport.vo.gps;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 南岐GPS 车辆定位信息 VO(与 mhd_thrid_party.NanqiGpsLocationVO 同构,供 BI 开放接口返回)
*
* @author mhd
*/
@Data
@ApiModel("南岐GPS 车辆定位信息")
public class NanqiGpsLocationVO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("应答结果:1成功 0失败")
private Integer rspCode;
@ApiModelProperty("应答描述")
private String rspDesc;
@ApiModelProperty("车辆id")
private String carId;
@ApiModelProperty("车牌号")
private String carPlate;
@ApiModelProperty("车辆名")
private String carName;
@ApiModelProperty("车队id")
private String teamId;
@ApiModelProperty("车队名称")
private String teamName;
@ApiModelProperty("终端号")
private String tmnNo;
@ApiModelProperty("消息序列号")
private String tmnKey;
@ApiModelProperty("卡号")
private String simCard;
@ApiModelProperty("定位时间")
private String time;
@ApiModelProperty("经度(WGS84")
private BigDecimal lng;
@ApiModelProperty("纬度(WGS84")
private BigDecimal lat;
@ApiModelProperty("百度经度")
private BigDecimal blng;
@ApiModelProperty("百度纬度")
private BigDecimal blat;
@ApiModelProperty("谷歌/高德经度")
private BigDecimal glng;
@ApiModelProperty("谷歌/高德纬度")
private BigDecimal glat;
@ApiModelProperty("海拔(米)")
private BigDecimal height;
@ApiModelProperty("速度(千米/小时)")
private BigDecimal speed;
@ApiModelProperty("方向 0~360")
private String drct;
@ApiModelProperty("昨日平台总里程(千米)")
private String prevDayTotalMile;
@ApiModelProperty("平台总里程(千米)")
private String mile;
@ApiModelProperty("今日里程(千米)")
private String preMile;
@ApiModelProperty("终端里程(千米)")
private String tmnMile;
@ApiModelProperty("定位信号:0无 弱<=3 中<=6 强>6")
private String satl;
@ApiModelProperty("通讯信号:0无 弱<=10 中<=20 强>20")
private String sgn;
@ApiModelProperty("地理描述")
private String addr;
@ApiModelProperty("车辆定位状态")
private String state;
@ApiModelProperty("车辆状态文字")
private String stateCn;
@ApiModelProperty("驾驶员")
private String drvName;
@ApiModelProperty("驾驶员电话")
private String drvPhone;
@ApiModelProperty("车牌颜色")
private String plateColor;
@ApiModelProperty("车辆类型")
private String carType;
@ApiModelProperty("地图图标")
private String icon;
@ApiModelProperty("地图图标完整链接")
private String iconLink;
@ApiModelProperty("是否上传自定义图片:true取上传图片 false取大图")
private String uploadFlag;
@ApiModelProperty("大图片")
private String bigIcon;
@ApiModelProperty("大图片完整链接")
private String bigIconLink;
@ApiModelProperty("实时照片")
private String photoIcon;
@ApiModelProperty("中文方向")
private String drctCn;
@ApiModelProperty("今日行驶时长(秒)")
private String todayRun;
@ApiModelProperty("行停时长")
private String runStopTime;
@ApiModelProperty("行停时长(秒),排序时使用")
private String runStopTimeSecond;
@ApiModelProperty("ACC时长")
private String accTime;
@ApiModelProperty("ACC总时长")
private String totalAccTime;
@ApiModelProperty("离线时长")
private String offLineTime;
@ApiModelProperty("信号线(返回集合,每记录含name/value)")
private String sgnStts;
@ApiModelProperty("提醒信息(返回集合,每记录含name/value)")
private String reminds;
@ApiModelProperty("油量(返回集合,每记录含name/value)")
private String st1;
@ApiModelProperty("载重(返回集合,每记录含name/value)")
private String st2;
@ApiModelProperty("温度(返回集合,每记录含name/value)")
private String st3;
@ApiModelProperty("湿度(返回集合,每记录含name/value)")
private String st4;
@ApiModelProperty("水位(返回集合,每记录含name/value)")
private String st5;
@ApiModelProperty("服务到期状态:1已到期 0未到期")
private String expState;
@ApiModelProperty("服务期限")
private String expTime;
@ApiModelProperty("油量")
private String oil;
@ApiModelProperty("定位附加项(JSON")
private String protocols;
@ApiModelProperty("扩展字段(JSON")
private String exts;
@ApiModelProperty("模拟量1")
private String moni1;
@ApiModelProperty("模拟量2")
private String moni2;
@ApiModelProperty("模拟量3")
private String moni3;
@ApiModelProperty("模拟量4")
private String moni4;
@ApiModelProperty("视频通道(JSON")
private String videos;
@ApiModelProperty("未处理报警类型名称(逗号分隔)")
private String alarms;
@ApiModelProperty("未处理报警类型ID(逗号分隔)")
private String alarmTypeIds;
@ApiModelProperty("终端型号")
private String tmnType;
@ApiModelProperty("终端到期时间")
private String tmnExpirTime;
@ApiModelProperty("视频设备报警id集合")
private String videoAlarmTypes;
@ApiModelProperty("驾驶员登签姓名(卡号)")
private String driverSign;
@ApiModelProperty("驾驶员登签电话号码")
private String driverSignPhone;
@ApiModelProperty("驾驶员登签时间")
private String driverSignTime;
@ApiModelProperty("驾驶员登签时长")
private String drvSignTimeLong;
@ApiModelProperty("车辆基本状态(16进制位掩码)")
private String carStts;
@ApiModelProperty("报警状态(16进制位掩码)")
private String alarmStts;
@ApiModelProperty("协议版本")
private String prtcVer;
@ApiModelProperty("接入端口")
private String tmnPort;
}
@@ -20,6 +20,12 @@ public class VehicleStatusVO {
@ApiModelProperty("当前未签到,单位:辆")
private Integer notCheckedIn;
@ApiModelProperty("当前等待作业(已预约/已签到/已叫号),单位:辆")
private Integer waiting;
@ApiModelProperty("已签到+已叫号+已过号(FLOW_STATUS IN 2/3/4),单位:辆")
private Integer callStatusTotal;
@ApiModelProperty("当前作业中,单位:辆")
private Integer operatingVehicles;
+5 -5
View File
@@ -16,17 +16,17 @@ spring:
# 线上测试环境配置 容器名+端口号
# server-addr: 10.33.0.129:6010
# server-addr: 10.102.192.5:6848
username: nacos
password: manhuoda@2023
#线上正式环境
server-addr: 10.102.192.30:6848
# username: nacos
# password: manhuoda@2023
#线上正式环境
server-addr: 10.102.192.31:6848
username: nacos
password: manhuoda@2023
config:
# server-addr: 127.0.0.1:8848
# 线上测试环境配置 容器名+端口号
# server-addr: 10.33.0.129:6010
server-addr: 10.102.192.30:6848
server-addr: 10.102.192.31:6848
username: nacos
password: manhuoda@2023
#线上正式环境
@@ -4,7 +4,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mhd.bi.domain.biPlatformRealData.repository.mapper.BiPlatformRealDataMapper">
<!-- 作业车辆状态列表(vehicles):
<!-- 作业车辆状态列表(vehicles):仅查询预约日期是今天(TARGET_DATE)的车辆
plateNumber <- qms_main_business.CAR_NO
status <- qms_main_business.FLOW_STATUS
platformId <- qms_main_business.WINDOW_NAME(叫号月台,主业务表直接有)
@@ -27,6 +27,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
FROM NGWL_TEST_YMS.QMS_MAIN_BUSINESS mb
LEFT JOIN NGWL_TEST_YMS.QMS_WINDOW_INFO wi
ON wi.WINDOW_NAME = mb.WINDOW_NAME
WHERE mb.TARGET_DATE IS NOT NULL
AND TRUNC(mb.TARGET_DATE) = TRUNC(SYSDATE)
ORDER BY mb.CREATE_TIME DESC
</select>
@@ -38,31 +40,130 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
FROM NGWL_TEST_YMS.QMS_WINDOW_INFO
</select>
<!-- 今日作业任务:实际进入时间是今日的 -->
<!-- 今日作业任务:按预约时间 TARGET_DATE(今日)统计主业务记录数 -->
<select id="countTodayTasks" resultType="java.lang.Long">
SELECT COUNT(1)
FROM NGWL_TEST_YMS.QMS_MAIN_BUSINESS
WHERE ENTRY_TIME IS NOT NULL
AND TRUNC(ENTRY_TIME) = TRUNC(SYSDATE)
WHERE TARGET_DATE IS NOT NULL
AND TRUNC(TARGET_DATE) = TRUNC(SYSDATE)
</select>
<!-- 车辆状态统计:今日的统计(按创建时间 / 流程状态)
<!-- 今日作业准时率统计:
total 今日有入场时间的预约车辆数(ENTRY_TIME 不为空 且 TARGET_DATE = 今天)
onTimeCount 今日准时车辆数(实际进入时间 < 预约日期 + 预约结束时间 BOOKING_END_TIME,即 ENTRY_TIME < TRUNC(TARGET_DATE) + 预约时段结束时分)
「准时率 = 今日准时车辆数 / 今日有入场时间的车辆数 × 100%(四舍五入取整);分子分母都只统计有入场时间的车辆」
BOOKING_END_TIME 存纯时分(如 18:30),故用 TO_DATE 解析后取其时分间隔加到 TARGET_DATE 上
-->
<select id="queryTodayOnTimeStats" resultType="com.mhd.bi.domain.biPlatformRealData.repository.po.RealOnTimeStatPO">
SELECT
SUM(CASE WHEN ENTRY_TIME IS NOT NULL
AND TARGET_DATE IS NOT NULL
AND TRUNC(TARGET_DATE) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS total,
SUM(CASE WHEN ENTRY_TIME IS NOT NULL
AND TRUNC(TARGET_DATE) = TRUNC(SYSDATE)
AND ENTRY_TIME &lt; TRUNC(TARGET_DATE)
+ (TO_DATE(BOOKING_END_TIME, 'HH24:MI') - TRUNC(TO_DATE(BOOKING_END_TIME, 'HH24:MI')))
THEN 1 ELSE 0 END) AS onTimeCount
FROM NGWL_TEST_YMS.QMS_MAIN_BUSINESS
</select>
<!-- 车辆状态统计:今日的统计(按创建时间 CREATE_TIME / 流程状态)
todayReservedVehicles 改为按预约日期 TARGET_DATE(其它 3 项保持 CREATE_TIME
waiting 等待作业:FLOW_STATUS IN ('1','2','3') 已预约/已签到/已叫号 且 预约日期 = 今天
callStatusTotal 叫号状态合计:已签到 + 已叫号 + 已过号 = FLOW_STATUS IN ('2','3','4') 且 预约日期 = 今天
FLOW_STATUS: 1已预约 2已签到 3已叫号 4已过号 5已进场 6已称重 7已装车 8二次称重 9已结算 10已还卡 11已出场 12已取消 -->
<select id="queryVehicleStatusToday" resultType="com.mhd.bi.domain.biPlatformRealData.repository.po.RealVehicleStatusPO">
SELECT
SUM(CASE WHEN TRUNC(CREATE_TIME) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS todayReservedVehicles,
SUM(CASE WHEN TRUNC(TARGET_DATE) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS todayReservedVehicles,
SUM(CASE WHEN FLOW_STATUS = '1' AND TRUNC(CREATE_TIME) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS notCheckedIn,
SUM(CASE WHEN FLOW_STATUS IN ('1','2','3') AND TRUNC(TARGET_DATE) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS waiting,
SUM(CASE WHEN FLOW_STATUS IN ('2','3','4') AND TRUNC(TARGET_DATE) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS callStatusTotal,
SUM(CASE WHEN FLOW_STATUS IN ('5','6','7','8','9','10') AND TRUNC(CREATE_TIME) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS operatingVehicles,
SUM(CASE WHEN FLOW_STATUS = '11' AND TRUNC(CREATE_TIME) = TRUNC(SYSDATE) THEN 1 ELSE 0 END) AS completedVehicles
FROM NGWL_TEST_YMS.QMS_MAIN_BUSINESS
</select>
<!-- 本月任务量:实际进入时间是本月的 -->
<!-- 本月任务量:按预约日期 TARGET_DATE(Y预约日期)统计本月预约总数量 -->
<select id="countMonthTasks" resultType="java.lang.Long">
SELECT COUNT(1)
FROM NGWL_TEST_YMS.QMS_MAIN_BUSINESS
WHERE ENTRY_TIME IS NOT NULL
AND TO_CHAR(ENTRY_TIME, 'YYYY-MM') = TO_CHAR(SYSDATE, 'YYYY-MM')
WHERE TARGET_DATE IS NOT NULL
AND TO_CHAR(TARGET_DATE, 'YYYY-MM') = TO_CHAR(SYSDATE, 'YYYY-MM')
</select>
<!-- 本月任务平均作业时长 / 完成率统计:
avgDurationHours 平均时长(小时)= AVG((EXIT_TIME - ENTRY_TIME) * 24)
仅 ENTRY_TIME 与 EXIT_TIME 都不为空时参与;
durationCount 参与平均时长统计的有效条数;
completedCount 本月(TARGET_DATE)已出场(EXIT_TIME IS NOT NULL)的数量;
totalCount 本月(TARGET_DATE)预约总数量。
-->
<select id="queryMonthTaskStats" resultType="com.mhd.bi.domain.biPlatformRealData.repository.po.RealMonthlyTaskDurationPO">
SELECT
AVG(CASE WHEN ENTRY_TIME IS NOT NULL AND EXIT_TIME IS NOT NULL
THEN (EXIT_TIME - ENTRY_TIME) * 24
END) AS avgDurationHours,
SUM(CASE WHEN ENTRY_TIME IS NOT NULL AND EXIT_TIME IS NOT NULL
AND TO_CHAR(ENTRY_TIME, 'YYYY-MM') = TO_CHAR(SYSDATE, 'YYYY-MM')
THEN 1 ELSE 0 END) AS durationCount,
SUM(CASE WHEN EXIT_TIME IS NOT NULL
AND TARGET_DATE IS NOT NULL
AND TO_CHAR(TARGET_DATE, 'YYYY-MM') = TO_CHAR(SYSDATE, 'YYYY-MM')
THEN 1 ELSE 0 END) AS completedCount,
SUM(CASE WHEN TARGET_DATE IS NOT NULL
AND TO_CHAR(TARGET_DATE, 'YYYY-MM') = TO_CHAR(SYSDATE, 'YYYY-MM')
THEN 1 ELSE 0 END) AS totalCount
FROM NGWL_TEST_YMS.QMS_MAIN_BUSINESS
</select>
<!-- 今日预约-业务类型统计:按 BUSINESS_NAME(业务类型名称)分组统计今日预约数量,按预约日期 TARGET_DATE 过滤 -->
<select id="queryTodayBusinessTypeStats" resultType="com.mhd.bi.domain.biPlatformRealData.repository.po.RealBusinessTypeStatPO">
SELECT
BUSINESS_NAME AS businessName,
COUNT(1) AS count
FROM NGWL_TEST_YMS.QMS_MAIN_BUSINESS
WHERE BUSINESS_NAME IS NOT NULL
AND TRUNC(TARGET_DATE) = TRUNC(SYSDATE)
GROUP BY BUSINESS_NAME
ORDER BY COUNT(1) DESC
</select>
<!-- 月台监控-按楼层统计:楼层从叫号月台 WINDOW_NAME 解析(如 S1-01 / N1-02 -> F1),不依赖字母前缀,只取 - 前面那段的数字
platformCount 来自 QMS_WINDOW_INFOtodayReservedVehicles/operatingVehicles 来自 QMS_MAIN_BUSINESS(按 TARGET_DATE=今天) -->
<select id="queryPlatformFloorStats" resultType="com.mhd.bi.domain.biPlatformRealData.repository.po.RealPlatformFloorStatPO">
SELECT
'F' || SUBSTR(W.WINDOW_NAME, INSTR(W.WINDOW_NAME,'-')-1, 1) AS floorNo,
COUNT(DISTINCT W.WINDOW_NAME) AS platformCount,
COUNT(DISTINCT CASE
WHEN TRUNC(mb.TARGET_DATE) = TRUNC(SYSDATE) THEN mb.CAR_NO
END) AS todayReservedVehicles,
COUNT(DISTINCT CASE
WHEN TRUNC(mb.TARGET_DATE) = TRUNC(SYSDATE)
AND mb.FLOW_STATUS IN ('5','6','7','8','9','10')
THEN mb.CAR_NO
END) AS operatingVehicles
FROM NGWL_TEST_YMS.QMS_WINDOW_INFO W
LEFT JOIN NGWL_TEST_YMS.QMS_MAIN_BUSINESS mb
ON mb.WINDOW_NAME = W.WINDOW_NAME
WHERE W.WINDOW_NAME LIKE '%-%'
GROUP BY SUBSTR(W.WINDOW_NAME, INSTR(W.WINDOW_NAME,'-')-1, 1)
ORDER BY 1 DESC
</select>
<!-- 任务类型统计(来自 NGWL_TEST_YMS.QMS_MAIN_BUSINESS 预约登记表):
按本月预约车辆数(TARGET_DATE 在当前月)按 BUSINESS_NAME 业务类型分组统计 -->
<select id="queryMonthTransportOrderByType" resultType="com.mhd.bi.domain.biPlatformRealData.repository.po.RealTaskTypeStatPO">
SELECT
BUSINESS_NAME AS businessName,
COUNT(1) AS count
FROM NGWL_TEST_YMS.QMS_MAIN_BUSINESS
WHERE BUSINESS_NAME IS NOT NULL
AND TARGET_DATE IS NOT NULL
AND TO_CHAR(TARGET_DATE, 'YYYY-MM') = TO_CHAR(SYSDATE, 'YYYY-MM')
GROUP BY BUSINESS_NAME
ORDER BY COUNT(1) DESC
</select>
<!-- 任务负荷(今日 24 小时时段):按 EXTRACT(HOUR FROM ENTRY_TIME) 统计 -->
@@ -76,4 +177,34 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
GROUP BY EXTRACT(HOUR FROM ENTRY_TIME)
ORDER BY EXTRACT(HOUR FROM ENTRY_TIME)
</select>
<!-- 月台管理列表:查询 YMS qms_window_info 月台/鹤位窗口
机构名称 deptName <- sys_dept.DEPT_NAMEd.dept_id = a.ORG_NO 关联)
鹤位屏名称 berrNames <- qms_window_berr(WINDOW_ID/BERR_ID) join qms_berr_config(ID/BERR_NAME)WM_CONCAT 拼接
其余列来自 qms_window_info 本身 -->
<select id="queryPlatformInfoList" resultType="com.mhd.bi.domain.biPlatformRealData.repository.po.RealPlatformInfoPO">
SELECT
a.ID AS id,
a.ORG_NO AS orgNo,
d.DEPT_NAME AS deptName,
a.WINDOW_NUMBER AS windowNumber,
a.WINDOW_NAME AS windowName,
a.PRIORITY_SET AS prioritySet,
a.RUNNING_STATUS AS runningStatus,
a.CREATE_BY AS createBy,
TO_CHAR(a.CREATE_TIME, 'YYYY-MM-DD HH24:MI:SS') AS createTime,
a.UUID AS uuid,
a.GUIDE_IMAGES AS guideImages,
a.PLATFORM_FLOOR AS platformFloor,
a.JOB_STATUS AS jobStatus,
(
SELECT WM_CONCAT(qbc.BERR_NAME)
FROM NGWL_TEST_YMS.QMS_WINDOW_BERR qwb
LEFT JOIN NGWL_TEST_YMS.QMS_BERR_CONFIG qbc ON qbc.ID = qwb.BERR_ID
WHERE qwb.WINDOW_ID = a.ID
) AS berrNames
FROM NGWL_TEST_YMS.QMS_WINDOW_INFO a
LEFT JOIN NGWL_TEST_YMS.SYS_DEPT d ON d.DEPT_ID = a.ORG_NO
ORDER BY a.PLATFORM_FLOOR, a.WINDOW_NUMBER
</select>
</mapper>
@@ -18,6 +18,15 @@
values (#{payloadJson}, SYSDATE, SYSDATE, 1, #{remark})
</insert>
<update id="updateLatestPlatformSurveillanceSnapshot">
update NGWL_TEST_BI.BI_PLATFORM_SURVEILLANCE_SNAPSHOT
set PAYLOAD_JSON = #{payloadJson},
UPDATE_TIME = SYSDATE,
REMARK = #{remark}
where DEL_FLAG = 1
and ID = (select max(t.ID) from NGWL_TEST_BI.BI_PLATFORM_SURVEILLANCE_SNAPSHOT t where t.DEL_FLAG = 1)
</update>
<insert id="insertOverallOperationSnapshot">
insert into NGWL_TEST_BI.BI_OVERALL_OPERATION_SNAPSHOT (PAYLOAD_JSON, CREATE_TIME, UPDATE_TIME, DEL_FLAG, REMARK)
values (#{payloadJson}, SYSDATE, SYSDATE, 1, #{remark})
@@ -66,6 +66,9 @@ public class ServiceNameConstants
//YMS系统
public static final String YMS_SERVICE = "mhd-yms-service";
//YMS系统
public static final String XXL_JOB_SERVICE = "xxl-job-service";
}
@@ -88,5 +88,10 @@ public class SettlementCustomersPO extends BaseVOEntity{
private String settlementObject;
}
@ApiModelProperty(name = "散租推送计费单据/流水任务id")
private Integer pushBillingRecordJobId;
@ApiModelProperty(name = "整租推送计费单据/流水任务id")
private Integer pushContractManageToBmsJobId;
@ApiModelProperty(name = "生成应收账单任务id")
private Integer pushBillManageJobId;
}
@@ -0,0 +1,58 @@
package com.mhd.common.core.domain.po;
import com.mhd.common.core.annotation.Excel;
import com.mhd.common.core.web.domain.BaseVOEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "结算对象参数对象", description = "结算对象参数响应对象")
public class SettlementCustomersParametersPO extends BaseVOEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("id")
private Long id;
@ApiModelProperty("结算对象id")
private Long settlementCustomersId;
@ApiModelProperty("一级组织表ID")
@Excel(name = "一级组织表ID")
private Long topOrganizationId;
@ApiModelProperty("组织表ID")
@Excel(name = "组织表ID")
private Long organizationId;
@ApiModelProperty("组织名称")
@Excel(name = "组织名称")
private String organizationName;
@ApiModelProperty("结算对象编码")
@Excel(name = "结算对象编码")
private String settlementCustomersCode;
@ApiModelProperty("结算主体id")
@Excel(name = "结算主体id")
private Long settlementEntityId;
@ApiModelProperty("结算主体")
@Excel(name = "结算主体")
private String settlementEntity;
@ApiModelProperty("合同编号")
@Excel(name = "合同编号")
private String contractNumber;
@ApiModelProperty("合同管理id")
private Long contractManageId;
@ApiModelProperty("参数内容")
@Excel(name = "参数内容")
private String content;
@ApiModelProperty("参数名称")
@Excel(name = "参数名称")
private String parametersName;
}
@@ -16,7 +16,8 @@ public enum RoleEnum {
STORE_ADMIN("店铺管理员", "store_admin"),
OWNER_CARGO("货主", "ownerCargo"),
SUPPLIER("供应商", "supplier"),
CUSTOMER("客户", "customer");
CUSTOMER("客户", "customer"),
TENANT("租户", "tenant");
private String name;
private String code;
@@ -26,7 +26,8 @@ import java.io.InputStream;
import java.util.List;
import java.util.Map;
@FeignClient(value = "mhd-third-party-service", configuration = FeignAutoConfiguration.class)
// 目标地址由 nacos 配置项 feign.url.mhd-third-party-service 指定,未配置时按服务名走服务发现
@FeignClient(value = "mhd-third-party-service",url = "http://10.102.192.31:8012", configuration = FeignAutoConfiguration.class)
public interface ThirdPartyServiceFeign {
//查询当前组织所有三方接口信息
@@ -334,4 +335,12 @@ public interface ThirdPartyServiceFeign {
@PostMapping("/mdmMasterData/queryMerchant")
R<com.mhd.common.core.domain.thirdparty.MdmMerchantQueryResultDTO> queryMdmMerchant(
@RequestBody com.mhd.common.core.domain.thirdparty.MdmMerchantQueryDTO queryDTO);
/**
* 南岐GPS:根据车牌号查询车辆实时定位
* @param carPlate 车牌号
* @return AjaxResultdata 为车辆定位信息(code=200 成功)
*/
@GetMapping("/nanqiGps/queryByPlate")
AjaxResult nanqiQueryByPlate(@RequestParam("carPlate") String carPlate);
}
@@ -0,0 +1,112 @@
package com.mhd.common.core.jackson;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import java.io.IOException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 宽松日期反序列化:兼容 yyyy-MM-dd(纯日期)等常见格式。
*
* 背景:库存查询等接口日期只保留年月日后,前端可能把 "2026-07-06" 这类纯日期回传,
* 而实体/DTO 上 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") 是严格格式,反序列化直接报
* InvalidFormatException。本反序列化器解析顺序:
* 1) 字段 @JsonFormat 指定的模式(严格全匹配,保持原行为);
* 2) 兜底常见格式:yyyy-MM-dd HH:mm:ss → yyyy-MM-dd HH:mm → yyyy-MM-dd
* 3) 纯数字按毫秒时间戳;
* 4) 交给 Jackson 默认解析(ISO-8601 等)。
* 仅放宽接受范围,不影响任何既有格式的解析结果;由各服务 DateCompatibilityConfig 显式注册生效。
*
* @author rcx
* @date 2026-09-05
*/
public class FlexibleDateDeserializer extends JsonDeserializer<Date> implements ContextualDeserializer {
/** 兜底格式按长度降序排列,避免长文本被短格式部分解析 */
private static final String[] FALLBACK_PATTERNS = {
"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM-dd"
};
/** 字段 @JsonFormat 指定的模式,可为空 */
private final String pattern;
public FlexibleDateDeserializer() {
this(null);
}
public FlexibleDateDeserializer(String pattern) {
this.pattern = pattern;
}
@Override
public Date deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException {
String value = parser.getText();
if (value == null || value.trim().isEmpty()) {
return null;
}
value = value.trim();
// 纯数字按毫秒时间戳(10位秒级时间戳也兼容)
if (value.matches("\\d{10,}")) {
return new Date(Long.parseLong(value));
}
// 1) 字段 @JsonFormat 指定的模式优先
Date parsed = tryParse(value, pattern);
// 2) 兜底常见格式
if (parsed == null) {
for (String fallback : FALLBACK_PATTERNS) {
parsed = tryParse(value, fallback);
if (parsed != null) {
break;
}
}
}
// 3) 交给 Jackson 默认解析(ISO-8601 等)
if (parsed == null) {
try {
parsed = ctxt.parseDate(value);
} catch (IllegalArgumentException ignore) {
// 走不到默认解析时统一在下方抛出友好异常
}
}
if (parsed == null) {
throw ctxt.weirdStringException(value, Date.class,
"支持格式:yyyy-MM-dd HH:mm:ss / yyyy-MM-dd HH:mm / yyyy-MM-dd");
}
return parsed;
}
/**
* 严格全匹配解析:解析失败或文本未完整消费(如按 yyyy-MM-dd 解析 "2026-07-06 10:00:00"
* 都视为不匹配返回 null,交由下一个格式尝试
*/
private Date tryParse(String value, String pattern) {
if (pattern == null || pattern.isEmpty()) {
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
sdf.setLenient(false);
ParsePosition pos = new ParsePosition(0);
Date date = sdf.parse(value, pos);
return (date != null && pos.getIndex() == value.length()) ? date : null;
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
if (property != null) {
JsonFormat jsonFormat = property.getAnnotation(JsonFormat.class);
if (jsonFormat != null && jsonFormat.pattern() != null && !jsonFormat.pattern().isEmpty()) {
return new FlexibleDateDeserializer(jsonFormat.pattern());
}
}
return this;
}
}
@@ -88,7 +88,8 @@ public class GlobalExceptionHandler
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestURI, e);
return AjaxResult.error(e.getMessage());
String msg = StringUtils.isEmpty(e.getMessage()) ? "系统繁忙,请稍后重试" : e.getMessage();
return AjaxResult.error(msg);
}
/**
@@ -99,7 +100,8 @@ public class GlobalExceptionHandler
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生系统异常.", requestURI, e);
return AjaxResult.error(e.getMessage());
String msg = StringUtils.isEmpty(e.getMessage()) ? "系统繁忙,请稍后重试" : e.getMessage();
return AjaxResult.error(msg);
}
/**
@@ -34,6 +34,8 @@ public class CaptchaConfig
properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
// 验证码文本字符长度 默认为5
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
// 验证码文本字符源 默认为abcde2345678gfynmnpqr 改为纯数字
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_STRING, "0123456789");
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
+6 -13
View File
@@ -14,23 +14,16 @@ spring:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 线上测试环境配置 容器名+端口号
# server-addr: 10.102.192.31:6848
# server-addr: 10.102.192.30:6848
# server-addr: 10.33.0.129:6010
#线上正式环境
# server-addr: 127.0.0.1:8848
# server-addr: 10.102.192.31:6848
# username: nacos
# password: manhuoda@2023
username: nacos
password: manhuoda@2023
config:
server-addr: 127.0.0.1:8848
# 线上测试环境配置 容器名+端口号
# server-addr: 10.33.0.129:6010
#线上正式环境
# server-addr: 127.0.0.1:8848
# server-addr: 10.102.192.31:6848
# username: nacos
# password: manhuoda@2023
# server-addr: 10.102.192.30:6848
username: nacos
password: manhuoda@2023
group: DEFAULT_GROUP # 默认分组就是DEFAULT_GROUP,如果使用默认分组可以不配置
file-extension: yml #默认properties
# 共享配置
+10
View File
@@ -111,6 +111,16 @@
<version>2.24</version>
</dependency>
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.17.2</version>
</dependency>
<!-- JPush推送 -->
<!-- jpush极光推送导入start -->
<!-- <dependency>-->
@@ -278,7 +278,7 @@ public class LeaseApplicationService {
* 货主租赁明细
* 定时任务:每天凌晨1点执行,计算前一天的货主租赁明细
*/
@Scheduled(cron = "0 0 1 * * ?")
// @Scheduled(cron = "0 0 1 * * ?")
public void shipperLeaseDetails() {
log.info("开始执行货主租赁明细定时任务");
// 计算前一天的日期,将时间设置为当天的0点0分0秒,只保留日期部分
@@ -461,7 +461,8 @@ public class MessageManageApplicationService
//获取当前登录人
LoginUser loginUser = SecurityUtils.getLoginUser();
if(null == loginUser){
throw new ServiceException("获取登录人失败");
//未登录(游客):返回空消息中心结构,保证小程序界面正常展示而不报错
return buildEmptyAppNoticeList();
}
noticeMessageLoggingDo.setUserId(loginUser.getUserid());
//获取每种类型的最新一条
@@ -522,6 +523,27 @@ public class MessageManageApplicationService
return result;
}
/**
* 未登录(游客)时返回空的消息中心结构,避免前端页面因无登录上下文而报错
* @return NoticeMessageLoggingAppListPo
*/
private NoticeMessageLoggingAppListPo buildEmptyAppNoticeList() {
NoticeMessageLoggingAppListPo result = new NoticeMessageLoggingAppListPo();
NoticeMessageLoggingAppListPo.CommonNotice serviceNotice = new NoticeMessageLoggingAppListPo.CommonNotice();
serviceNotice.setUnreadNum("0");
NoticeMessageLoggingAppListPo.CommonNotice activityNotice = new NoticeMessageLoggingAppListPo.CommonNotice();
activityNotice.setUnreadNum("0");
NoticeMessageLoggingAppListPo.CommonNotice logisticsNotice = new NoticeMessageLoggingAppListPo.CommonNotice();
logisticsNotice.setUnreadNum("0");
NoticeMessageLoggingAppListPo.CommonNotice systemNotice = new NoticeMessageLoggingAppListPo.CommonNotice();
systemNotice.setUnreadNum("0");
result.setServiceNotice(serviceNotice);
result.setActivityNotice(activityNotice);
result.setLogisticsNotice(logisticsNotice);
result.setSystemNotice(systemNotice);
return result;
}
/**
* APP根据类型获取消息通知
* @param noticeMessageLoggingDo
@@ -4,6 +4,7 @@ import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -11,16 +12,26 @@ import com.mhd.basic.domain.contractManage.entity.ContractManage;
import com.mhd.basic.domain.contractManage.repository.mapper.ContractManageMapper;
import com.mhd.basic.domain.contractManage.repository.po.ContractManagePO;
import com.mhd.basic.domain.contractManage.repository.todo.ContractManageDO;
import com.mhd.basic.domain.contractManage.repository.util.HttpWebServiceUtil;
import com.mhd.basic.domain.contractManage.repository.util.XxlJobOpenApiUtil;
import com.mhd.basic.domain.contractManage.service.ContractManageDomainService;
import com.mhd.basic.domain.contractManageDetail.entity.ContractManageDetail;
import com.mhd.basic.domain.contractManageDetail.repository.mapper.ContractManageDetailMapper;
import com.mhd.basic.domain.contractManageDetail.repository.po.ContractManageDetailPO;
import com.mhd.basic.domain.contractManageDetail.repository.todo.ContractManageDetailDO;
import com.mhd.basic.domain.contractManageDetail.service.ContractManageDetailDomainService;
import com.mhd.basic.domain.contractManageParameters.entity.ContractManageParameters;
import com.mhd.basic.domain.contractManageParameters.repository.facade.IContractManageParametersService;
import com.mhd.basic.domain.contractManageParameters.repository.mapper.ContractManageParametersMapper;
import com.mhd.basic.domain.contractManageParameters.repository.po.ContractManageParametersPO;
import com.mhd.basic.domain.contractManageParameters.repository.todo.ContractManageParametersDO;
import com.mhd.basic.domain.contractManageParameters.service.ContractManageParametersDomainService;
import com.mhd.basic.domain.expenseAccount.repository.po.ExpenseAccountPO;
import com.mhd.basic.domain.expenseAccount.service.ExpenseAccountDomainService;
import com.mhd.basic.interfaces.dto.contractManage.*;
import com.mhd.basic.interfaces.assember.contractManageParameters.ContractManageParametersAssembler;
import com.mhd.basic.interfaces.dto.contractManageDetail.ContractManageDetailDTO;
import com.mhd.basic.interfaces.dto.contractManageParameters.ContractManageParametersDTO;
import com.mhd.common.core.domain.po.*;
import com.mhd.common.core.enums.DictCode;
import com.mhd.common.core.exception.ServiceException;
@@ -32,18 +43,23 @@ import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.BmsServiceFeign;
import com.mhd.system.api.SystemServiceFeign;
import com.mhd.system.api.domain.BusinessDocumentFeign;
import com.mhd.system.api.domain.MaterialInventoryPO;
import com.mhd.system.api.domain.SysDictData;
import com.mhd.system.api.model.LoginUser;
import com.mhd.system.service.ISysDictTypeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestParam;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -75,6 +91,20 @@ public class ContractManageApplicationService {
@Autowired
private SystemServiceFeign systemServiceFeign;
@Autowired
private ContractManageParametersDomainService contractManageParametersDomainService;
@Autowired
private ContractManageParametersAssembler contractManageParametersAssembler;
@Autowired
private ContractManageParametersMapper contractManageParametersMapper;
@Autowired
private IContractManageParametersService contractManageParametersService;
@Resource
private XxlJobOpenApiUtil apiUtil;
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
private static final String token = "Bearer eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjozNTYzLCJ0b3BPcmdhbml6YXRpb25JZCI6MjgyNywidXNlcl9rZXkiOiI0N2MzY2U4My0wMmFjLTQ0NTQtOTdiYS1lYmQwMDc2MzNiNDMiLCJ1c2VybmFtZSI6ImNhbmdtYSJ9.tnZyywwburfBo3Kizdhfk5eYdXbrrZ9976Z0W7GrS6UqSpKwfI2RwdRwfYx7DbD7Ri1Fdj_eOVxBkjvdFF226Q";
/**
* 分页查询合同管理列表
@@ -126,7 +156,21 @@ public class ContractManageApplicationService {
contractManageDO.setTopOrganizationId(2827L);
}
}
return contractManageDomainService.queryList(contractManageDO);
List<ContractManagePO> list = contractManageDomainService.queryList(contractManageDO);
if (!list.isEmpty()) {
List<Long> contractManageIds = list.stream()
.map(ContractManagePO::getContractManageId)
.collect(Collectors.toList());
List<ContractManageParametersPO> allParams = contractManageParametersDomainService.queryListByManageIds(contractManageIds);
Map<Long, List<ContractManageParametersPO>> paramsMap = allParams.stream()
.collect(Collectors.groupingBy(ContractManageParametersPO::getContractManageId));
for (ContractManagePO po : list) {
po.setContractManageParametersList(paramsMap.getOrDefault(po.getContractManageId(), new ArrayList<>()));
}
}
return list;
}
// public BigDecimal getAmount(List<SubjectAndPriceReturn> subjectAndPriceReturnList) {
@@ -610,6 +654,7 @@ public class ContractManageApplicationService {
*/
//20260702取消同一时间段不能有相同合同校验
//checkContract(contractManageDO);
checkUniqueActiveContract(contractManageDO);
handleData(contractManageDO);
handleDict(contractManageDO);
contractManageDO.setContractNumber(OrderSequence.getOrderCode("HT"));
@@ -618,9 +663,42 @@ public class ContractManageApplicationService {
contractManageDO.setOrganizationName(loginUser.getUserPo().getOrganizationName());
boolean flag = contractManageDomainService.insert(contractManageDO);
handleDetils(contractManageDO, true);
handleParameters(contractManageDO);
return flag;
}
/**
* 新增合同管理
*/
@Transactional
public ContractManageDO insertFeign(ContractManageDO contractManageDO) {
//获取当前登录人信息
LoginUser loginUser = SecurityUtils.getLoginUser();
if (ObjectUtil.isNull(loginUser)) {
throw new DigitalLogisticsException(UserError.TIMEOUT);
}
validateContractType(contractManageDO.getContractType());
/*
通用合同同一时间段同一合同类型只能存在一个
特殊合同同一结算主体同一时间段同一合同类型只能存在一个
*/
//20260702取消同一时间段不能有相同合同校验
//checkContract(contractManageDO);
checkUniqueActiveContract(contractManageDO);
handleData(contractManageDO);
handleDict(contractManageDO);
contractManageDO.setContractNumber(OrderSequence.getOrderCode("HT"));
contractManageDO.setTopOrganizationId(loginUser.getUserPo().getTopOrganizationId());
contractManageDO.setOrganizationId(loginUser.getUserPo().getOrganizationId());
contractManageDO.setOrganizationName(loginUser.getUserPo().getOrganizationName());
boolean flag = contractManageDomainService.insert(contractManageDO);
if (!flag) {
throw new ServiceException("新增合同管理失败,请检查数据");
}
handleDetils(contractManageDO, true);
return contractManageDO;
}
/**
* 处理数据字典
*/
@@ -748,6 +826,26 @@ public class ContractManageApplicationService {
}
}
private void checkUniqueActiveContract(ContractManageDO contractManageDO) {
if (contractManageDO.getContractState() != null && contractManageDO.getContractState() != 2) {
return;
}
if (contractManageDO.getSettlementCustomersId() == null
|| StringUtils.isEmpty(contractManageDO.getWarehouseTempLayerType())
|| StringUtils.isEmpty(contractManageDO.getContractRentType())) {
return;
}
long count = contractManageDomainService.countByUniqueKey(
contractManageDO.getSettlementCustomersId(),
contractManageDO.getWarehouseTempLayerType(),
contractManageDO.getContractRentType(),
contractManageDO.getContractManageId()
);
if (count > 0) {
throw new ServiceException("同一结算客户、同一温层、同一租赁类型只能存在一个使用中的合同");
}
}
/**
* 处理合同详情
*/
@@ -846,6 +944,111 @@ public class ContractManageApplicationService {
}
}
private void handleParameters(ContractManageDO contractManageDO) {
List<ContractManageParametersPO> existingList = contractManageParametersDomainService.queryListByManageId(contractManageDO.getContractManageId());
if (!existingList.isEmpty()) {
Long[] ids = existingList.stream().map(ContractManageParametersPO::getId).toArray(Long[]::new);
contractManageParametersDomainService.delete(ids);
}
if (StringUtils.isNotEmpty(contractManageDO.getParametersJson())) {
ContractManageParametersDTO dto = new ContractManageParametersDTO();
dto.setContractManageId(contractManageDO.getContractManageId());
dto.setContractNumber(contractManageDO.getContractNumber());
dto.setContractName(contractManageDO.getContractName());
dto.setParametersJson(contractManageDO.getParametersJson());
dto.setOrganizationId(contractManageDO.getOrganizationId());
dto.setOrganizationName(contractManageDO.getOrganizationName());
dto.setTopOrganizationId(contractManageDO.getTopOrganizationId());
ContractManageParametersDO parametersDO = contractManageParametersAssembler.toDO(dto);
contractManageParametersDomainService.insert(parametersDO);
if(StringUtils.isNotEmpty(contractManageDO.getSettlementCustomersCode())){
AjaxResult ajaxResult = bmsServiceFeign.getCustomersInfoByCode(contractManageDO.getSettlementCustomersCode());
if (!"200".equals(String.valueOf(ajaxResult.get("code")))){
throw new ServiceException("获取结算客户信息失败");
}
SettlementCustomersPO settlementCustomersPO = JSON.parseObject(JSONObject.toJSONString(ajaxResult.get("data")), SettlementCustomersPO.class);
if(null == settlementCustomersPO){
throw new ServiceException("结算客户信息未找到");
}
Long settlementEntityId = settlementCustomersPO.getSettlementEntityId();
if(null != settlementEntityId){
Integer pushContractManageToBmsJobId = settlementCustomersPO.getPushContractManageToBmsJobId();
if (null != pushContractManageToBmsJobId) {
stopXxlJob(pushContractManageToBmsJobId);
}
Integer jobId = addXxlJob(settlementEntityId.toString());
settlementCustomersPO.setPushContractManageToBmsJobId(jobId);
AjaxResult updateResult = bmsServiceFeign.updatePushContractManageJobId(settlementCustomersPO.getSettlementCustomersId(), jobId);
if (!"200".equals(String.valueOf(updateResult.get("code")))) {
throw new ServiceException("更新结算对象定时任务ID失败");
}
}
}
}
}
/**
* 创建夜间23点定时任务
* @param shipperId 结算对象id作为任务参数
* @return xxlJobId 保存到业务表后续用于删除/暂停
*/
public Integer addXxlJob(String shipperId) {
XxlJobOpenApiUtil.JobInfoAddReq req = new XxlJobOpenApiUtil.JobInfoAddReq();
req.setJobGroup(5);
//生成唯一业务流水号拼接至JobDesc保证本次新建任务的JobDesc唯一据此查询jobId不会命中历史任务
String xxlJobBizNo = generateXxlJobBizNo();
req.setJobDesc("整租推送计费单据/流水任务,结算对象id:" + shipperId + ",业务流水号:" + xxlJobBizNo);
req.setAuthor("system");
req.setAlarmEmail("");
req.setScheduleType("CRON");
// 每天晚上23点执行
req.setScheduleConf("0 0 1 * * ?");
req.setGlueType("BEAN");
req.setExecutorHandler("pushContractManageToBms");
req.setExecutorParam(shipperId);
req.setGlueRemark("");
req.setGlueSource("");
req.setExecutorRouteStrategy("FIRST");
req.setChildJobId(null);
req.setMisfireStrategy("DO_NOTHING");
req.setExecutorBlockStrategy("SERIAL_EXECUTION");
req.setExecutorTimeout(0);
req.setExecutorFailRetryCount(0);
boolean addOk = apiUtil.addJob(req);
if (!addOk) {
throw new RuntimeException("创建夜间定时任务失败");
}
Integer xxlJobId = apiUtil.queryJobIdByDescAndParam(req.getJobDesc(), shipperId,"5");
if (xxlJobId == null) {
throw new RuntimeException("定时任务创建成功,但查询jobId失败");
}
return xxlJobId;
}
public void stopXxlJob(Integer shipperId) {
//1.实例化工具类建议交给Spring管理
XxlJobOpenApiUtil apiUtil = new XxlJobOpenApiUtil(adminAddresses,token);
//3.调用创建任务接口 返回任务ID
apiUtil.pauseJob(shipperId);
}
private static final DateTimeFormatter JOB_BIZ_NO_FORMATTER = DateTimeFormatter.ofPattern("yyMMddHHmmssSSS");
private static final Random RANDOM = new Random();
/**
* 生成xxl-job任务业务流水号JOB + 当天yyMMddHHmmssSSS + 6位随机数字保证每次创建的JobDesc唯一
* 示例JOB260905102030123123456
* @return 业务流水号字符串
*/
private static String generateXxlJobBizNo() {
String dateTimeStr = LocalDateTime.now().format(JOB_BIZ_NO_FORMATTER);
int randomNum = RANDOM.nextInt(1_000_000);
String randomStr = String.format("%06d", randomNum);
return "JOB" + dateTimeStr + randomStr;
}
/**
* 修改合同管理
*/
@@ -853,8 +1056,10 @@ public class ContractManageApplicationService {
public Boolean update(ContractManageDO contractManageDO) {
validateContractType(contractManageDO.getContractType());
//checkContract(contractManageDO);
checkUniqueActiveContract(contractManageDO);
handleData(contractManageDO);
handleDetils(contractManageDO, false);
handleParameters(contractManageDO);
handleDict(contractManageDO);
return contractManageDomainService.update(contractManageDO);
}
@@ -880,6 +1085,8 @@ public class ContractManageApplicationService {
ContractManagePO result = contractManageDomainService.getInfo(contractManageId);
List<ContractManageDetailPO> contractManageDetailPOList = contractManageDetailDomainService.queryListByManageId(result.getContractManageId());
result.setContractManageDetailPOList(contractManageDetailPOList);
List<ContractManageParametersPO> paramsList = contractManageParametersDomainService.queryListByManageId(result.getContractManageId());
result.setContractManageParametersList(paramsList);
return result;
}
@@ -1171,4 +1378,296 @@ public class ContractManageApplicationService {
}
return exportList;
}
/*
* 获取结算对象
* */
public SettlementCustomersPO getSettlementCustomers(Long shipperId) {
try {
Long topOrganizationId = null;
AjaxResult ajaxResult = bmsServiceFeign.getCustomersInfoBySettlementEntityId(shipperId, topOrganizationId);
if (ajaxResult == null || !"200".equals(String.valueOf(ajaxResult.get("code"))) || ajaxResult.get("data") == null) {
log.warn("按 settlementEntityId 查询结算对象未成功:shipperId={}, code={}", shipperId, ajaxResult != null ? ajaxResult.get("code") : null);
return null;
}
return JSON.parseObject(JSON.toJSONString(ajaxResult.get("data")), SettlementCustomersPO.class);
} catch (Exception e) {
log.warn("按 settlementEntityId 查询结算对象异常,shipperId={}", shipperId, e);
return null;
}
}
/*
* 根据结算对象id获取结算对象参数列表
* */
public List<SettlementCustomersParametersPO> getSettlementCustomersParameters(Long settlementCustomersId) {
try {
AjaxResult ajaxResult = bmsServiceFeign.getListBySettlementCustomersId(settlementCustomersId);
if (ajaxResult == null || !"200".equals(String.valueOf(ajaxResult.get("code"))) || ajaxResult.get("data") == null) {
log.warn("按 settlementCustomersId 查询结算对象参数未成功:settlementCustomersId={}, code={}", settlementCustomersId, ajaxResult != null ? ajaxResult.get("code") : null);
return null;
}
return JSON.parseArray(JSON.toJSONString(ajaxResult.get("data")), SettlementCustomersParametersPO.class);
} catch (Exception e) {
log.warn("按 settlementCustomersId 查询结算对象参数异常,settlementCustomersId={}", settlementCustomersId, e);
return null;
}
}
public void pushContractManageToBms(String shipperId) {
SettlementCustomersPO settlementCustomers = getSettlementCustomers(Long.valueOf(shipperId));
if (settlementCustomers == null) {
log.warn("pushContractManageToBms 未找到结算对象,shipperId={}", shipperId);
return;
}
Long settlementCustomersId = settlementCustomers.getSettlementCustomersId();
List<SettlementCustomersParametersPO> parametersList = getSettlementCustomersParameters(settlementCustomersId);
if (parametersList == null || parametersList.isEmpty()) {
log.warn("pushContractManageToBms 未找到结算对象参数,settlementCustomersId={}", settlementCustomersId);
return;
}
for (SettlementCustomersParametersPO param : parametersList) {
String parametersName = param.getParametersName();
String content = param.getContent();
if (StringUtils.isEmpty(content)) {
log.warn("结算对象参数 content 为空,parametersName={}, settlementCustomersId={}", parametersName, settlementCustomersId);
continue;
}
Map<String, Object> contentMap = JSON.parseObject(content, new TypeReference<Map<String, Object>>() {});
Integer sfzdcz = Integer.valueOf(contentMap.get("sfzdcz").toString());
if (sfzdcz != 1) {
log.info("结算对象参数 sfzdcz 不为1,跳过推送,parametersName={}, sfzdcz={}", parametersName, sfzdcz);
continue;
}
String warehouseTempLayerType;
String serviceItemsName;
Boolean isGc = false;
if ("冻仓计费配置".equals(parametersName)) {
warehouseTempLayerType = "DC";
serviceItemsName = "冻仓仓租费用";
} else if ("干仓计费配置".equals(parametersName)) {
warehouseTempLayerType = "GC";
serviceItemsName = "干仓仓租费用";
isGc=true;
} else {
log.warn("未知的结算对象参数类型,parametersName={}", parametersName);
continue;
}
List<ContractManage> contractManages = contractManageMapper.selectList(new LambdaQueryWrapper<ContractManage>()
.eq(ContractManage::getContractRentType, "NORMAL")
.eq(ContractManage::getContractState, 2)
.eq(ContractManage::getDelFlag, 1)
.eq(ContractManage::getSettlementCustomersId, settlementCustomersId)
.eq(ContractManage::getWarehouseTempLayerType, warehouseTempLayerType));
if (contractManages == null || contractManages.isEmpty()) {
log.warn("未找到正式合同,warehouseTempLayerType={}, settlementCustomersId={}", warehouseTempLayerType, settlementCustomersId);
continue;
}
for (ContractManage contractManage : contractManages) {
Long contractManageId = contractManage.getContractManageId();
ContractManageParameters contractManageParameters = contractManageParametersService.getOne(new LambdaQueryWrapper<ContractManageParameters>()
.eq(ContractManageParameters::getContractManageId, contractManageId), false);
if (contractManageParameters == null) {
continue;
}
String parametersJson = contractManageParameters.getParametersJson();
Map<String, Object> parametersMap = JSON.parseObject(parametersJson, new TypeReference<Map<String, Object>>() {});
BigDecimal hyj = (BigDecimal) parametersMap.get("hyj");
buildBusinessDocument(hyj, settlementCustomers, contractManage, "0D01", "租金(固定租)", warehouseTempLayerType, serviceItemsName);
}
}
}
private void buildBusinessDocument(BigDecimal fee, SettlementCustomersPO settlementCustomers, ContractManage contractManage, String code, String name, String warehouseType, String serviceItemsName) {
Long settlementCustomersId = settlementCustomers.getSettlementCustomersId();
Long settlementEntityId = settlementCustomers.getSettlementEntityId();
String settlementEntity = settlementCustomers.getSettlementEntity();
String settlementCustomersCode = settlementCustomers.getSettlementCustomersCode();
BusinessDocumentFeign businessDocumentFeign = new BusinessDocumentFeign();
businessDocumentFeign.setBillAmount(fee);
businessDocumentFeign.setOrganizationId(contractManage.getOrganizationId());
businessDocumentFeign.setOrganizationName(contractManage.getOrganizationName());
businessDocumentFeign.setTopOrganizationId(contractManage.getTopOrganizationId());
businessDocumentFeign.setBelongModuleCode("wms");
businessDocumentFeign.setBelongModule("WMS");
businessDocumentFeign.setDataSources(1);
businessDocumentFeign.setSettlementEntity(settlementEntity);
businessDocumentFeign.setSettlementCustomersId(settlementCustomersId);
businessDocumentFeign.setSettlementCustomersCode(settlementCustomersCode);
businessDocumentFeign.setDocumentTypeId(4l);
businessDocumentFeign.setDocumentTypeCode("CM_ZL");
businessDocumentFeign.setDocumentType("仓库租赁单");
businessDocumentFeign.setFirstSubjectCode(code);
businessDocumentFeign.setFirstSubjectName(name);
businessDocumentFeign.setBillingTime(new Date());
businessDocumentFeign.setServiceItemsCode("GC_CZ");
businessDocumentFeign.setServiceItemsName(serviceItemsName);
businessDocumentFeign.setSettlementWay(1);
businessDocumentFeign.setOriginalBusinessNum(contractManage.getContractNumber());
businessDocumentFeign.setWarehouseType(warehouseType);
String contractName = contractManage.getContractName();
String contractNumber = contractManage.getContractNumber();
Long contractManageId = contractManage.getContractManageId();
businessDocumentFeign.setContractName(contractName);
businessDocumentFeign.setContractNumber(contractNumber);
businessDocumentFeign.setContractManageId(contractManageId);
businessDocumentFeign.setBusinessFlow(OrderSequence.getOrderCode("LS"));
businessDocumentFeign.setIsPush(1);
AjaxResult ajaxResult = bmsServiceFeign.feignSaveBusinessDocument(businessDocumentFeign);
if (ajaxResult == null || !"200".equals(String.valueOf(ajaxResult.get("code")))) {
log.error("保存业务单据失败: {}", ajaxResult != null ? ajaxResult.get("msg") : "feign调用异常");
}
}
private static final String WEBSERVICE_URL = "http://10.33.0.116:8080/sys/webservice/ngLegalByFindWebserviceService";
public List<com.alibaba.fastjson.JSONObject> getContractNosByCompanyName(String companyName, int pageNum, int pageSize) {
List<com.alibaba.fastjson.JSONObject> contractList = new ArrayList<>();
try {
log.info("=== 开始查询合同列表 ===");
log.info("查询公司: {}, 页码: {}, 每页数量: {}", companyName, pageNum, pageSize);
String httpRequest = buildXmlRequest(companyName, pageNum, pageSize);
log.info("XML请求: {}", httpRequest);
log.info("XML请求生成完成,长度: {} 字符", httpRequest.length());
log.info("正在调用WebService...");
String httpResponse = HttpWebServiceUtil.callWebService(httpRequest, WEBSERVICE_URL);
String jsonData = extractJsonFromResponse(httpResponse);
log.info("正在解析JSON..."+jsonData);
if (jsonData == null || jsonData.trim().isEmpty()) {
log.warn("未提取到JSON数据,返回空列表");
return contractList;
}
log.info("提取到JSON数据,长度: {} 字符", jsonData.length());
List<com.alibaba.fastjson.JSONObject> result = parseContractListFromJson(jsonData);
contractList.addAll(result);
log.info("=== 查询完成 ===");
log.info("找到 {} 个合同", contractList.size());
} catch (Exception e) {
log.error("查询合同列表失败", e);
throw new RuntimeException("查询合同列表失败: " + e.getMessage(), e);
}
return contractList;
}
/**
* 构建XML请求 - 动态分页
*/
private String buildXmlRequest(String companyName, int pageNum, int pageSize) {
String jsonParam = "{\"fdName\":\"" + companyName + "\"}";
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"" +
" xmlns:web=\"http://webservice.legal.ng.kmss.landray.com/\">" +
"<soapenv:Body>" +
"<web:findNgLegal>" +
"<arg0 xmlns=\"\">" +
"<fdPageNo>" + pageNum + "</fdPageNo>" +
"<fdRowSize>" + pageSize + "</fdRowSize>" +
"<fdRelativePerson><![CDATA[" + jsonParam + "]]></fdRelativePerson>" +
"<fdContractNo></fdContractNo>" +
"<fdPersonNo>NA0301</fdPersonNo>" +
"<fdSponsor></fdSponsor>" +
"<docCreateTimeStar></docCreateTimeStar>" +
"<docCreateTimeEnd></docCreateTimeEnd>" +
"</arg0>" +
"</web:findNgLegal>" +
"</soapenv:Body>" +
"</soapenv:Envelope>";
}
/**
* 从SOAP响应中提取JSON
*/
private String extractJsonFromResponse(String soapResponse) {
if (soapResponse == null || soapResponse.isEmpty()) {
return null;
}
// 直接查找JSON部分
int jsonStart = soapResponse.indexOf("{\"returnState\":");
int jsonEnd = soapResponse.indexOf("}</return>");
if (jsonStart != -1 && jsonEnd != -1) {
// 找到JSON结束位置
jsonEnd = jsonEnd + 1; // 包含右大括号
String json = soapResponse.substring(jsonStart, jsonEnd);
log.info("直接提取JSON成功");
return json;
}
// 备用方法查找<return>标签
int start = soapResponse.indexOf("<return>");
int end = soapResponse.indexOf("</return>");
if (start != -1 && end != -1) {
start += "<return>".length();
String json = soapResponse.substring(start, end);
// 解码XML实体
json = json.replace("&lt;", "<")
.replace("&gt;", ">")
.replace("&amp;", "&")
.replace("&quot;", "\"")
.replace("&apos;", "'");
log.info("从<return>标签提取JSON成功");
return json.trim();
}
log.warn("无法从响应中提取JSON");
return null;
}
/**
* 解析JSON获取完整合同列表
*/
private List<com.alibaba.fastjson.JSONObject> parseContractListFromJson(String jsonData) {
List<com.alibaba.fastjson.JSONObject> contractList = new ArrayList<>();
try {
com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(jsonData);
String returnState = jsonObject.getString("returnState");
if (!"2".equals(returnState)) {
log.warn("返回状态异常: {}", returnState);
return contractList;
}
JSONArray dataArray = jsonObject.getJSONArray("data");
if (dataArray == null || dataArray.isEmpty()) {
log.info("查询成功,但未找到合同数据");
return contractList;
}
log.info("找到 {} 个合同", dataArray.size());
for (int i = 0; i < dataArray.size(); i++) {
com.alibaba.fastjson.JSONObject contract = dataArray.getJSONObject(i);
contractList.add(contract);
}
} catch (Exception e) {
log.error("JSON解析失败", e);
log.error("原始JSON: {}", jsonData);
}
return contractList;
}
}
@@ -0,0 +1,72 @@
package com.mhd.basic.application.service.contractManageParameters;
import com.mhd.basic.domain.contractManageParameters.repository.po.ContractManageParametersPO;
import com.mhd.basic.domain.contractManageParameters.repository.todo.ContractManageParametersDO;
import com.mhd.basic.domain.contractManageParameters.service.ContractManageParametersDomainService;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.model.LoginUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 合约信息规则维护表ApplicationService
*
* @author gen
* @date 2024-06-07
*/
@Service
@Slf4j
public class ContractManageParametersApplicationService {
@Autowired
private ContractManageParametersDomainService contractManageParametersDomainService;
/**
* 分页查询合约信息规则维护表列表
*/
public List<ContractManageParametersPO> queryList(ContractManageParametersDO contractManageParametersDO) {
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser != null){
Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId();
if (topOrganizationId != null && topOrganizationId != 1){
contractManageParametersDO.setTopOrganizationId(loginUser.getUserPo().getTopOrganizationId());
}
}
return contractManageParametersDomainService.queryList(contractManageParametersDO);
}
/**
* 新增合约信息规则维护表
*/
public Boolean insert(ContractManageParametersDO contractManageParametersDO) {
return contractManageParametersDomainService.insert(contractManageParametersDO);
}
/**
* 修改合约信息规则维护表
*/
public Boolean update(ContractManageParametersDO contractManageParametersDO) {
return contractManageParametersDomainService.update(contractManageParametersDO);
}
/**
* 批量删除合约信息规则维护表
*/
public boolean delete(Long[] ids) {
return contractManageParametersDomainService.delete(ids);
}
/**
* 获取合约信息规则维护表详细信息
*/
public ContractManageParametersPO getInfo(Long id)
{
return contractManageParametersDomainService.getInfo(id);
}
}
@@ -13,6 +13,7 @@ import com.mhd.basic.application.service.contractManage.ContractManageApplicatio
import com.mhd.basic.domain.expenseAccount.service.ExpenseAccountDomainService;
import com.mhd.basic.infrastructure.feign.vo.SysDictDataVo;
import com.mhd.basic.interfaces.dto.expenseAccount.ExpenseAccountDTO;
import com.mhd.basic.interfaces.dto.expenseAccount.ExpenseAccountExportVO;
import com.mhd.basic.interfaces.dto.expenseAccount.SearchExpenseAccountDTO;
import com.mhd.common.core.enums.DictCode;
import com.mhd.common.core.exception.ServiceException;
@@ -27,6 +28,7 @@ import com.mhd.system.api.SystemServiceFeign;
import com.mhd.system.api.model.LoginUser;
import com.mhd.common.core.domain.po.UserPo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -378,6 +380,55 @@ public class ExpenseAccountApplicationService {
return expenseAccountDomainService.getInfo(expenseAccountId);
}
/**
* 复制费用科目-获取原科目数据供前端回显
* <p>前端拿到数据后回显到新增弹窗用户填写新科目编码后通过 /edit 接口id 为空走新增提交保存
* 清空科目编码是为了避免直接复制出重复编码费用科目编码无唯一性校验</p>
*/
public ExpenseAccountPO copy(ExpenseAccountDTO expenseAccountDTO) {
if (expenseAccountDTO == null || expenseAccountDTO.getExpenseAccountId() == null) {
throw new ServiceException("原费用科目id不能为空");
}
// 获取原费用科目信息
ExpenseAccountPO original = expenseAccountDomainService.getInfo(expenseAccountDTO.getExpenseAccountId());
if (null == original) {
throw new ServiceException("原费用科目信息未找到");
}
// 清空主键前端提交时走新增
original.setExpenseAccountId(null);
// 清空科目编码强制用户输入新编码避免编码重复
original.setSubjectCode(null);
// 清空审计字段新增时由 ExpenseAccountAssembler 重新生成
original.setCreateBy(null);
original.setCreateByName(null);
original.setCreateTime(null);
original.setUpdateBy(null);
original.setUpdateByName(null);
original.setUpdateTime(null);
original.setDelFlag(null);
// 清空非表字段
original.setUnit(null);
original.setExpenseAccountPOList(null);
return original;
}
/**
* 构建费用科目导出数据不分页全量按列表页查询条件 + 数据权限过滤
*/
public List<ExpenseAccountExportVO> buildExportList(ExpenseAccountDO expenseAccountDO) {
List<ExpenseAccountExportVO> exportList = new ArrayList<>();
List<ExpenseAccountPO> list = this.queryList(expenseAccountDO);
if (list == null || list.isEmpty()) {
return exportList;
}
for (ExpenseAccountPO po : list) {
ExpenseAccountExportVO vo = new ExpenseAccountExportVO();
BeanUtils.copyProperties(po, vo);
exportList.add(vo);
}
return exportList;
}
/**
* 修改状态
* @param expenseAccountDTO
@@ -594,22 +645,28 @@ public class ExpenseAccountApplicationService {
// unit.add(expenseAccountPO.getBillingUnit2() + "-" + expenseAccountPO.getBillingUnitEn2());
// expenseAccountPO.setUnit(unit);
// }
// 组装计费单位列表(数组)
// 组装计费单位列表(数组)中文和英文都为null时过滤掉避免出现"null-null"
List<Map<String, String>> unit = new ArrayList<>();
if (expenseAccountPO != null) {
Map<String, String> unit1 = new HashMap<>();
unit1.put("unit", expenseAccountPO.getBillingUnit() + "-" + expenseAccountPO.getBillingUnitEn());
unit.add(unit1);
Map<String, String> unit2 = new HashMap<>();
unit2.put("unit", expenseAccountPO.getBillingUnit2() + "-" + expenseAccountPO.getBillingUnitEn2());
unit.add(unit2);
addUnitIfPresent(unit, expenseAccountPO.getBillingUnit(), expenseAccountPO.getBillingUnitEn());
addUnitIfPresent(unit, expenseAccountPO.getBillingUnit2(), expenseAccountPO.getBillingUnitEn2());
expenseAccountPO.setUnit(unit);
}
return expenseAccountPO;
}
/**
* 组装计费单位中文和英文都为null时不加入列表
*/
private void addUnitIfPresent(List<Map<String, String>> unit, String billingUnit, String billingUnitEn) {
if (billingUnit == null && billingUnitEn == null) {
return;
}
Map<String, String> unitMap = new HashMap<>();
unitMap.put("unit", billingUnit + "-" + billingUnitEn);
unit.add(unitMap);
}
public List<QueryExpenseAccountPO> selectExpenseAccount(SearchExpenseAccountDO searchExpenseAccountDO){
setDataPermissionForSearch(searchExpenseAccountDO);
return expenseAccountDomainService.selectExpenseAccount(searchExpenseAccountDO);
@@ -6,6 +6,7 @@ import com.alibaba.fastjson.JSON;
import com.mhd.basic.domain.storageLocation.repository.po.StorageLocationPO;
import com.mhd.basic.domain.storageLocation.repository.todo.StorageLocationDO;
import com.mhd.basic.domain.storageLocation.service.StorageLocationDomainService;
import com.mhd.basic.interfaces.dto.storageLocation.StorageLocationDTO;
import com.mhd.basic.domain.storageSection.repository.todo.StorageSectionDO;
import com.mhd.basic.infrastructure.feign.vo.SysDictDataVo;
import com.mhd.common.core.domain.po.UserPo;
@@ -207,6 +208,39 @@ public class StorageLocationApplicationService {
return storageLocationDomainService.getInfo(storageLocationId);
}
/**
* 复制库位-获取原库位数据供前端回显
* 前端拿到数据后跳转到新增库位页面用户修改编码后通过edit接口提交新增
*/
public StorageLocationPO copyStorageLocation(StorageLocationDTO storageLocationDTO) {
if (storageLocationDTO.getStorageLocationId() == null) {
throw new ServiceException("原库位id不能为空");
}
// 获取原库位信息
StorageLocationPO originalStorageLocation = storageLocationDomainService.getInfo(storageLocationDTO.getStorageLocationId());
if (null == originalStorageLocation) {
throw new ServiceException("原库位信息未找到");
}
// 清空唯一性字段库位id不清空会导致提交后走修改分支覆盖原库位
// 库位编码同组织同仓库唯一须由用户重新填写条码为物理唯一标识一并清空
originalStorageLocation.setStorageLocationId(null);
originalStorageLocation.setStorageLocationCode(null);
originalStorageLocation.setLocationBarCode(null);
// 状态重置为初始值新库位无库存()启用未作废
originalStorageLocation.setStatus(1);
originalStorageLocation.setOpeningUp(1);
originalStorageLocation.setNullify(1);
// 清空审计字段提交新增时由Assembler按当前登录人重新生成
originalStorageLocation.setCreateBy(null);
originalStorageLocation.setCreateByName(null);
originalStorageLocation.setCreateTime(null);
originalStorageLocation.setUpdateBy(null);
originalStorageLocation.setUpdateByName(null);
originalStorageLocation.setUpdateTime(null);
// 其余业务字段(仓库/库区/库位组分类/种类/混放策略限制长宽高坐标备注)保留回显
return originalStorageLocation;
}
public int getStorageLocationCount(StorageLocationDO storageLocationDO)
{
return storageLocationDomainService.getStorageLocationCount(storageLocationDO);
@@ -21,6 +21,7 @@ import com.mhd.system.service.ISysDictTypeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
/**
* 库区ApplicationService
@@ -42,11 +43,32 @@ public class StorageSectionApplicationService {
*/
public List<StorageSectionPO> queryList(StorageSectionDO storageSectionDO) {
//设置查询数据权限
applyQueryDataPermission(storageSectionDO);
return storageSectionDomainService.queryList(storageSectionDO);
}
/**
* 库区面积合计 /list 相同的数据权限和筛选条件SQL 直接 SUM(库区面积)
* 保证合计数与列表中各行库区面积之和一致
*/
public BigDecimal sumStorageRegion(StorageSectionDO storageSectionDO) {
// /list 共用同一数据权限保证合计与列表口径一致
applyQueryDataPermission(storageSectionDO);
return storageSectionDomainService.sumStorageRegion(storageSectionDO);
}
/**
* @description 设置查询数据权限queryList sumStorageRegion 共用
* @author ZhouGY
* @param storageSectionDO
*/
private void applyQueryDataPermission(StorageSectionDO storageSectionDO) {
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser != null && loginUser.getUserPo() != null) {
Long userOrganizationId = loginUser.getUserPo().getOrganizationId();
Long frontendOrganizationId = storageSectionDO.getOrganizationId(); // 前端传递的 organizationId
// 数据权限根据 organizationId 来设置查询权限
// 当前登录用户的 organizationId 2827 可查全部组织数据其他组织只能查自己
if (userOrganizationId != null && userOrganizationId == 2827L) {
@@ -91,7 +113,6 @@ public class StorageSectionApplicationService {
}
}
}
return storageSectionDomainService.queryList(storageSectionDO);
}
@@ -126,6 +126,18 @@ public class ContractManage extends BaseVOEntity{
@Excel(name = "其他附件地址")
private String otherAttachmentsUrl;
@ApiModelProperty("合同正本附件原始文件名")
private String contractOriginalName;
@ApiModelProperty("廉政协议附件原始文件名")
private String integrityAgreementName;
@ApiModelProperty("安全协议附件原始文件名")
private String securityProtocolName;
@ApiModelProperty("其他附件原始文件名(多文件时与 otherAttachmentsUrl 同分隔符、同顺序)")
private String otherAttachmentsName;
@ApiModelProperty("我司签订人")
@Excel(name = "我司签订人")
private String ourSinged;
@@ -180,4 +192,30 @@ public class ContractManage extends BaseVOEntity{
@ApiModelProperty(name = "是否包含运输费用(1-包含,2-包含)")
private Integer transportationCosts;
@ApiModelProperty("合同租赁类型")
private String contractRentType;
@ApiModelProperty("合同租赁类型")
private String contractRentTypeName;
@ApiModelProperty("仓库温层类型")
private String warehouseTempLayerType;
@ApiModelProperty("仓库温层类型")
private String warehouseTempLayerTypeName;
@ApiModelProperty("续期提前天数")
private Integer renewalAdvanceDays;
@ApiModelProperty("标的物")
private String subjectMatter;
@ApiModelProperty("性质")
private String nature;
@ApiModelProperty("合同最初起始日")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@Excel(name = "合同最初起始日", width = 30, dateFormat = "yyyy-MM-dd")
private Date initialStartDate;
}
@@ -1,6 +1,7 @@
package com.mhd.basic.domain.contractManage.repository.po;
import com.mhd.basic.domain.contractManageDetail.repository.po.ContractManageDetailPO;
import com.mhd.basic.domain.contractManageParameters.repository.po.ContractManageParametersPO;
import com.mhd.basic.interfaces.dto.contractManageDetail.ContractManageDetailDTO;
import com.mhd.common.core.annotation.Excel;
import lombok.Data;
@@ -124,6 +125,18 @@ public class ContractManagePO extends BaseVOEntity{
@Excel(name = "其他附件地址")
private String otherAttachmentsUrl;
@ApiModelProperty("合同正本附件原始文件名")
private String contractOriginalName;
@ApiModelProperty("廉政协议附件原始文件名")
private String integrityAgreementName;
@ApiModelProperty("安全协议附件原始文件名")
private String securityProtocolName;
@ApiModelProperty("其他附件原始文件名(多文件时与 otherAttachmentsUrl 同分隔符、同顺序)")
private String otherAttachmentsName;
@ApiModelProperty("我司签订人")
@Excel(name = "我司签订人")
private String ourSinged;
@@ -183,5 +196,35 @@ public class ContractManagePO extends BaseVOEntity{
@ApiModelProperty(name = "是否包含运输费用(1-包含,2-包含)")
private Integer transportationCosts;
@ApiModelProperty("客户类型")
private String contractRentType;
@ApiModelProperty("客户类型编码")
private String contractRentTypeName;
@ApiModelProperty("仓库温层类型")
private String warehouseTempLayerType;
@ApiModelProperty("仓库温层类型编码")
private String warehouseTempLayerTypeName;
private String isMany;
@ApiModelProperty("合约信息规则维护列表")
private List<ContractManageParametersPO> contractManageParametersList;
@ApiModelProperty("续期提前天数")
private Integer renewalAdvanceDays;
@ApiModelProperty("标的物")
private String subjectMatter;
@ApiModelProperty("性质")
private String nature;
@ApiModelProperty("合同最初起始日")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@Excel(name = "合同最初起始日", width = 30, dateFormat = "yyyy-MM-dd")
private Date initialStartDate;
}
@@ -121,6 +121,18 @@ public class ContractManageDO extends BaseVOEntity{
@Excel(name = "其他附件地址")
private String otherAttachmentsUrl;
@ApiModelProperty("合同正本附件原始文件名")
private String contractOriginalName;
@ApiModelProperty("廉政协议附件原始文件名")
private String integrityAgreementName;
@ApiModelProperty("安全协议附件原始文件名")
private String securityProtocolName;
@ApiModelProperty("其他附件原始文件名(多文件时与 otherAttachmentsUrl 同分隔符、同顺序)")
private String otherAttachmentsName;
@ApiModelProperty("我司签订人")
@Excel(name = "我司签订人")
private String ourSinged;
@@ -214,6 +226,34 @@ public class ContractManageDO extends BaseVOEntity{
@ApiModelProperty(name = "是否包含运输费用(1-包含,2-包含)")
private Integer transportationCosts;
@ApiModelProperty("客户类型")
private String contractRentType;
@ApiModelProperty("客户类型编码")
private String contractRentTypeName;
@ApiModelProperty("仓库温层类型")
private String warehouseTempLayerType;
@ApiModelProperty("仓库温层类型编码")
private String warehouseTempLayerTypeName;
@ApiModelProperty("一级费用科目code")
private String firstSubjectCode;
@ApiModelProperty("合约信息规则维护JSON")
private String parametersJson;
@ApiModelProperty("续期提前天数")
private Integer renewalAdvanceDays;
@ApiModelProperty("标的物")
private String subjectMatter;
@ApiModelProperty("性质")
private String nature;
@ApiModelProperty("合同最初起始日")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date initialStartDate;
}
@@ -0,0 +1,96 @@
package com.mhd.basic.domain.contractManage.repository.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.zip.GZIPInputStream;
@Slf4j
@Component
public class HttpWebServiceUtil {
/**
* 调用HTTP WebService
*/
public static String callWebService(String requestBody, String endpointUrl) {
try {
// 创建RestTemplate
RestTemplate restTemplate = new RestTemplate();
// 设置请求头
HttpHeaders headers = new HttpHeaders();
// headers.setContentType(MediaType.TEXT_XML);
// headers.setAccept(Collections.singletonList(MediaType.TEXT_XML));
headers.setContentType(MediaType.parseMediaType("text/xml; charset=UTF-8")); // 核心修复
headers.setAccept(Collections.singletonList(MediaType.ALL));
headers.set("SOAPAction", "");
headers.set("User-Agent", "PostmanRuntime/7.26.10");
headers.set("Cache-Control", "no-cache");
headers.set("Accept-Encoding", "gzip, deflate");
headers.set("Connection", "keep-alive");
// 创建请求实体
HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);
log.info("发送请求到: {}", endpointUrl);
// 发送请求
ResponseEntity<byte[]> response = restTemplate.exchange(
endpointUrl,
HttpMethod.POST,
entity,
byte[].class
);
log.info("响应状态: {}", response.getStatusCode());
// 处理响应
byte[] rawResponse = response.getBody();
if (rawResponse == null) {
return "";
}
// 检查是否gzip压缩
String contentEncoding = response.getHeaders().getFirst("Content-Encoding");
String responseBody;
if ("gzip".equalsIgnoreCase(contentEncoding)) {
responseBody = decompressGzip(rawResponse);
log.info("已解压gzip响应,长度: {} 字符", responseBody.length());
} else {
responseBody = new String(rawResponse, StandardCharsets.UTF_8);
log.info("响应长度: {} 字符", responseBody.length());
}
return responseBody;
} catch (Exception e) {
log.error("WebService调用失败", e);
throw new RuntimeException("WebService调用失败: " + e.getMessage(), e);
}
}
/**
* 解压缩GZIP内容
*/
private static String decompressGzip(byte[] compressedData) throws IOException {
try (GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(compressedData));
InputStreamReader reader = new InputStreamReader(gis, StandardCharsets.UTF_8)) {
StringBuilder sb = new StringBuilder();
char[] buffer = new char[1024];
int len;
while ((len = reader.read(buffer)) != -1) {
sb.append(buffer, 0, len);
}
return sb.toString();
}
}
}
@@ -0,0 +1,20 @@
package com.mhd.basic.domain.contractManage.repository.util;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class XxlJobBusinessConfig {
@Value("${xxl.job.admin.address}")
private String adminAddress;
@Value("${xxl.job.admin.cookie}")
private String adminCookie;
@Bean
public XxlJobOpenApiUtil xxlJobOpenApiUtil() {
return new XxlJobOpenApiUtil(adminAddress, adminCookie);
}
}
@@ -0,0 +1,218 @@
package com.mhd.basic.domain.contractManage.repository.util;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.Map;
/**
* 调用XXLJOB后台页面接口
* 列表接口返回JSON不再返回HTML
*/
@Slf4j
public class XxlJobOpenApiUtil {
private final String adminUrl;
private final String cookie;
public XxlJobOpenApiUtil(String adminUrl, String cookie) {
this.adminUrl = StrUtil.removeSuffix(adminUrl, "/");
this.cookie = cookie;
}
/**
* 新增定时任务 POST /jobinfo/add
* @param addReq 请求实体与浏览器payload字段完全对齐
* @return true=新增成功false=失败
*/
public boolean addJob(JobInfoAddReq addReq) {
String url = adminUrl + "/jobinfo/add";
try {
Map<String, Object> formMap = BeanUtil.beanToMap(addReq);
HttpResponse response = HttpRequest.post(url)
.header("Cookie", cookie)
.form(formMap)
.execute();
int status = response.getStatus();
log.info("[XxlJob] addJob status:{}", status);
// 新增成功200重定向到列表页
return status == 200;
} catch (Exception e) {
log.error("[XxlJob] addJob异常", e);
return false;
}
}
/**
* 根据任务描述+执行器参数查询jobId
* 适配返回JSON格式的pageList接口
* @param jobDesc 任务描述
* @param executorParam 任务参数(业务shipperId)
* @return jobId查不到返回null
*/
public Integer queryJobIdByDescAndParam(String jobDesc, String executorParam,String jobGroup) {
StringBuilder querySb = new StringBuilder();
// start length必须
querySb.append("start=0&length=10");
// jobGroup为空就不拼接这个参数不要 jobGroup=""
// 你们业务固定jobGroup=3直接写死3不用传空
if(StrUtil.isNotBlank(jobGroup)){
querySb.append("&jobGroup=").append(jobGroup);
}
// jobDesc
if(StrUtil.isNotBlank(jobDesc)){
querySb.append("&jobDesc=").append(cn.hutool.core.net.URLEncodeUtil.encode(jobDesc));
}
// executorParam
if(StrUtil.isNotBlank(executorParam)){
querySb.append("&executorParam=").append(cn.hutool.core.net.URLEncodeUtil.encode(executorParam));
}
// executorHandler为空直接不携带
// triggerStatus=-1
querySb.append("&triggerStatus=-1");
String url = adminUrl + "/jobinfo/pageList?" + querySb;
log.info("[XxlJob] pageList请求url:{}",url);
try {
HttpResponse response = HttpRequest.get(url)
.header("Authorization", cookie)
.execute();
String jsonStr = response.body();
log.info("[XxlJob] pageList响应:{}",jsonStr);
if (StrUtil.isBlank(jsonStr)) {
log.warn("[XxlJob] pageList返回空响应");
return null;
}
JSONObject jsonObj = JSON.parseObject(jsonStr);
JSONArray jsonArray = jsonObj.getJSONArray("data");
if(jsonArray == null){
return null;
}
List<JobPageItem> dataList = jsonArray.toList(JobPageItem.class);
if (dataList == null || dataList.isEmpty()) {
return null;
}
for (JobPageItem item : dataList) {
if (StrUtil.equals(item.jobDesc, jobDesc) && StrUtil.equals(item.executorParam, executorParam)) {
return item.id;
}
}
return null;
} catch (Exception e) {
log.error("[XxlJob] queryJobId异常", e);
return null;
}
}
/**
* 暂停任务 POST /jobinfo/pause
* @param jobId xxl任务id
* @return true成功
*/
public boolean pauseJob(Integer jobId) {
if (jobId == null) {
return false;
}
String url = adminUrl + "/jobinfo/pause";
try {
HttpResponse response = HttpRequest.post(url)
.header("Cookie", cookie)
.form("id", jobId)
.execute();
return response.getStatus() == 200;
} catch (Exception e) {
log.error("[XxlJob] pauseJob jobId:{}异常", jobId, e);
return false;
}
}
/**
* 开启任务 POST /jobinfo/start
* @param jobId xxl任务id
* @return true成功
*/
public boolean startJob(Integer jobId) {
if (jobId == null) {
return false;
}
String url = adminUrl + "/jobinfo/start";
try {
HttpResponse response = HttpRequest.post(url)
.header("Cookie", cookie)
.form("id", jobId)
.execute();
return response.getStatus() == 200;
} catch (Exception e) {
log.error("[XxlJob] startJob jobId:{}异常", jobId, e);
return false;
}
}
/**
* 删除任务 POST /jobinfo/remove
* @param jobId xxl任务id
* @return true成功
*/
public boolean removeJob(Integer jobId) {
if (jobId == null) {
return false;
}
String url = adminUrl + "/jobinfo/remove";
try {
HttpResponse response = HttpRequest.post(url)
.header("Cookie", cookie)
.form("id", jobId)
.execute();
return response.getStatus() == 200;
} catch (Exception e) {
log.error("[XxlJob] removeJob jobId:{}异常", jobId, e);
return false;
}
}
/**
* pageList 返回data数组中每一项实体
*/
@Data
public static class JobPageItem {
private Integer id;
private String jobDesc;
private String executorParam;
}
/**
* add接口请求实体与浏览器F12抓包payload字段一一对应
*/
@Data
public static class JobInfoAddReq {
private Integer jobGroup;
private String jobDesc;
private String author;
private String alarmEmail;
private String scheduleType;
private String scheduleConf;
private String glueType;
private String executorHandler;
private String executorParam;
private String glueRemark;
private String glueSource;
private String executorRouteStrategy;
private Integer childJobId;
private String misfireStrategy;
private String executorBlockStrategy;
private Integer executorTimeout;
private Integer executorFailRetryCount;
}
}
@@ -23,10 +23,7 @@ import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -193,6 +190,22 @@ public class ContractManageDomainService {
}
/**
* 同结算客户+同温层+同租赁类型+使用中 的合同数量用于唯一性校验
*/
public long countByUniqueKey(Long settlementCustomersId, String warehouseTempLayerType, String contractRentType, Long excludeId) {
LambdaQueryWrapper<ContractManage> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ContractManage::getSettlementCustomersId, settlementCustomersId);
wrapper.eq(ContractManage::getWarehouseTempLayerType, warehouseTempLayerType);
wrapper.eq(ContractManage::getContractRentType, contractRentType);
wrapper.eq(ContractManage::getContractState, 2);
wrapper.eq(ContractManage::getDelFlag, 1);
if (excludeId != null) {
wrapper.ne(ContractManage::getContractManageId, excludeId);
}
return contractManageService.count(wrapper);
}
public ContractManagePO getGeneralContract() {
ContractManagePO contractManagePO = new ContractManagePO();
LambdaQueryWrapper<ContractManage> queryWrapper = new LambdaQueryWrapper<>();
@@ -228,4 +241,6 @@ public class ContractManageDomainService {
}
return contractManagePO;
}
}
@@ -0,0 +1,52 @@
package com.mhd.basic.domain.contractManageParameters.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.mhd.common.core.annotation.Excel;
import io.swagger.annotations.ApiModelProperty;
import com.mhd.common.core.web.domain.BaseVOEntity;
import lombok.Data;
/**
* 合约信息规则维护表
*
* @author gen
* @date 2024-06-07
*/
@Data
public class ContractManageParameters extends BaseVOEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("合同管理id")
@TableId(type = IdType.AUTO)
private Long id;
@ApiModelProperty("合同管理id")
private Long contractManageId;
@ApiModelProperty("一级组织表ID")
@Excel(name = "一级组织表ID")
private Long topOrganizationId;
@ApiModelProperty("组织表ID")
@Excel(name = "组织表ID")
private Long organizationId;
@ApiModelProperty("组织名称")
@Excel(name = "组织名称")
private String organizationName;
@ApiModelProperty("合同编号")
@Excel(name = "合同编号")
private String contractNumber;
@ApiModelProperty("合同名称")
@Excel(name = "合同名称")
private String contractName;
@ApiModelProperty("合约信息规则维护")
@Excel(name = "合约信息规则维护表")
private String parametersJson;
}
@@ -0,0 +1,47 @@
package com.mhd.basic.domain.contractManageParameters.repository.facade;
import com.baomidou.mybatisplus.extension.service.IService;
import com.mhd.basic.domain.contractManageParameters.entity.ContractManageParameters;
import com.mhd.basic.domain.contractManageParameters.repository.po.ContractManageParametersPO;
import com.mhd.basic.domain.contractManageParameters.repository.todo.ContractManageParametersDO;
import java.util.List;
/**
* 合约信息规则维护表Service接口
*
* @author gen
* @date 2024-06-07
*/
public interface IContractManageParametersService extends IService<ContractManageParameters>
{
/**
* 分页查询合约信息规则维护表列表
*/
public List<ContractManageParametersPO> queryList(ContractManageParametersDO contractManageParametersDO);
/**
* 新增合约信息规则维护表
*/
public Boolean insert(ContractManageParametersDO contractManageParametersDO);
/**
* 修改合约信息规则维护表
*/
public Boolean update(ContractManageParametersDO contractManageParametersDO);
/**
* 批量删除合约信息规则维护表
*/
public Boolean delete(Long[] ids);
/**
* 查询合约信息规则维护表
*/
public ContractManageParametersPO getInfo(Long id);
/**
* 根据合同管理id查询合约信息规则维护列表
*/
List<ContractManageParametersPO> getListByContractManageId(Long contractManageId);
}
@@ -0,0 +1,26 @@
package com.mhd.basic.domain.contractManageParameters.repository.mapper;
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mhd.basic.domain.contractManageParameters.entity.ContractManageParameters;
import com.mhd.basic.domain.contractManageParameters.repository.po.ContractManageParametersPO;
import com.mhd.basic.domain.contractManageParameters.repository.todo.ContractManageParametersDO;
import org.apache.ibatis.annotations.Param;
/**
* 合约信息规则维护表Mapper接口
*
* @author gen
* @date 2024-06-07
*/
public interface ContractManageParametersMapper extends BaseMapper<ContractManageParameters>
{
/**
* 查询合约信息规则维护表列表
*/
public List<ContractManageParametersPO> queryList(@Param("contractManageParametersDO") ContractManageParametersDO contractManageParametersDO);
/**
* 根据合同管理id查询合约信息规则维护列表
*/
List<ContractManageParametersPO> getListByContractManageId(@Param("contractManageId") Long contractManageId);
}
@@ -0,0 +1,86 @@
package com.mhd.basic.domain.contractManageParameters.repository.persistence;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mhd.basic.domain.contractManageParameters.entity.ContractManageParameters;
import com.mhd.basic.domain.contractManageParameters.repository.facade.IContractManageParametersService;
import com.mhd.basic.domain.contractManageParameters.repository.mapper.ContractManageParametersMapper;
import com.mhd.basic.domain.contractManageParameters.repository.po.ContractManageParametersPO;
import com.mhd.basic.domain.contractManageParameters.repository.todo.ContractManageParametersDO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* 合约信息规则维护表Service业务层处理
*
* @author gen
* @date 2024-06-07
*/
@Service
public class ContractManageParametersImpl extends ServiceImpl<ContractManageParametersMapper, ContractManageParameters> implements IContractManageParametersService{
@Autowired
private ContractManageParametersMapper contractManageParametersMapper;
/**
* 查询合约信息规则维护表列表
*/
@Override
public List<ContractManageParametersPO> queryList(ContractManageParametersDO contractManageParametersDO)
{
return contractManageParametersMapper.queryList(contractManageParametersDO);
}
/**
* 新增合约信息规则维护表
*/
@Override
public Boolean insert(ContractManageParametersDO contractManageParametersDO) {
ContractManageParameters contractManageParameters = new ContractManageParameters();
BeanUtils.copyProperties(contractManageParametersDO,contractManageParameters);
return this.save(contractManageParameters);
}
/**
* 修改合约信息规则维护表
*/
@Override
public Boolean update(ContractManageParametersDO contractManageParametersDO) {
ContractManageParameters contractManageParameters = new ContractManageParameters();
BeanUtils.copyProperties(contractManageParametersDO,contractManageParameters);
return this.updateById(contractManageParameters);
}
/**
* 批量删除合约信息规则维护表
*/
@Override
public Boolean delete(Long[] ids ) {
List<ContractManageParameters> list = new ArrayList<>();
for (Long id : ids) {
ContractManageParameters contractManageParameters = new ContractManageParameters();
contractManageParameters.setId(id);
contractManageParameters.setDelFlag(2);
list.add(contractManageParameters);
}
return this.updateBatchById(list);
}
/**
* 查询合约信息规则维护表
*/
@Override
public ContractManageParametersPO getInfo(Long id) {
ContractManageParameters contractManageParameters = this.getById(id);
ContractManageParametersPO contractManageParametersPO = new ContractManageParametersPO();
BeanUtils.copyProperties(contractManageParameters,contractManageParametersPO);
return contractManageParametersPO;
}
@Override
public List<ContractManageParametersPO> getListByContractManageId(Long contractManageId) {
return contractManageParametersMapper.getListByContractManageId(contractManageId);
}
}
@@ -0,0 +1,51 @@
package com.mhd.basic.domain.contractManageParameters.repository.po;
import com.mhd.common.core.annotation.Excel;
import lombok.Data;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import com.mhd.common.core.web.domain.BaseVOEntity;
/**
* 合约信息规则维护表对象 contract_manage_parameters
*
* @author gen
* @date 2024-06-07
*/
@Data
@ApiModel(value = "合约信息规则维护表对象", description = "合约信息规则维护表响应对象")
public class ContractManageParametersPO extends BaseVOEntity{
private static final long serialVersionUID = 1L;
@ApiModelProperty("合同管理id")
private Long id;
@ApiModelProperty("合同管理id")
private Long contractManageId;
@ApiModelProperty("一级组织表ID")
@Excel(name = "一级组织表ID")
private Long topOrganizationId;
@ApiModelProperty("组织表ID")
@Excel(name = "组织表ID")
private Long organizationId;
@ApiModelProperty("组织名称")
@Excel(name = "组织名称")
private String organizationName;
@ApiModelProperty("合同编号")
@Excel(name = "合同编号")
private String contractNumber;
@ApiModelProperty("合同名称")
@Excel(name = "合同名称")
private String contractName;
@ApiModelProperty("合约信息规则维护")
@Excel(name = "合约信息规则维护表")
private String parametersJson;
}
@@ -0,0 +1,49 @@
package com.mhd.basic.domain.contractManageParameters.repository.todo;
import com.mhd.common.core.annotation.Excel;
import lombok.Data;
import io.swagger.annotations.ApiModelProperty;
import com.mhd.common.core.web.domain.BaseVOEntity;
/**
* 合约信息规则维护表对象 contract_manage_parameters
*
* @author gen
* @date 2024-06-07
*/
@Data
public class ContractManageParametersDO extends BaseVOEntity{
private static final long serialVersionUID = 1L;
@ApiModelProperty("合同管理id")
private Long id;
@ApiModelProperty("合同管理id")
private Long contractManageId;
@ApiModelProperty("一级组织表ID")
@Excel(name = "一级组织表ID")
private Long topOrganizationId;
@ApiModelProperty("组织表ID")
@Excel(name = "组织表ID")
private Long organizationId;
@ApiModelProperty("组织名称")
@Excel(name = "组织名称")
private String organizationName;
@ApiModelProperty("合同编号")
@Excel(name = "合同编号")
private String contractNumber;
@ApiModelProperty("合同名称")
@Excel(name = "合同名称")
private String contractName;
@ApiModelProperty("合约信息规则维护")
@Excel(name = "合约信息规则维护表")
private String parametersJson;
}
@@ -0,0 +1,105 @@
package com.mhd.basic.domain.contractManageParameters.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.mhd.basic.domain.contractManageParameters.entity.ContractManageParameters;
import com.mhd.basic.domain.contractManageParameters.repository.facade.IContractManageParametersService;
import com.mhd.basic.domain.contractManageParameters.repository.po.ContractManageParametersPO;
import com.mhd.basic.domain.contractManageParameters.repository.todo.ContractManageParametersDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* 合约信息规则维护表
*
* @author gen
* @date 2024-06-07
*/
@Service
@Slf4j
public class ContractManageParametersDomainService {
@Autowired
private IContractManageParametersService contractManageParametersService;
/**
* 分页查询合约信息规则维护表列表
*/
public List<ContractManageParametersPO> queryList(ContractManageParametersDO contractManageParametersDO) {
return contractManageParametersService.queryList(contractManageParametersDO);
}
/**
* 新增合约信息规则维护表
*/
public Boolean insert(ContractManageParametersDO contractManageParametersDO) {
return contractManageParametersService.insert(contractManageParametersDO);
}
/**
* 修改合约信息规则维护表
*/
public Boolean update(ContractManageParametersDO contractManageParametersDO) {
return contractManageParametersService.update(contractManageParametersDO);
}
/**
* 批量删除合约信息规则维护表
*/
public Boolean delete(Long[] ids) {
return contractManageParametersService.delete(ids);
}
/**
* 获取合约信息规则维护表详细信息
*/
public ContractManageParametersPO getInfo(Long id)
{
return contractManageParametersService.getInfo(id);
}
/**
* 根据合同管理id查询合约信息规则维护列表
* @param contractManageId
* @return
*/
public List<ContractManageParametersPO> queryListByManageId(Long contractManageId) {
List<ContractManageParametersPO> resultList = new ArrayList<>();
LambdaQueryWrapper<ContractManageParameters> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ContractManageParameters::getContractManageId,contractManageId);
queryWrapper.eq(ContractManageParameters::getDelFlag,1);
List<ContractManageParameters> contractManageParametersList = contractManageParametersService.list(queryWrapper);
if(contractManageParametersList.size()>0){
for (ContractManageParameters contractManageParameters : contractManageParametersList) {
ContractManageParametersPO contractManageParametersPO = new ContractManageParametersPO();
BeanUtils.copyProperties(contractManageParameters,contractManageParametersPO);
resultList.add(contractManageParametersPO);
}
}
return resultList;
}
public List<ContractManageParametersPO> queryListByManageIds(Collection<Long> contractManageIds) {
List<ContractManageParametersPO> resultList = new ArrayList<>();
if (contractManageIds == null || contractManageIds.isEmpty()) {
return resultList;
}
LambdaQueryWrapper<ContractManageParameters> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(ContractManageParameters::getContractManageId, contractManageIds);
queryWrapper.eq(ContractManageParameters::getDelFlag, 1);
List<ContractManageParameters> entityList = contractManageParametersService.list(queryWrapper);
for (ContractManageParameters entity : entityList) {
ContractManageParametersPO po = new ContractManageParametersPO();
BeanUtils.copyProperties(entity, po);
resultList.add(po);
}
return resultList;
}
}
@@ -413,4 +413,20 @@ public class MaterialBaseInfo extends BaseVOEntity{
@ApiModelProperty(name = "是否正常物料: 0-是 1-否")
private Integer isNormalMaterial;
@ApiModelProperty("每月仓租")
@Excel(name = "每月仓租")
private BigDecimal monthlyWarehouseRent;
@ApiModelProperty("半月仓租")
@Excel(name = "半月仓租")
private BigDecimal halfMonthWarehouseRent;
@ApiModelProperty("每月仓租单位")
@Excel(name = "每月仓租单位")
private String monthlyWarehouseRentUnit;
@ApiModelProperty("半月仓租单位")
@Excel(name = "半月仓租单位")
private String halfMonthWarehouseRentUnit;
}
@@ -399,4 +399,20 @@ public class MaterialBaseInfoPO extends BaseVOEntity{
@ApiModelProperty(name = "是否正常物料: 0-是 1-否")
private Integer isNormalMaterial;
@ApiModelProperty("每月仓租")
@Excel(name = "每月仓租")
private BigDecimal monthlyWarehouseRent;
@ApiModelProperty("半月仓租")
@Excel(name = "半月仓租")
private BigDecimal halfMonthWarehouseRent;
@ApiModelProperty("每月仓租单位")
@Excel(name = "每月仓租单位")
private String monthlyWarehouseRentUnit;
@ApiModelProperty("半月仓租单位")
@Excel(name = "半月仓租单位")
private String halfMonthWarehouseRentUnit;
}
@@ -405,4 +405,20 @@ public class MaterialBaseInfoDO extends BaseVOEntity{
@ApiModelProperty(name = "是否正常物料: 0-是 1-否")
private Integer isNormalMaterial;
@ApiModelProperty("每月仓租")
@Excel(name = "每月仓租")
private BigDecimal monthlyWarehouseRent;
@ApiModelProperty("半月仓租")
@Excel(name = "半月仓租")
private BigDecimal halfMonthWarehouseRent;
@ApiModelProperty("每月仓租单位")
@Excel(name = "每月仓租单位")
private String monthlyWarehouseRentUnit;
@ApiModelProperty("半月仓租单位")
@Excel(name = "半月仓租单位")
private String halfMonthWarehouseRentUnit;
}
@@ -82,4 +82,20 @@ public class MaterialClassify extends BaseVOEntity{
@Excel(name = "单位")
private String unit;
@ApiModelProperty("每月仓租")
@Excel(name = "每月仓租")
private BigDecimal monthlyWarehouseRent;
@ApiModelProperty("半月仓租")
@Excel(name = "半月仓租")
private BigDecimal halfMonthWarehouseRent;
@ApiModelProperty("每月仓租单位")
@Excel(name = "每月仓租单位")
private String monthlyWarehouseRentUnit;
@ApiModelProperty("半月仓租单位")
@Excel(name = "半月仓租单位")
private String halfMonthWarehouseRentUnit;
}
@@ -89,4 +89,20 @@ public class MaterialClassifyPO extends BaseVOEntity{
@Excel(name = "单位")
private String unit;
@ApiModelProperty("每月仓租")
@Excel(name = "每月仓租")
private BigDecimal monthlyWarehouseRent;
@ApiModelProperty("半月仓租")
@Excel(name = "半月仓租")
private BigDecimal halfMonthWarehouseRent;
@ApiModelProperty("每月仓租单位")
@Excel(name = "每月仓租单位")
private String monthlyWarehouseRentUnit;
@ApiModelProperty("半月仓租单位")
@Excel(name = "半月仓租单位")
private String halfMonthWarehouseRentUnit;
}
@@ -89,4 +89,20 @@ public class MaterialClassifyDO extends BaseVOEntity{
@ApiModelProperty("单位")
@Excel(name = "单位")
private String unit;
@ApiModelProperty("每月仓租")
@Excel(name = "每月仓租")
private BigDecimal monthlyWarehouseRent;
@ApiModelProperty("半月仓租")
@Excel(name = "半月仓租")
private BigDecimal halfMonthWarehouseRent;
@ApiModelProperty("每月仓租单位")
@Excel(name = "每月仓租单位")
private String monthlyWarehouseRentUnit;
@ApiModelProperty("半月仓租单位")
@Excel(name = "半月仓租单位")
private String halfMonthWarehouseRentUnit;
}
@@ -5,6 +5,7 @@ import com.mhd.basic.domain.storageSection.entity.StorageSection;
import com.mhd.basic.domain.storageSection.repository.po.StorageSectionPO;
import com.mhd.basic.domain.storageSection.repository.todo.StorageSectionDO;
import java.math.BigDecimal;
import java.util.List;
/**
@@ -20,6 +21,11 @@ public interface IStorageSectionService extends IService<StorageSection>
*/
public List<StorageSectionPO> queryList(StorageSectionDO storageSectionDO);
/**
* 库区面积合计 queryList 相同筛选条件
*/
public BigDecimal sumStorageRegion(StorageSectionDO storageSectionDO);
/**
* 新增库区
*/
@@ -1,5 +1,6 @@
package com.mhd.basic.domain.storageSection.repository.mapper;
import java.math.BigDecimal;
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mhd.basic.domain.storageSection.entity.StorageSection;
@@ -19,4 +20,9 @@ public interface StorageSectionMapper extends BaseMapper<StorageSection>
*/
public List<StorageSectionPO> queryList(StorageSectionDO storageSectionDO);
/**
* 库区面积合计 queryList 相同筛选条件
*/
public BigDecimal sumStorageRegion(StorageSectionDO storageSectionDO);
}
@@ -10,6 +10,7 @@ import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@@ -33,6 +34,15 @@ public class StorageSectionImpl extends ServiceImpl<StorageSectionMapper, Storag
return storageSectionMapper.queryList(storageSectionDO);
}
/**
* 库区面积合计 queryList 相同筛选条件
*/
@Override
public BigDecimal sumStorageRegion(StorageSectionDO storageSectionDO)
{
return storageSectionMapper.sumStorageRegion(storageSectionDO);
}
/**
* 新增库区
*/
@@ -21,6 +21,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@@ -53,6 +54,13 @@ public class StorageSectionDomainService {
return storageSectionService.queryList(storageSectionDO);
}
/**
* 库区面积合计 queryList 相同筛选条件
*/
public BigDecimal sumStorageRegion(StorageSectionDO storageSectionDO) {
return storageSectionService.sumStorageRegion(storageSectionDO);
}
/**
* 新增库区
@@ -0,0 +1,65 @@
package com.mhd.basic.interfaces.assember.contractManageParameters;
import cn.hutool.core.util.ObjectUtil;
import com.mhd.basic.domain.contractManageParameters.repository.todo.ContractManageParametersDO;
import com.mhd.basic.interfaces.dto.contractManageParameters.ContractManageParametersDTO;
import com.mhd.common.core.exception.digitalLogisticsException.DigitalLogisticsException;
import com.mhd.common.core.exception.digitalLogisticsException.UserError;
import com.mhd.common.core.utils.bean.BeanUtils;
import com.mhd.common.core.utils.IgnoreNullUtil;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.model.LoginUser;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* 合约信息规则维护表Assembler
*
* @author gen
* @date 2024-06-07
*/
@Component
public class ContractManageParametersAssembler {
/**
* 转换实体
*/
public ContractManageParametersDO toDO(ContractManageParametersDTO contractManageParametersDTO) {
ContractManageParametersDO contractManageParametersDO = new ContractManageParametersDO();
// 拷贝
BeanUtils.copyProperties(contractManageParametersDTO, contractManageParametersDO, IgnoreNullUtil.getNullPropertyNames(contractManageParametersDTO));
LoginUser loginUser = SecurityUtils.getLoginUser();
if (ObjectUtil.isNull(loginUser)) {
throw new DigitalLogisticsException(UserError.TIMEOUT);
}
String userName = loginUser.getUsername();
// 获取登录人id
Long userId = loginUser.getUserid();
if(contractManageParametersDTO.getId() != null){
if(userId != null){
contractManageParametersDO.setUpdateBy(userId);
}else {
contractManageParametersDO.setUpdateBy(new Long("0"));
}
contractManageParametersDO.setUpdateByName(userName);
contractManageParametersDO.setUpdateTime(new Date());
}else {
if(userId != null){
contractManageParametersDO.setCreateBy(userId);
contractManageParametersDO.setUpdateBy(userId);
}else {
contractManageParametersDO.setCreateBy(new Long("0"));
contractManageParametersDO.setUpdateBy(new Long("0"));
}
contractManageParametersDO.setCreateByName(userName);
contractManageParametersDO.setUpdateByName(userName);
contractManageParametersDO.setCreateTime(new Date());
contractManageParametersDO.setUpdateTime(new Date());
contractManageParametersDO.setDelFlag(1);
}
return contractManageParametersDO;
}
}
@@ -121,6 +121,18 @@ public class ContractManageDTO extends BaseVOEntity{
@Excel(name = "其他附件地址")
private String otherAttachmentsUrl;
@ApiModelProperty("合同正本附件原始文件名")
private String contractOriginalName;
@ApiModelProperty("廉政协议附件原始文件名")
private String integrityAgreementName;
@ApiModelProperty("安全协议附件原始文件名")
private String securityProtocolName;
@ApiModelProperty("其他附件原始文件名(多文件时与 otherAttachmentsUrl 同分隔符、同顺序)")
private String otherAttachmentsName;
@ApiModelProperty("我司签订人")
@Excel(name = "我司签订人")
private String ourSinged;
@@ -204,4 +216,33 @@ public class ContractManageDTO extends BaseVOEntity{
@ApiModelProperty(name = "是否包含运输费用(1-包含,2-包含)")
private Integer transportationCosts;
@ApiModelProperty("合同租赁类型code")
private String contractRentType;
@ApiModelProperty("合同租赁类型名称")
private String contractRentTypeName;
@ApiModelProperty("仓库温层类型code")
private String warehouseTempLayerType;
@ApiModelProperty("仓库温层类型编码名称")
private String warehouseTempLayerTypeName;
@ApiModelProperty("合约信息规则维护JSON")
private String parametersJson;
@ApiModelProperty("续期提前天数")
private Integer renewalAdvanceDays;
@ApiModelProperty("标的物")
private String subjectMatter;
@ApiModelProperty("性质")
private String nature;
@ApiModelProperty("合同最初起始日")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@Excel(name = "合同最初起始日", width = 30, dateFormat = "yyyy-MM-dd")
private Date initialStartDate;
}
@@ -0,0 +1,48 @@
package com.mhd.basic.interfaces.dto.contractManageParameters;
import com.mhd.common.core.annotation.Excel;
import lombok.Data;
import io.swagger.annotations.ApiModelProperty;
import com.mhd.common.core.web.domain.BaseVOEntity;
/**
* 合约信息规则维护表对象 contract_manage_parameters
*
* @author gen
* @date 2024-06-07
*/
@Data
public class ContractManageParametersDTO extends BaseVOEntity{
private static final long serialVersionUID = 1L;
@ApiModelProperty("合同管理id")
private Long id;
@ApiModelProperty("合同管理id")
private Long contractManageId;
@ApiModelProperty("一级组织表ID")
@Excel(name = "一级组织表ID")
private Long topOrganizationId;
@ApiModelProperty("组织表ID")
@Excel(name = "组织表ID")
private Long organizationId;
@ApiModelProperty("组织名称")
@Excel(name = "组织名称")
private String organizationName;
@ApiModelProperty("合同编号")
@Excel(name = "合同编号")
private String contractNumber;
@ApiModelProperty("合同名称")
@Excel(name = "合同名称")
private String contractName;
@ApiModelProperty("合约信息规则维护")
@Excel(name = "合约信息规则维护表")
private String parametersJson;
}
@@ -0,0 +1,69 @@
package com.mhd.basic.interfaces.dto.expenseAccount;
import com.mhd.common.core.annotation.Excel;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 费用科目导出对象 expense_account
*
* @author rcx
* @date 2026-08-28
*/
@Data
public class ExpenseAccountExportVO {
/** 费用类别(关联服务项名称) */
@Excel(name = "费用类别")
private String serviceItemsName;
@Excel(name = "上级科目")
private String upperSubject;
@Excel(name = "费用代码")
private String subjectCode;
@Excel(name = "费用名称")
private String subjectName;
@Excel(name = "税率")
private BigDecimal taxRate;
@Excel(name = "创建订单固定显示", readConverterExp = "0=否,1=是")
private Integer fixedOrderDisplay;
@Excel(name = "入库显示", readConverterExp = "0=否,1=是")
private Integer inOrderDisplay;
@Excel(name = "出库显示", readConverterExp = "0=否,1=是")
private Integer outOrderDisplay;
@Excel(name = "自有车辆固定显示", readConverterExp = "1=是,2=否")
private Integer ownStatus;
@Excel(name = "社会车辆固定显示", readConverterExp = "1=是,2=否")
private Integer societyStatus;
@Excel(name = "备注信息")
private String remark;
@Excel(name = "状态", readConverterExp = "1=启用,2=停用")
private Integer status;
@Excel(name = "创建人")
private String createByName;
@Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@Excel(name = "更新人")
private String updateByName;
@Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
@Excel(name = "所属组织")
private String organizationName;
}
@@ -385,4 +385,19 @@ public class MaterialBaseInfoDTO extends BaseVOEntity{
@ApiModelProperty(name = "是否正常物料: 0-是 1-否")
private Integer isNormalMaterial;
@ApiModelProperty("每月仓租")
@Excel(name = "每月仓租")
private BigDecimal monthlyWarehouseRent;
@ApiModelProperty("半月仓租")
@Excel(name = "半月仓租")
private BigDecimal halfMonthWarehouseRent;
@ApiModelProperty("每月仓租单位")
@Excel(name = "每月仓租单位")
private String monthlyWarehouseRentUnit;
@ApiModelProperty("半月仓租单位")
@Excel(name = "半月仓租单位")
private String halfMonthWarehouseRentUnit;
}
@@ -89,4 +89,20 @@ public class MaterialClassifyDTO extends BaseVOEntity{
@ApiModelProperty("单位")
@Excel(name = "单位")
private String unit;
@ApiModelProperty("每月仓租")
@Excel(name = "每月仓租")
private BigDecimal monthlyWarehouseRent;
@ApiModelProperty("半月仓租")
@Excel(name = "半月仓租")
private BigDecimal halfMonthWarehouseRent;
@ApiModelProperty("每月仓租单位")
@Excel(name = "每月仓租单位")
private String monthlyWarehouseRentUnit;
@ApiModelProperty("半月仓租单位")
@Excel(name = "半月仓租单位")
private String halfMonthWarehouseRentUnit;
}
@@ -1,5 +1,7 @@
package com.mhd.basic.interfaces.facade.contractManage;
import com.alibaba.fastjson.JSONObject;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;
@@ -8,6 +10,10 @@ import com.mhd.basic.interfaces.assember.contractManage.ContractManageAssembler;
import com.mhd.basic.interfaces.dto.contractManage.ContractManageExportVO;
import com.mhd.basic.interfaces.dto.contractManage.SubjectAndPriceDTO;
import com.mhd.basic.interfaces.dto.contractManage.SubjectAndPriceReturn;
import com.mhd.common.core.utils.sms.Result;
import com.mhd.system.api.domain.ContractManageFeignDTO;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -119,6 +125,25 @@ public class ContractManageApi extends BaseController{
}
}
/**
* 保存合同管理
*/
@ApiOperation("保存临时合同管理")
@PostMapping("/feignSave")
public AjaxResult feignSave(@RequestBody ContractManageFeignDTO contractManageFeignDTO)
{
try {
//转换实体
ContractManageDTO contractManageDTO = new ContractManageDTO();
BeanUtils.copyProperties(contractManageFeignDTO, contractManageDTO);
ContractManageDO contractManageDO = contractManageAssembler.toDO(contractManageDTO);
ContractManageDO insert = contractManageApplicationService.insertFeign(contractManageDO);
return AjaxResult.success(insert);
}catch (Exception e){
return AjaxResult.error(e.getMessage());
}
}
/**
* 批量删除合同管理
*/
@@ -230,4 +255,45 @@ public class ContractManageApi extends BaseController{
util.exportExcel(response, list, "合同导出");
}
/**
* 1. 简单任务示例Bean模式
* 这里的 demoJobHandler 必须和调度中心配置一致
*/
@XxlJob("pushContractManageToBms")
public void pushContractManageToBms() {
// 获取任务参数(货主id)
String shipperId = XxlJobHelper.getJobParam();
logger.info("XXL-JOB 开始入库业务单推送bms计费流水,货主id参数:{}", shipperId);
try {
contractManageApplicationService.pushContractManageToBms(shipperId);
// 设置任务执行结果
XxlJobHelper.handleSuccess("入库业务单推送bms计费流水执行成功");
} catch (Exception e) {
logger.error("XXL-JOB 执行失败", e);
XxlJobHelper.handleFail("入库业务单推送bms计费流水执行失败:" + e.getMessage());
}
}
/**
* 根据公司名称获取合同列表(完整合同内容,支持分页)
* @param companyName 公司名称
* @param pageNum 页码
* @param pageSize 每页数量
* @return 合同列表
*/
@GetMapping("/contractNos")
public TableDataInfo getContractNosByCompanyName(
@RequestParam String companyName,
@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize) {
List<JSONObject> list = contractManageApplicationService.getContractNosByCompanyName(companyName, pageNum, pageSize);
TableDataInfo tableDataInfo = new TableDataInfo(list, list.size());
tableDataInfo.setCode(200);
tableDataInfo.setMsg("查询成功");
return tableDataInfo;
}
}
@@ -0,0 +1,105 @@
package com.mhd.basic.interfaces.facade.contractManageParameters;
import java.util.List;
import com.mhd.basic.interfaces.assember.contractManageParameters.ContractManageParametersAssembler;
import com.mhd.system.api.domain.ContractManageParametersFeign;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.mhd.basic.interfaces.dto.contractManageParameters.ContractManageParametersDTO;
import com.mhd.basic.domain.contractManageParameters.repository.todo.ContractManageParametersDO;
import com.mhd.basic.domain.contractManageParameters.repository.po.ContractManageParametersPO;
import com.mhd.basic.application.service.contractManageParameters.ContractManageParametersApplicationService;
import javax.annotation.Resource;
import com.mhd.common.core.web.controller.BaseController;
import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.common.core.web.page.TableDataInfo;
/**
* 合约信息规则维护表Api
*
* @author gen
* @date 2024-06-07
*/
@RestController
@RequestMapping("/contractManageParametersApi")
public class ContractManageParametersApi extends BaseController{
@Autowired
private ContractManageParametersApplicationService contractManageParametersApplicationService;
@Resource
private ContractManageParametersAssembler contractManageParametersAssembler;
/**
* 分页查询合约信息规则维护表列表
*/
@ApiOperation("查询合约信息规则维护表列表")
@GetMapping("/list")
public TableDataInfo list(ContractManageParametersDTO contractManageParametersDTO)
{
//转换实体
ContractManageParametersDO contractManageParametersDO = contractManageParametersAssembler.toDO(contractManageParametersDTO);
startPage();
List<ContractManageParametersPO> list = contractManageParametersApplicationService.queryList(contractManageParametersDO);
return getDataTable(list);
}
/**
* 编辑合约信息规则维护表
*/
@ApiOperation("编辑合约信息规则维护表")
@PostMapping("/edit")
public AjaxResult edit(@RequestBody ContractManageParametersDTO contractManageParametersDTO)
{
//转换实体
ContractManageParametersDO contractManageParametersDO = contractManageParametersAssembler.toDO(contractManageParametersDTO);
if(contractManageParametersDTO.getId() != null){
return toAjax(contractManageParametersApplicationService.update(contractManageParametersDO));
}else {
return toAjax(contractManageParametersApplicationService.insert(contractManageParametersDO));
}
}
/**
* 批量删除合约信息规则维护表
*/
@ApiOperation("批量删除合约信息规则维护表")
@DeleteMapping("/deleteByIds/{ids}")
public AjaxResult delete(@PathVariable Long[] ids)
{
return toAjax(contractManageParametersApplicationService.delete(ids));
}
/**
* 获取合约信息规则维护表详细信息
*/
@ApiOperation("获取合约信息规则维护表")
@GetMapping(value = "/getInfo/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return AjaxResult.success(contractManageParametersApplicationService.getInfo(id));
}
@ApiOperation("Feign保存合约信息规则维护")
@PostMapping("/feignSave")
public AjaxResult feignSave(@RequestBody ContractManageParametersFeign contractManageParametersFeign)
{
try {
ContractManageParametersDTO contractManageParametersDTO = new ContractManageParametersDTO();
BeanUtils.copyProperties(contractManageParametersFeign, contractManageParametersDTO);
ContractManageParametersDO contractManageParametersDO = contractManageParametersAssembler.toDO(contractManageParametersDTO);
return toAjax(contractManageParametersApplicationService.insert(contractManageParametersDO));
} catch (Exception e) {
return AjaxResult.error(e.getMessage());
}
}
}
@@ -1,5 +1,7 @@
package com.mhd.basic.interfaces.facade.expenseAccount;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
@@ -13,9 +15,11 @@ import com.mhd.basic.domain.expenseAccount.repository.todo.SearchExpenseAccountD
import com.mhd.basic.domain.sysMultistageDict.repository.po.SysMultistageDictPo;
import com.mhd.basic.interfaces.assember.expenseAccount.ExpenseAccountAssembler;
import com.mhd.basic.interfaces.dto.expenseAccount.ExpenseAccountDTO;
import com.mhd.basic.interfaces.dto.expenseAccount.ExpenseAccountExportVO;
import com.mhd.basic.interfaces.dto.expenseAccount.SearchExpenseAccountDTO;
import com.mhd.common.core.constant.HttpStatus;
import com.mhd.common.core.domain.entity.R;
import com.mhd.common.core.utils.poi.ExcelUtil;
import com.sun.org.apache.bcel.internal.generic.NEW;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang.StringUtils;
@@ -26,6 +30,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import com.mhd.common.core.web.controller.BaseController;
import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.common.core.web.page.TableDataInfo;
@@ -336,5 +341,45 @@ public class ExpenseAccountApi extends BaseController{
return AjaxResult.success(expenseAccountPO.getUnit());
}
/**
* 复制费用科目
* 返回原科目数据已清空主键科目编码等字段供前端回显到新增弹窗用户填写新科目编码后通过 /edit 接口提交新增
*/
@ApiOperation("复制费用科目-获取原科目数据用于回显")
@PostMapping("/copy")
public AjaxResult copy(@RequestBody ExpenseAccountDTO expenseAccountDTO)
{
try {
ExpenseAccountPO expenseAccountPO = expenseAccountApplicationService.copy(expenseAccountDTO);
return AjaxResult.success(expenseAccountPO);
}catch (Exception e){
return AjaxResult.error(e.getMessage());
}
}
/**
* 导出费用科目
* 按列表页当前查询条件 + 数据权限过滤不分页全量导出
*/
@ApiOperation("导出费用科目")
@RequestMapping(value = "/export", method = {RequestMethod.GET, RequestMethod.POST},
produces = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
public void export(ExpenseAccountDTO expenseAccountDTO, HttpServletResponse response) throws IOException
{
//转换实体不用Assembler避免其自动填充createByName/updateByName干扰查询条件/list一致
ExpenseAccountDO expenseAccountDO = new ExpenseAccountDO();
BeanUtils.copyProperties(expenseAccountDTO, expenseAccountDO);
//查询全量不分页并转换为导出对象
List<ExpenseAccountExportVO> list = expenseAccountApplicationService.buildExportList(expenseAccountDO);
//设置下载头防止浏览器乱码
String fileName = URLEncoder.encode("费用科目导出", "UTF-8").replaceAll("\\+", "%20");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
ExcelUtil<ExpenseAccountExportVO> util = new ExcelUtil<>(ExpenseAccountExportVO.class);
util.exportExcel(response, list, "费用科目");
}
}
@@ -19,6 +19,7 @@ import com.mhd.basic.domain.storageLocation.repository.todo.StorageLocationDO;
import com.mhd.basic.domain.storageLocation.repository.po.StorageLocationPO;
import com.mhd.basic.application.service.storageLocation.StorageLocationApplicationService;
import com.mhd.basic.interfaces.assember.storageLocation.StorageLocationAssembler;
import com.mhd.common.core.exception.ServiceException;
import javax.annotation.Resource;
import com.mhd.common.core.web.controller.BaseController;
import com.mhd.common.core.web.domain.AjaxResult;
@@ -127,6 +128,24 @@ public class StorageLocationApi extends BaseController{
{
return AjaxResult.success(storageLocationApplicationService.getInfo(storageLocationId));
}
/**
* 复制库位-获取原库位数据用于回显
*/
@ApiOperation("复制库位-获取原库位数据用于回显")
@PostMapping("/copyStorageLocation")
public AjaxResult copyStorageLocation(@RequestBody StorageLocationDTO storageLocationDTO)
{
if(storageLocationDTO.getStorageLocationId() == null){
throw new ServiceException("原库位id不能为空");
}
try {
StorageLocationPO result = storageLocationApplicationService.copyStorageLocation(storageLocationDTO);
return AjaxResult.success(result);
}catch (Exception e){
return AjaxResult.error("复制库位失败:"+e.getMessage());
}
}
@ApiOperation("获取库位总数")
@PostMapping(value = "/getStorageLocationCount")
public AjaxResult getStorageLocationCount(@RequestBody StorageLocationDTO storageLocationDTO)
@@ -55,6 +55,18 @@ public class StorageSectionApi extends BaseController{
return getDataTable(list);
}
/**
* 库区面积合计 /list 相同的筛选条件返回库区面积总和
*/
@ApiOperation("查询库区面积合计(与库区列表相同筛选条件,SUM(库区面积))")
@GetMapping("/sumStorageRegion")
public AjaxResult sumStorageRegion(StorageSectionDTO storageSectionDTO)
{
//转换实体
StorageSectionDO storageSectionDO = storageSectionAssembler.toDO(storageSectionDTO);
return AjaxResult.success(storageSectionApplicationService.sumStorageRegion(storageSectionDO));
}
/**
* 分页查询库区列表
*/
@@ -0,0 +1,54 @@
package com.mhd.basic.util;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* XXL-JOB 配置类
*/
@Configuration
public class XxlJobConfig {
private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.appname}")
private String appname;
@Value("${xxl.job.executor.address}")
private String address;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private int port;
@Value("${xxl.job.executor.logpath}")
private String logPath;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appname);
xxlJobSpringExecutor.setAddress(address);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}
}
@@ -40,14 +40,14 @@ public class SysConfigController extends BaseController
/**
* 获取参数配置列表
*/
@RequiresPermissions("system:config:list")
@GetMapping("/list")
public TableDataInfo list(SysConfig config)
{
startPage();
List<SysConfig> list = configService.selectConfigList(config);
return getDataTable(list);
}
// @RequiresPermissions("system:config:list")
// @GetMapping("/list")
// public TableDataInfo list(SysConfig config)
// {
// startPage();
// List<SysConfig> list = configService.selectConfigList(config);
// return getDataTable(list);
// }
@Log(title = "参数管理", businessType = BusinessType.EXPORT)
@RequiresPermissions("system:config:export")
@@ -71,11 +71,11 @@ public class SysConfigController extends BaseController
/**
* 根据参数键名查询参数值
*/
@GetMapping(value = "/configKey/{configKey}")
public AjaxResult getConfigKey(@PathVariable String configKey)
{
return AjaxResult.success(configService.selectConfigByKey(configKey));
}
// @GetMapping(value = "/configKey/{configKey}")
// public AjaxResult getConfigKey(@PathVariable String configKey)
// {
// return AjaxResult.success(configService.selectConfigByKey(configKey));
// }
/**
* 新增参数配置
@@ -132,4 +132,4 @@ public class SysConfigController extends BaseController
configService.resetConfigCache();
return AjaxResult.success();
}
}
}
@@ -85,6 +85,9 @@ public class SysUserController extends BaseController
}
}
List<SysUser> list = userService.selectUserList(user);
for (SysUser sysUser : list) {
sysUser.setPassword("******");
}
return getDataTable(list);
}
@@ -323,4 +326,4 @@ public class SysUserController extends BaseController
userService.insertUserAuth(userId, roleIds);
return success();
}
}
}

Some files were not shown because too many files have changed in this diff Show More