Merge branch 'dev' into dev_sfjk_0827
This commit is contained in:
@@ -18,7 +18,7 @@ import java.util.Set;
|
||||
/**
|
||||
* bms财务结算系统feign调用接口
|
||||
*/
|
||||
@FeignClient(contextId = "BmsService", value = ServiceNameConstants.BMS_SERVICE, url = "http://10.102.192.32:8019",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||
@FeignClient(contextId = "BmsService", value = ServiceNameConstants.BMS_SERVICE, url = "http://10.102.192.3:8012",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||
public interface BmsServiceFeign {
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@FeignClient(contextId = "OmsService", value = ServiceNameConstants.OMS_SERVICE,url = "http://10.102.192.33:8017",fallbackFactory = RemoteOmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||
@FeignClient(contextId = "OmsService", value = ServiceNameConstants.OMS_SERVICE,url = "http://10.102.192.4:8017",fallbackFactory = RemoteOmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||
public interface OmsServiceFeign {
|
||||
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@FeignClient(contextId = "commonWlhyServiceFeign",value = ServiceNameConstants.WLHY_SERVICE,url = "http://10.102.192.31:8014",configuration = FeignAutoConfiguration.class)
|
||||
@FeignClient(contextId = "commonWlhyServiceFeign",value = ServiceNameConstants.WLHY_SERVICE,url = "http://10.102.192.30:8014",configuration = FeignAutoConfiguration.class)
|
||||
public interface WlhyServiceFeign {
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.util.Map;
|
||||
* @description: TODO
|
||||
* @date 2024/5/10 8:58
|
||||
**/
|
||||
@FeignClient(contextId = "remoteWmsServiceFeign",value = ServiceNameConstants.WMS_SERVICE, url = "http://10.102.192.32:8016",fallbackFactory = RemoteWmsFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||
@FeignClient(contextId = "remoteWmsServiceFeign",value = ServiceNameConstants.WMS_SERVICE, url = "http://10.102.192.3:8016",fallbackFactory = RemoteWmsFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||
public interface WmsServiceFeign {
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@ 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.102.192.33:8018",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||
@FeignClient(contextId = "YmsService", value = ServiceNameConstants.YMS_SERVICE,url = "http://10.102.192.4:8018",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||
public interface YmsServiceFeign {
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
+2
@@ -83,6 +83,8 @@ public class MaterialInventoryOmsParamDO extends BaseVOEntity {
|
||||
private Date productionDate;
|
||||
//过期日期
|
||||
private Date expiryDate;
|
||||
//进货日期
|
||||
private Date purchaseDate;
|
||||
|
||||
private String executioOrderNumber;
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@ spring:
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# server-addr: 127.0.0.1:8848
|
||||
server-addr: 127.0.0.1:8848
|
||||
# 线上测试环境配置 容器名+端口号
|
||||
# server-addr: 10.33.0.129:6010
|
||||
server-addr: 10.102.192.31:6848
|
||||
# server-addr: 10.102.192.31:6848
|
||||
username: nacos
|
||||
password: manhuoda@2023
|
||||
#线上正式环境
|
||||
@@ -23,8 +23,8 @@ spring:
|
||||
# username: nacos
|
||||
# password: manhuoda@2023
|
||||
config:
|
||||
# server-addr: 127.0.0.1:8848
|
||||
server-addr: 10.102.192.31:6848
|
||||
server-addr: 127.0.0.1:8848
|
||||
# server-addr: 10.102.192.31:6848
|
||||
# 测试环境配置 容器名+端口号
|
||||
# server-addr: 10.33.0.129:6010
|
||||
# 线上测试环境配置 容器名+端口号
|
||||
|
||||
+4
@@ -6,6 +6,7 @@ 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 java.util.List;
|
||||
|
||||
@@ -39,4 +40,7 @@ public interface BiPlatformRealDataMapper {
|
||||
|
||||
/** 月台监控-按楼层统计月台车辆(楼层、月台数量、今日预约车辆、当前作业车辆) */
|
||||
List<RealPlatformFloorStatPO> queryPlatformFloorStats();
|
||||
|
||||
/** 月台管理列表(YMS qms_window_info,含机构/鹤位/状态/楼层/UUID 等完整信息) */
|
||||
List<RealPlatformInfoPO> queryPlatformInfoList();
|
||||
}
|
||||
+59
@@ -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;
|
||||
}
|
||||
+35
-24
@@ -8,6 +8,7 @@ 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.biPlatformSurveillance.repository.mapper.BiPlatformSurveillanceReportMapper;
|
||||
import com.mhd.bi.domain.biSnapshotSync.repository.mapper.BiSnapshotWriteMapper;
|
||||
import com.mhd.bi.domain.biSnapshotSync.support.BiSnapshotWeeklyRandomDataBuilder;
|
||||
@@ -231,15 +232,30 @@ public class BiPlatformRealDataService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 实时组装月台监测数据:真实字段查 YMS 库,写死字段保留静态值。
|
||||
* 月台管理列表:实时查 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(20260825L);
|
||||
PlatformSurveillanceVO template = BiSnapshotWeeklyRandomDataBuilder.platformSurveillance(r);
|
||||
|
||||
// 作业车辆列表
|
||||
// 作业车辆列表:真实数据源,无条件覆盖(真实为空则返回空列表,不用模板模拟)
|
||||
List<RealPlatformVehiclePO> realVehicles = queryVehicles();
|
||||
List<PlatformVehicleItemVO> vehicleVos = new ArrayList<>();
|
||||
for (RealPlatformVehiclePO rv : realVehicles) {
|
||||
@@ -252,52 +268,47 @@ 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();
|
||||
// 月台实时概览:真实数据源,无条件覆盖
|
||||
if (template.getPlatformOverview() != null) {
|
||||
template.getPlatformOverview().setTotalPlatforms(ov.getTotalPlatforms().intValue());
|
||||
template.getPlatformOverview().setOperatingPlatforms(ov.getOperatingPlatforms().intValue());
|
||||
RealPlatformOverviewPO ov = queryPlatformOverview();
|
||||
int total = ov.getTotalPlatforms().intValue();
|
||||
int occ = ov.getOperatingPlatforms().intValue();
|
||||
double rate = total > 0 ? Math.round(((double) occ / total) * 100.0 * 100.0) / 100.0 : 0d;
|
||||
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((int) countTodayTasks());
|
||||
}
|
||||
|
||||
// 作业车辆状态统计
|
||||
RealVehicleStatusPO vs = queryVehicleStatusToday();
|
||||
// 作业车辆状态统计:真实数据源,无条件覆盖
|
||||
if (template.getVehicleStatus() != null) {
|
||||
RealVehicleStatusPO vs = queryVehicleStatusToday();
|
||||
template.getVehicleStatus().setTodayReservedVehicles(vs.getTodayReservedVehicles().intValue());
|
||||
template.getVehicleStatus().setNotCheckedIn(vs.getNotCheckedIn().intValue());
|
||||
template.getVehicleStatus().setOperatingVehicles(vs.getOperatingVehicles().intValue());
|
||||
template.getVehicleStatus().setCompletedVehicles(vs.getCompletedVehicles().intValue());
|
||||
}
|
||||
|
||||
// 本月任务统计 taskTotal
|
||||
long monthTasks = countMonthTasks();
|
||||
// 本月任务统计:taskTotal 有真实数据源无条件覆盖;YoY 等无真实数据源保留模板写死值
|
||||
if (template.getMonthlyTaskStatistics() != null) {
|
||||
template.getMonthlyTaskStatistics().setTaskTotal((int) monthTasks);
|
||||
template.getMonthlyTaskStatistics().setTaskTotal((int) countMonthTasks());
|
||||
}
|
||||
|
||||
// 任务负荷(今日 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.mhd.bi.domain.biPlatformSurveillance.service.BiPlatformSurveillanceRe
|
||||
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;
|
||||
@@ -128,6 +129,16 @@ public class BiReportApi extends BaseController {
|
||||
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);
|
||||
}
|
||||
//园区物流大屏接口----------------------------------------------------------------------
|
||||
/**
|
||||
* 总体运营态势
|
||||
|
||||
@@ -16,12 +16,12 @@ 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.31: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
|
||||
# 线上测试环境配置 容器名+端口号
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -111,4 +113,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_NAME(d.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>
|
||||
@@ -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;
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@FeignClient(value = "mhd-third-party-service",url = "http://10.102.192.31:8012", configuration = FeignAutoConfiguration.class)
|
||||
@FeignClient(value = "mhd-third-party-service",url = "http://10.102.192.30:8012",configuration = FeignAutoConfiguration.class)
|
||||
public interface ThirdPartyServiceFeign {
|
||||
|
||||
//查询当前组织所有三方接口信息
|
||||
|
||||
@@ -13,15 +13,15 @@ spring:
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# server-addr: 127.0.0.1:8848
|
||||
server-addr: 10.102.192.30:6848
|
||||
server-addr: 127.0.0.1:8848
|
||||
# server-addr: 10.102.192.30:6848
|
||||
# server-addr: 10.33.0.129:6010
|
||||
username: nacos
|
||||
password: manhuoda@2023
|
||||
config:
|
||||
# server-addr: 127.0.0.1:8848
|
||||
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.30:6848
|
||||
username: nacos
|
||||
password: manhuoda@2023
|
||||
group: DEFAULT_GROUP # 默认分组就是DEFAULT_GROUP,如果使用默认分组可以不配置
|
||||
|
||||
+1
-1
@@ -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秒,只保留日期部分
|
||||
|
||||
+23
-1
@@ -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
|
||||
|
||||
+51
@@ -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
|
||||
|
||||
+34
@@ -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);
|
||||
|
||||
+23
-2
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+6
@@ -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);
|
||||
|
||||
/**
|
||||
* 新增库区
|
||||
*/
|
||||
|
||||
+6
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增库区
|
||||
*/
|
||||
|
||||
+8
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 新增库区
|
||||
|
||||
+69
@@ -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;
|
||||
}
|
||||
+45
@@ -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
@@ -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)
|
||||
|
||||
+12
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询库区列表
|
||||
*/
|
||||
|
||||
+8
@@ -102,4 +102,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<include refid="selectStorageSectionPo1"/>
|
||||
order by a.storage_name asc, a.create_time desc
|
||||
</select>
|
||||
|
||||
<!-- 库区面积合计:与 queryList 相同筛选条件,SUM(库区面积),无数据时返回 0 -->
|
||||
<select id="sumStorageRegion" parameterType="com.mhd.basic.domain.storageSection.repository.todo.StorageSectionDO" resultType="java.math.BigDecimal">
|
||||
select IFNULL(SUM(a.storage_region), 0)
|
||||
from storage_section a
|
||||
LEFT JOIN warehouse w ON a.warehouse_id = w.warehouse_id AND w.del_flag = 1
|
||||
<include refid="selectStorageSectionPo1"/>
|
||||
</select>
|
||||
</mapper>
|
||||
+8
-2
@@ -129,8 +129,14 @@ public class CommonApplicationService {
|
||||
* @param userDO
|
||||
*/
|
||||
public void checkAccountInfo(UserDO userDO) {
|
||||
//获取当前登录用户的最高组织
|
||||
Long topOrganizationId = SecurityUtils.getLoginUser().getUserPo().getTopOrganizationId();
|
||||
//获取当前登录用户的最高组织,远程Feign调用无loginUser时从userDO取
|
||||
Long topOrganizationId;
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (ObjectUtil.isNotNull(loginUser)) {
|
||||
topOrganizationId = loginUser.getUserPo().getTopOrganizationId();
|
||||
} else {
|
||||
topOrganizationId = userDO.getTopOrganizationId();
|
||||
}
|
||||
|
||||
checkUserInfo(topOrganizationId, userDO);
|
||||
}
|
||||
|
||||
+130
-18
@@ -735,14 +735,20 @@ public class UserApplicationService {
|
||||
*/
|
||||
public void addUserInfo(UserDO userDO) {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (ObjectUtil.isNull(loginUser)) {
|
||||
throw new DigitalLogisticsException(UserError.USER_NULL);
|
||||
UserPo loginUserPo = null;
|
||||
if (ObjectUtil.isNotNull(loginUser)) {
|
||||
loginUserPo = loginUser.getUserPo();
|
||||
}
|
||||
UserPo loginUserPo = loginUser.getUserPo();
|
||||
CheckDO checkDO = userFactory.getCheckDo(userDO);
|
||||
//用户密码传值
|
||||
if (StringUtils.isNotEmpty(userDO.getUserPassword())) {
|
||||
String userPassword = AESUtil.decrypt(userDO.getUserPassword());
|
||||
String userPassword;
|
||||
if (ObjectUtil.isNotNull(loginUser)) {
|
||||
userPassword = AESUtil.decrypt(userDO.getUserPassword());
|
||||
} else {
|
||||
// Feign调用传入的是明文密码,无需AES解密
|
||||
userPassword = userDO.getUserPassword();
|
||||
}
|
||||
checkDO.setUserPassword(userPassword);
|
||||
}
|
||||
//用户信息格式校验
|
||||
@@ -750,9 +756,15 @@ public class UserApplicationService {
|
||||
String salt = OConvertUtils.randomGen(8);
|
||||
userDO.setUserSalt(salt);
|
||||
regCheck(checkDO);
|
||||
//用户密码传值
|
||||
//用户密码加密入库
|
||||
if (StringUtils.isNotEmpty(userDO.getUserPassword())) {
|
||||
String userPassword = AESUtil.decrypt(userDO.getUserPassword());
|
||||
String userPassword;
|
||||
if (ObjectUtil.isNotNull(loginUser)) {
|
||||
userPassword = AESUtil.decrypt(userDO.getUserPassword());
|
||||
} else {
|
||||
// Feign调用传入的是明文密码,无需AES解密
|
||||
userPassword = userDO.getUserPassword();
|
||||
}
|
||||
//加密算法,PBEWithMD5AndDES,
|
||||
String passwordEncode = PasswordUtil.encrypt(userDO.getUserAccount(), userPassword, salt);
|
||||
userDO.setUserPassword(passwordEncode);
|
||||
@@ -783,11 +795,13 @@ public class UserApplicationService {
|
||||
String idCardDes = DesensitizedUtil.desensitizedIdNumber(idCard);
|
||||
userDO.setUserIdcardNumberDes(idCardDes);
|
||||
}
|
||||
userDO.setCreateBy(loginUserPo.getUserId());
|
||||
userDO.setCreateByName(loginUserPo.getUserName());
|
||||
if (loginUserPo.getUserAccountType() != null && loginUserPo.getUserAccountType() == 5) {
|
||||
userDO.setCreateBy(loginUserPo.getCreateBy());
|
||||
userDO.setCreateByName(loginUserPo.getCreateByName());
|
||||
if (ObjectUtil.isNotNull(loginUserPo)) {
|
||||
userDO.setCreateBy(loginUserPo.getUserId());
|
||||
userDO.setCreateByName(loginUserPo.getUserName());
|
||||
if (loginUserPo.getUserAccountType() != null && loginUserPo.getUserAccountType() == 5) {
|
||||
userDO.setCreateBy(loginUserPo.getCreateBy());
|
||||
userDO.setCreateByName(loginUserPo.getCreateByName());
|
||||
}
|
||||
}
|
||||
userDO.setCreateTime(new Date());
|
||||
//用户状态:0-无状态,1-正常,2-锁定
|
||||
@@ -1379,6 +1393,66 @@ public class UserApplicationService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 远程Feign调用新增用户(无token),仅操作USER表基本字段
|
||||
* 调用方需传入:organizationId、userAccount、userPassword(明文)、userName
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public UserPo addUserByFeign(UserDO userDO) {
|
||||
// 1. 获取一级组织
|
||||
AjaxResult organizationInfo = productServiceFeign.getTopOrganization(userDO.getOrganizationId());
|
||||
String code = organizationInfo.get("code").toString();
|
||||
if (!"200".equals(code)) {
|
||||
throw new DigitalLogisticsException(UserError.USER_ACCOUNT_REG_ERROR);
|
||||
}
|
||||
String data = organizationInfo.get("data").toString();
|
||||
if (ObjectUtil.isNotNull(data)) {
|
||||
userDO.setTopOrganizationId(Long.parseLong(data));
|
||||
}
|
||||
// 2. 获取组织名称
|
||||
AjaxResult orgInfoResult = productServiceFeign.getInfo(userDO.getOrganizationId());
|
||||
if ("200".equals(orgInfoResult.get("code").toString())) {
|
||||
Object orgData = orgInfoResult.get("data");
|
||||
if (ObjectUtil.isNotNull(orgData)) {
|
||||
OrganizationPo organizationPo = BeanUtil.toBean(orgData, OrganizationPo.class);
|
||||
if (ObjectUtil.isNotNull(organizationPo)) {
|
||||
userDO.setOrganizationName(organizationPo.getOrganizationName());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 3. 密码加密(调用方传入明文,无需AES解密)
|
||||
if (StringUtils.isNotEmpty(userDO.getUserPassword())) {
|
||||
String salt = OConvertUtils.randomGen(8);
|
||||
userDO.setUserSalt(salt);
|
||||
String passwordEncode = PasswordUtil.encrypt(userDO.getUserAccount(), userDO.getUserPassword(), salt);
|
||||
userDO.setUserPassword(passwordEncode);
|
||||
}
|
||||
// 4. 根据userId判断新增还是更新
|
||||
if (ObjectUtil.isNotNull(userDO.getUserId())) {
|
||||
// 有userId,更新已有用户
|
||||
UserEntity updateEntity = new UserEntity();
|
||||
BeanUtils.copyProperties(userDO, updateEntity);
|
||||
userMapper.updateById(updateEntity);
|
||||
UserPo userPo = userMapper.selectByUserId(userDO.getUserId());
|
||||
return userPo;
|
||||
} else {
|
||||
// 无userId,新增用户
|
||||
userDO.setCreateTime(new Date());
|
||||
userDO.setUserStatus(1);
|
||||
userDO.setUserAuthStatus(1);
|
||||
userDO.setDelFlag(1);
|
||||
userDO.setRoleCode(RoleEnum.MUST.getCode());
|
||||
userDO.setRoleName(RoleEnum.MUST.getName());
|
||||
if (ObjectUtil.isNull(userDO.getUserAccount()) && ObjectUtil.isNotNull(userDO.getUserPhone())) {
|
||||
userDO.setUserAccount(userDO.getUserPhone());
|
||||
}
|
||||
UserEntity userEntity = userDomainService.addUser(userDO);
|
||||
UserPo userPo = new UserPo();
|
||||
BeanUtils.copyProperties(userEntity, userPo);
|
||||
return userPo;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description 新增用户
|
||||
* @Author Alex
|
||||
@@ -1388,18 +1462,31 @@ public class UserApplicationService {
|
||||
public UserPo addUserYG(UserDO userDO) {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
String password = userDO.getPlainPassword();
|
||||
if (ObjectUtil.isNull(loginUser)) {
|
||||
throw new DigitalLogisticsException(UserError.TIMEOUT);
|
||||
// 远程Feign调用时可能无loginUser,优先从loginUser取组织信息,否则使用userDO自身携带的数据
|
||||
if (ObjectUtil.isNotNull(loginUser)) {
|
||||
userDO.setOrganizationName(loginUser.getUserPo().getOrganizationName());
|
||||
} else if (ObjectUtil.isNotNull(userDO.getOrganizationId()) && ObjectUtil.isNull(userDO.getTopOrganizationId())) {
|
||||
// 无loginUser时,提前获取一级组织ID,供后续checkAccountInfo使用
|
||||
AjaxResult topOrgResult = productServiceFeign.getTopOrganization(userDO.getOrganizationId());
|
||||
if ("200".equals(topOrgResult.get("code").toString())) {
|
||||
String topOrgData = topOrgResult.get("data").toString();
|
||||
if (ObjectUtil.isNotNull(topOrgData)) {
|
||||
userDO.setTopOrganizationId(Long.parseLong(topOrgData));
|
||||
}
|
||||
}
|
||||
}
|
||||
userDO.setOrganizationName(loginUser.getUserPo().getOrganizationName());
|
||||
//2023年3月24日 bug修改 增加登录账号/手机号唯一性校验
|
||||
commonApplicationService.checkAccountInfo(userDO);
|
||||
if (ObjectUtil.isNotNull(userDO.getUserId())) {
|
||||
return updateUser(userDO);
|
||||
} else {
|
||||
if (ObjectUtil.isNull(userDO.getOrganizationId())) {
|
||||
userDO.setOrganizationId(loginUser.getUserPo().getOrganizationId());
|
||||
userDO.setOrganizationName(loginUser.getUserPo().getOrganizationName());
|
||||
if (ObjectUtil.isNotNull(loginUser)) {
|
||||
userDO.setOrganizationId(loginUser.getUserPo().getOrganizationId());
|
||||
userDO.setOrganizationName(loginUser.getUserPo().getOrganizationName());
|
||||
} else {
|
||||
throw new DigitalLogisticsException(UserError.TIMEOUT);
|
||||
}
|
||||
} else {
|
||||
// 如果前端传递了 organizationId,根据 organizationId 查询组织信息,获取正确的组织名称
|
||||
AjaxResult orgInfoResult = productServiceFeign.getInfo(userDO.getOrganizationId());
|
||||
@@ -1441,7 +1528,14 @@ public class UserApplicationService {
|
||||
|
||||
//组装新增用户信息
|
||||
addUserInfo(userDO);
|
||||
// 远程Feign调用无loginUser时,强制分配默认普通角色,不走管理员
|
||||
if (ObjectUtil.isNull(loginUser)) {
|
||||
userDO.setRoleCode(RoleEnum.TENANT.getCode());
|
||||
userDO.setRoleName(RoleEnum.TENANT.getName());
|
||||
userDO.setUserAccountType(null);
|
||||
}
|
||||
UserEntity userEntity = userDomainService.addUser(userDO);
|
||||
if (ObjectUtil.isNotNull(loginUser)) {
|
||||
if (isCustomerManagementPresetRole(userEntity.getRoleCode())) {
|
||||
// 保持插入结果,不再改 role_code / user_role
|
||||
} else if ((StringUtils.isEmpty(userEntity.getRoleCode()) || !RoleEnum.PLAT_ADMIN.getCode().equals(userEntity.getRoleCode())) &&
|
||||
@@ -1480,6 +1574,22 @@ public class UserApplicationService {
|
||||
userDomainService.updateUser(userDO);
|
||||
}
|
||||
}
|
||||
} // end of loginUser != null check
|
||||
else {
|
||||
RoleDo roleDo = new RoleDo();
|
||||
roleDo.setRoleCode(userEntity.getRoleCode());
|
||||
roleDo.setOrganizationId(userEntity.getOrganizationId());
|
||||
RoleEntity roleEntity = roleDomainService.selectByOrganizationIdAndRoleCode(roleDo);
|
||||
if (roleEntity != null) {
|
||||
UserRoleDo userRoleDo = new UserRoleDo();
|
||||
userRoleDo.setUserId(userEntity.getUserId());
|
||||
userRoleDo.setRoleId(roleEntity.getRoleId());
|
||||
userRoleDomainService.addUserRole(userRoleDo);
|
||||
userDO.setRoleId(roleEntity.getRoleId());
|
||||
userDO.setUserId(userEntity.getUserId());
|
||||
userDomainService.updateUser(userDO);
|
||||
}
|
||||
}
|
||||
BeanUtils.copyProperties(userEntity, userPo);
|
||||
//同步网货平台员工信息
|
||||
try {
|
||||
@@ -1492,13 +1602,15 @@ public class UserApplicationService {
|
||||
// throw e;
|
||||
}
|
||||
//获取组织信息
|
||||
Long organizationId = loginUser.getUserPo().getOrganizationId();
|
||||
Long organizationId = ObjectUtil.isNotNull(loginUser) ? loginUser.getUserPo().getOrganizationId() : userDO.getOrganizationId();
|
||||
Integer isQueue = userMapper.getOrdIsQueue(organizationId);
|
||||
|
||||
if (isQueue==1) {
|
||||
//同步YMS员工信息
|
||||
try {
|
||||
userEntity.setUserPassword(password);
|
||||
if (StringUtils.isNotEmpty(password)) {
|
||||
userEntity.setUserPassword(password);
|
||||
}
|
||||
ymsDomainService.addOrEditYmsUser(userEntity);
|
||||
} catch (Exception e) {
|
||||
// 同步YMS失败时,记录日志但不影响用户保存
|
||||
|
||||
+5
-5
@@ -22,12 +22,12 @@ public class UserRoleFactory {
|
||||
UserRoleEntity userRoleEntity = new UserRoleEntity();
|
||||
BeanUtils.copyProperties(userRoleDo,userRoleEntity, IgnoreNullUtil.getNullPropertyNames(userRoleDo));
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (ObjectUtil.isNull(loginUser)) {
|
||||
throw new DigitalLogisticsException(UserError.TIMEOUT);
|
||||
String userName = null;
|
||||
Long userId = null;
|
||||
if (ObjectUtil.isNotNull(loginUser)) {
|
||||
userName = loginUser.getUsername();
|
||||
userId = loginUser.getUserid();
|
||||
}
|
||||
String userName = loginUser.getUsername();
|
||||
// 获取登录人id
|
||||
Long userId = loginUser.getUserid();
|
||||
if(userRoleDo.getUserRoleId() != null){
|
||||
if(userId != null){
|
||||
userRoleEntity.setUpdateBy(userId);
|
||||
|
||||
+6
-2
@@ -12,6 +12,7 @@ import com.mhd.user.domain.userAggregate.repository.todo.UserDO;
|
||||
import com.mhd.common.core.domain.po.app.UserAppPo;
|
||||
import com.mhd.common.core.annotation.fieldsetvalue.NeedSetValueMethod;
|
||||
import com.mhd.common.security.utils.SecurityUtils;
|
||||
import com.mhd.system.api.model.LoginUser;
|
||||
import com.mhd.user.infrastructure.util.annotation.DataPermissions;
|
||||
import com.mhd.user.interfaces.dto.queryDTO.UserDocumentWarningQueryDTO;
|
||||
import com.mhd.user.interfaces.dto.updateDTO.UpdateHasDefaultAddressDTO;
|
||||
@@ -50,9 +51,12 @@ public class UserDomainService {
|
||||
*/
|
||||
public UserEntity updateUser(UserDO userDO) {
|
||||
UserEntity userEntity = userFactory.getUserEntity(userDO);
|
||||
userEntity.setUpdateBy(SecurityUtils.getLoginUser().getUserid());
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (ObjectUtil.isNotNull(loginUser)) {
|
||||
userEntity.setUpdateBy(loginUser.getUserid());
|
||||
userEntity.setUpdateByName(loginUser.getUsername());
|
||||
}
|
||||
userEntity.setUpdateTime(new Date());
|
||||
userEntity.setUpdateByName(SecurityUtils.getLoginUser().getUsername());
|
||||
userRepositoryInterface.updateUser(userEntity);
|
||||
return userEntity;
|
||||
}
|
||||
|
||||
@@ -283,6 +283,22 @@ public class UserAPI extends BaseController {
|
||||
return R.ok(sysUserVo, "操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 远程Feign调用新增用户(无token),仅操作USER表基本字段
|
||||
*/
|
||||
@ApiOperation("远程Feign修改用户(无token)")
|
||||
@PostMapping("/addUserByFeign")
|
||||
public R<LoginUser> addUserByFeign(@RequestBody UserDTO userDTO) {
|
||||
LoginUser sysUserVo = new LoginUser();
|
||||
UserDO userDO = userAssember.toUserDO(userDTO);
|
||||
UserPo userPo = userApplicationService.addUserByFeign(userDO);
|
||||
if (ObjectUtil.isNotNull(userPo)) {
|
||||
sysUserVo.setUserid(userPo.getUserId());
|
||||
}
|
||||
sysUserVo.setUserPo(userPo);
|
||||
return R.ok(sysUserVo, "操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description 新增合作商
|
||||
* @Author Alex
|
||||
|
||||
@@ -19,14 +19,12 @@ spring:
|
||||
nacos:
|
||||
discovery:
|
||||
# server-addr: 127.0.0.1:8848
|
||||
# 线上测试环境配置 容器名+端口号
|
||||
# server-addr: 10.33.0.129:6010
|
||||
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.31:6848
|
||||
username: nacos
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.mhd.bms.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* MyBatis-Plus 分页插件配置
|
||||
* 不注册该拦截器时,Mapper 里传入的 Page 不生效:SQL 不分页、total 恒为 0
|
||||
*/
|
||||
@Configuration
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
@Bean
|
||||
public PaginationInterceptor paginationInterceptor() {
|
||||
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
|
||||
// 达梦数据库(不设置也会根据 jdbc:dm:// 自动识别,这里显式指定更稳妥)
|
||||
paginationInterceptor.setDialectType(DbType.DM.getDb());
|
||||
return paginationInterceptor;
|
||||
}
|
||||
}
|
||||
@@ -52,11 +52,14 @@ public class BmsInvoiceListApi extends BaseController {
|
||||
@ApiOperation(value = "销项发票表-分页列表查询", notes = "销项发票表-分页列表查询")
|
||||
@RequestMapping(value = "/queryList")
|
||||
public Result<IPage<BmsInvoiceListVO>> queryList(BmsInvoiceListQueryDTO bmsInvoiceListQueryDTO,
|
||||
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
|
||||
@RequestParam(name="pageNo", required=false) Integer pageNo,
|
||||
@RequestParam(name="pageNum", required=false) Integer pageNum,
|
||||
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
|
||||
HttpServletRequest req) {
|
||||
// 前端分页参数名不统一(pageNum / pageNo),两个都兼容,优先 pageNo
|
||||
int currentPage = pageNo != null ? pageNo : (pageNum != null ? pageNum : 1);
|
||||
Result<IPage<BmsInvoiceListVO>> result = new Result<IPage<BmsInvoiceListVO>>();
|
||||
Page<BmsInvoiceListVO> pageList = new Page<BmsInvoiceListVO>(pageNo,pageSize);
|
||||
Page<BmsInvoiceListVO> pageList = new Page<BmsInvoiceListVO>(currentPage,pageSize);
|
||||
pageList = bmsInvoiceListService.queryList(pageList, bmsInvoiceListQueryDTO);
|
||||
result.setResult(pageList);
|
||||
result.setSuccess(true);
|
||||
|
||||
@@ -18,15 +18,15 @@ spring:
|
||||
discovery:
|
||||
# server-addr: 127.0.0.1:8848
|
||||
# 线上测试环境配置 容器名+端口号
|
||||
# server-addr: 10.33.0.129:6010
|
||||
server-addr: 10.102.192.31:6848
|
||||
server-addr: 10.33.0.129:6010
|
||||
# server-addr: 10.102.192.30: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.31:6848
|
||||
server-addr: 10.33.0.129:6010
|
||||
# server-addr: 10.102.192.30:6848
|
||||
username: nacos
|
||||
password: manhuoda@2023
|
||||
group: DEFAULT_GROUP # 默认分组就是DEFAULT_GROUP,如果使用默认分组可以不配置
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
where a.is_delete = 0
|
||||
<include refid="common_where"></include>
|
||||
group by a.id
|
||||
order by a.create_time desc
|
||||
order by a.create_time desc, a.id desc
|
||||
</select>
|
||||
<select id="getById" parameterType="java.lang.String" resultType="com.mhd.bms.domain.bmsInvoiceList.repository.vo.BmsInvoiceListDetailsVO">
|
||||
select
|
||||
|
||||
@@ -18,17 +18,15 @@ spring:
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# server-addr: 127.0.0.1:8848
|
||||
# 线上测试环境配置 容器名+端口号
|
||||
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.30:6848
|
||||
username: nacos
|
||||
password: manhuoda@2023
|
||||
config:
|
||||
# server-addr: 127.0.0.1:8848
|
||||
# 线上测试环境配置 容器名+端口号
|
||||
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.30:6848
|
||||
username: nacos
|
||||
password: manhuoda@2023
|
||||
#线上正式环境
|
||||
|
||||
+6
-2
@@ -103,10 +103,14 @@ public class BusinessDocumentOrderApplicationService {
|
||||
|
||||
public List<BusinessDocumentOrderPO> queryList(BusinessDocumentOrderDO businessDocumentOrderDO) {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (loginUser != null){
|
||||
if (loginUser != null && loginUser.getUserPo() != null){
|
||||
// 前端未传组织ID时,查询当前登录组织的数据
|
||||
if (businessDocumentOrderDO.getOrganizationId() == null){
|
||||
businessDocumentOrderDO.setOrganizationId(loginUser.getUserPo().getOrganizationId());
|
||||
}
|
||||
Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId();
|
||||
if (topOrganizationId != null && topOrganizationId != 1){
|
||||
businessDocumentOrderDO.setTopOrganizationId(loginUser.getUserPo().getTopOrganizationId());
|
||||
businessDocumentOrderDO.setTopOrganizationId(topOrganizationId);
|
||||
}
|
||||
}
|
||||
return businessDocumentOrderDomainService.queryList(businessDocumentOrderDO);
|
||||
|
||||
+390
@@ -0,0 +1,390 @@
|
||||
package com.mhd.oms.application.service.outEntrustOrder;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.mhd.common.core.exception.ServiceException;
|
||||
import com.mhd.common.core.web.domain.AjaxResult;
|
||||
import com.mhd.common.security.utils.SecurityUtils;
|
||||
import com.mhd.oms.constant.OutEntrustOrderConstant;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.po.OutEntrustOrderDetailPO;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.po.OutEntrustOrderPO;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDO;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDetailDO;
|
||||
import com.mhd.oms.domain.outEntrustOrder.service.OutEntrustOrderDomainService;
|
||||
import com.mhd.system.api.SystemServiceFeign;
|
||||
import com.mhd.system.api.WmsServiceFeign;
|
||||
import com.mhd.system.api.domain.NoticeMaterialDetailDTO;
|
||||
import com.mhd.system.api.domain.NoticeOrderDTO;
|
||||
import com.mhd.system.api.domain.WarehouseFeignPO;
|
||||
import com.mhd.system.api.model.LoginUser;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 出仓委托单ApplicationService
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-09-02
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class OutEntrustOrderApplicationService {
|
||||
|
||||
@Resource
|
||||
private OutEntrustOrderDomainService outEntrustOrderDomainService;
|
||||
|
||||
@Resource
|
||||
private SystemServiceFeign systemServiceFeign;
|
||||
|
||||
@Resource
|
||||
private WmsServiceFeign wmsServiceFeign;
|
||||
|
||||
/**
|
||||
* 分页查询出仓委托单列表(不做默认状态过滤,显示全部数据)
|
||||
*/
|
||||
public List<OutEntrustOrderPO> queryList(OutEntrustOrderDO outEntrustOrderDO) {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (loginUser != null) {
|
||||
Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId();
|
||||
if (topOrganizationId != null && topOrganizationId != 1) {
|
||||
outEntrustOrderDO.setTopOrganizationId(topOrganizationId);
|
||||
}
|
||||
}
|
||||
//结束时间只传年月日(解析为当天00:00:00),归一为当天最后一秒,保证查询包含结束日当天
|
||||
if (outEntrustOrderDO.getEndCreateTime() != null) {
|
||||
outEntrustOrderDO.setEndCreateTime(DateUtil.endOfDay(outEntrustOrderDO.getEndCreateTime()));
|
||||
}
|
||||
List<OutEntrustOrderPO> orderList = outEntrustOrderDomainService.queryList(outEntrustOrderDO);
|
||||
fillInvoiceNo(orderList);
|
||||
return orderList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表回填发票号(明细发票号去重逗号拼接,供列表列展示)
|
||||
*/
|
||||
private void fillInvoiceNo(List<OutEntrustOrderPO> orderList) {
|
||||
if (orderList == null || orderList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<String> entrustNumbers = orderList.stream()
|
||||
.map(OutEntrustOrderPO::getEntrustNumber).collect(Collectors.toList());
|
||||
Map<String, String> invoiceNoMap = outEntrustOrderDomainService.getDetailByEntrustNumbers(entrustNumbers).stream()
|
||||
.filter(detail -> detail.getInvoiceNo() != null && !detail.getInvoiceNo().trim().isEmpty())
|
||||
.collect(Collectors.toMap(OutEntrustOrderDetailPO::getEntrustNumber, detail -> detail.getInvoiceNo().trim(),
|
||||
(existing, replacement) -> existing.contains(replacement) ? existing
|
||||
: existing + "," + replacement));
|
||||
for (OutEntrustOrderPO order : orderList) {
|
||||
order.setInvoiceNo(invoiceNoMap.get(order.getEntrustNumber()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增出仓委托单(必填:客户、业务员、仓库、商品名称、申报数量)
|
||||
*/
|
||||
public Boolean insert(OutEntrustOrderDO outEntrustOrderDO) {
|
||||
//必填项校验
|
||||
validateRequired(outEntrustOrderDO);
|
||||
//订单类型未维护时默认普通出库(落库即生效,列表可见)
|
||||
setDefaultOrderType(outEntrustOrderDO);
|
||||
//仓库信息回填(校验仓库存在)
|
||||
setWarehouseInfo(outEntrustOrderDO);
|
||||
//组织信息填充
|
||||
setOrganizationInfo(outEntrustOrderDO);
|
||||
//汇总申报总数量
|
||||
outEntrustOrderDO.setDeclareTotalQuantity(sumDeclareQuantity(outEntrustOrderDO.getMaterialDetailList()));
|
||||
return outEntrustOrderDomainService.insert(outEntrustOrderDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改出仓委托单(仅待审核/已驳回且未下发可修改;已驳回单编辑后状态重置为待审核)
|
||||
*/
|
||||
public Boolean update(OutEntrustOrderDO outEntrustOrderDO) {
|
||||
//必填项校验
|
||||
validateRequired(outEntrustOrderDO);
|
||||
//状态类字段由审核/下发/完成入库接口推进,编辑不允许改动(置null后updateById跳过这些列,防前端误传/绕过审核改状态)
|
||||
outEntrustOrderDO.setOrderStatus(null);
|
||||
outEntrustOrderDO.setIssueStatus(null);
|
||||
outEntrustOrderDO.setAuditRemark(null);
|
||||
//订单类型未维护时默认普通出库(落库即生效,列表可见)
|
||||
setDefaultOrderType(outEntrustOrderDO);
|
||||
//仓库信息回填(校验仓库存在)
|
||||
setWarehouseInfo(outEntrustOrderDO);
|
||||
//明细全量替换时明细创建信息随主单更新人
|
||||
if (outEntrustOrderDO.getCreateBy() == null) {
|
||||
outEntrustOrderDO.setCreateBy(outEntrustOrderDO.getUpdateBy());
|
||||
outEntrustOrderDO.setCreateByName(outEntrustOrderDO.getUpdateByName());
|
||||
outEntrustOrderDO.setCreateTime(outEntrustOrderDO.getUpdateTime());
|
||||
}
|
||||
//汇总申报总数量
|
||||
outEntrustOrderDO.setDeclareTotalQuantity(sumDeclareQuantity(outEntrustOrderDO.getMaterialDetailList()));
|
||||
return outEntrustOrderDomainService.update(outEntrustOrderDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单类型未维护时默认普通出库(与下发兜底保持一致,保存即落库)
|
||||
*/
|
||||
private void setDefaultOrderType(OutEntrustOrderDO outEntrustOrderDO) {
|
||||
if (outEntrustOrderDO.getOrderTypeCode() == null || outEntrustOrderDO.getOrderTypeCode().trim().isEmpty()) {
|
||||
outEntrustOrderDO.setOrderTypeCode(OutEntrustOrderConstant.WMS_ORDER_TYPE_CODE);
|
||||
outEntrustOrderDO.setOrderTypeName(OutEntrustOrderConstant.WMS_ORDER_TYPE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取出仓委托单详情(含明细)
|
||||
*/
|
||||
public OutEntrustOrderPO getInfo(Long id) {
|
||||
return outEntrustOrderDomainService.getInfo(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除出仓委托单(仅待审核/已驳回且未下发可删除)
|
||||
*/
|
||||
public Boolean delete(Long[] ids) {
|
||||
if (ids == null || ids.length == 0) {
|
||||
throw new ServiceException("传递的ID值缺失或为空");
|
||||
}
|
||||
return outEntrustOrderDomainService.delete(ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量审核:待审核/已驳回 → 已审核(2)或已驳回(3)
|
||||
*
|
||||
* @param dto.idList 委托单ID集合
|
||||
* @param dto.orderStatus 审核结果:2-审核通过 3-驳回
|
||||
* @param dto.auditRemark 审核备注(驳回原因,选填)
|
||||
*/
|
||||
public Boolean audit(OutEntrustOrderDO outEntrustOrderDO) {
|
||||
List<Long> ids = outEntrustOrderDO.getIdList();
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
throw new ServiceException("传递的ID值缺失或为空");
|
||||
}
|
||||
Integer targetStatus = outEntrustOrderDO.getOrderStatus();
|
||||
if (!OutEntrustOrderConstant.ORDER_STATUS_AUDITED.equals(targetStatus)
|
||||
&& !OutEntrustOrderConstant.ORDER_STATUS_REJECTED.equals(targetStatus)) {
|
||||
throw new ServiceException("审核结果参数错误,只能为已审核或已驳回");
|
||||
}
|
||||
//只有待审核/已驳回的委托单才可以审核
|
||||
List<OutEntrustOrderPO> orderList = outEntrustOrderDomainService.queryByIds(ids);
|
||||
List<Long> unableAuditIds = orderList.stream()
|
||||
.filter(order -> !OutEntrustOrderConstant.ORDER_STATUS_PENDING_AUDIT.equals(order.getOrderStatus())
|
||||
&& !OutEntrustOrderConstant.ORDER_STATUS_REJECTED.equals(order.getOrderStatus()))
|
||||
.map(OutEntrustOrderPO::getId).collect(Collectors.toList());
|
||||
if (!unableAuditIds.isEmpty()) {
|
||||
throw new ServiceException("选中数据中存在进行中/已审核/已入库的委托单,只有待审核或已驳回的委托单方可审核");
|
||||
}
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
Long auditBy = loginUser == null ? null : loginUser.getUserid();
|
||||
String auditByName = loginUser == null ? null : loginUser.getUsername();
|
||||
return outEntrustOrderDomainService.audit(ids, targetStatus, auditBy, auditByName, outEntrustOrderDO.getAuditRemark());
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量下发:已审核的委托单下发到WMS(生成出库通知单),下发后订单状态保持已审核,仅置下发状态为已下发
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean issueOrder(List<Long> ids) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
throw new ServiceException("传递的ID值缺失或为空");
|
||||
}
|
||||
//只有已审核且未下发的委托单才可以下发
|
||||
List<OutEntrustOrderPO> orderList = outEntrustOrderDomainService.queryByIds(ids);
|
||||
for (OutEntrustOrderPO order : orderList) {
|
||||
if (!OutEntrustOrderConstant.ORDER_STATUS_AUDITED.equals(order.getOrderStatus())) {
|
||||
throw new ServiceException(String.format("委托单[%s]未审核,已审核的委托单方可下发", order.getEntrustNumber()));
|
||||
}
|
||||
if (!OutEntrustOrderConstant.ISSUE_STATUS_1.equals(order.getIssueStatus())) {
|
||||
throw new ServiceException(String.format("委托单[%s]已下发,不允许重复下发", order.getEntrustNumber()));
|
||||
}
|
||||
}
|
||||
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
Long issueBy = loginUser == null ? null : loginUser.getUserid();
|
||||
String issueByName = loginUser == null ? null : loginUser.getUsername();
|
||||
for (OutEntrustOrderPO order : orderList) {
|
||||
//组装出库通知单
|
||||
NoticeOrderDTO noticeOrderDTO = createNoticeOrderInfo(order);
|
||||
//推送给WMS
|
||||
AjaxResult ajaxResult = wmsServiceFeign.stockOutOrInInsert(noticeOrderDTO);
|
||||
//WMS返回成功才允许置为已下发,避免下发失败被静默吞掉
|
||||
if (ajaxResult == null || !"200".equals(String.valueOf(ajaxResult.get("code")))) {
|
||||
String errorMsg = ajaxResult == null ? "WMS服务调用失败" : String.valueOf(ajaxResult.get("msg"));
|
||||
log.error("出仓委托单[{}]下发WMS失败:{}", order.getEntrustNumber(), errorMsg);
|
||||
throw new ServiceException(String.format("委托单[%s]下发WMS失败:%s", order.getEntrustNumber(), errorMsg));
|
||||
}
|
||||
log.info("出仓委托单[{}]下发WMS成功", order.getEntrustNumber());
|
||||
}
|
||||
return outEntrustOrderDomainService.issue(ids, issueBy, issueByName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量完成:已审核(已下发) → 已入库(OMS人工推进,WMS出库完成后由人工确认)
|
||||
*/
|
||||
public Boolean finish(List<Long> ids) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
throw new ServiceException("传递的ID值缺失或为空");
|
||||
}
|
||||
//只有已审核且已下发的委托单才可以置为已入库(兼容存量:旧版本曾自动置为进行中的也允许)
|
||||
List<OutEntrustOrderPO> orderList = outEntrustOrderDomainService.queryByIds(ids);
|
||||
List<Long> unableFinishIds = orderList.stream()
|
||||
.filter(order -> !(OutEntrustOrderConstant.ORDER_STATUS_IN_PROGRESS.equals(order.getOrderStatus())
|
||||
|| (OutEntrustOrderConstant.ORDER_STATUS_AUDITED.equals(order.getOrderStatus())
|
||||
&& OutEntrustOrderConstant.ISSUE_STATUS_2.equals(order.getIssueStatus()))))
|
||||
.map(OutEntrustOrderPO::getId).collect(Collectors.toList());
|
||||
if (!unableFinishIds.isEmpty()) {
|
||||
throw new ServiceException("选中数据中存在待审核/已驳回或未下发的委托单,只有已审核且已下发的委托单方可置为已入库");
|
||||
}
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
Long updateBy = loginUser == null ? null : loginUser.getUserid();
|
||||
String updateByName = loginUser == null ? null : loginUser.getUsername();
|
||||
return outEntrustOrderDomainService.finish(ids, updateBy, updateByName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装出库通知单信息(客户=货主)
|
||||
*/
|
||||
private NoticeOrderDTO createNoticeOrderInfo(OutEntrustOrderPO order) {
|
||||
NoticeOrderDTO noticeOrderDTO = new NoticeOrderDTO();
|
||||
noticeOrderDTO.setType(OutEntrustOrderConstant.NOTICE_TYPE_OUT);
|
||||
//客户订单号记录委托单号,便于WMS侧追溯
|
||||
noticeOrderDTO.setCustomerOrderNumber(order.getEntrustNumber());
|
||||
noticeOrderDTO.setWarehouseId(order.getWarehouseId());
|
||||
//出仓委托单的客户即WMS货主,货主为物料档案/库存作业的归属
|
||||
noticeOrderDTO.setShipperId(order.getShipperId());
|
||||
noticeOrderDTO.setShipperName(order.getShipperName());
|
||||
noticeOrderDTO.setCustomerId(order.getShipperId());
|
||||
noticeOrderDTO.setCustomerName(order.getShipperName());
|
||||
//订单类型:未维护时默认普通出库(WMS侧按出库单据类型字典翻译)
|
||||
noticeOrderDTO.setOrderTypeCode(order.getOrderTypeCode() == null
|
||||
? OutEntrustOrderConstant.WMS_ORDER_TYPE_CODE : order.getOrderTypeCode());
|
||||
noticeOrderDTO.setOrderTypeName(order.getOrderTypeName());
|
||||
noticeOrderDTO.setExpectTime(order.getExpectTime() == null ? new Date() : order.getExpectTime());
|
||||
noticeOrderDTO.setNullify(2);
|
||||
//出库通知单仅有限几个业务字段:业务员及委托/报关关键信息拼入备注留存(超长截断)
|
||||
noticeOrderDTO.setRemark(buildNoticeRemark(order));
|
||||
//物料明细:申报数量作为通知单计划数量
|
||||
List<OutEntrustOrderDetailPO> detailList = outEntrustOrderDomainService.getDetailByEntrustNumber(order.getEntrustNumber());
|
||||
if (detailList == null || detailList.isEmpty()) {
|
||||
throw new ServiceException(String.format("委托单[%s]缺少商品明细,无法下发", order.getEntrustNumber()));
|
||||
}
|
||||
List<NoticeMaterialDetailDTO> materialDetailList = new ArrayList<>();
|
||||
for (OutEntrustOrderDetailPO detail : detailList) {
|
||||
NoticeMaterialDetailDTO materialDetail = new NoticeMaterialDetailDTO();
|
||||
materialDetail.setMaterialBaseInfoId(detail.getMaterialBaseInfoId());
|
||||
materialDetail.setMaterialCode(detail.getMaterialCode());
|
||||
//物料名称:档案名优先,无档案时用委托单品名
|
||||
materialDetail.setMaterialName(detail.getMaterialName() != null && !detail.getMaterialName().trim().isEmpty()
|
||||
? detail.getMaterialName() : detail.getCommodityName());
|
||||
materialDetail.setBarCode(detail.getBarCode());
|
||||
materialDetail.setQuantity(detail.getDeclareQuantity());
|
||||
materialDetailList.add(materialDetail);
|
||||
}
|
||||
noticeOrderDTO.setMaterialDetailList(materialDetailList);
|
||||
return noticeOrderDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 出库通知单备注:拼入业务员及委托/报关关键信息(通知单无对应字段)
|
||||
*/
|
||||
private String buildNoticeRemark(OutEntrustOrderPO order) {
|
||||
StringBuilder remark = new StringBuilder();
|
||||
if (order.getRemark() != null && !order.getRemark().trim().isEmpty()) {
|
||||
remark.append(order.getRemark().trim());
|
||||
}
|
||||
appendIfPresent(remark, "业务员", order.getSalesmanName());
|
||||
appendIfPresent(remark, "委托编号", order.getEntrustNo());
|
||||
appendIfPresent(remark, "柜号", order.getContainerNo());
|
||||
appendIfPresent(remark, "承运商", order.getCarrierName() != null ? order.getCarrierName() : order.getCarrier());
|
||||
appendIfPresent(remark, "To", order.getToReceiver());
|
||||
appendIfPresent(remark, "联系人", order.getContactPerson());
|
||||
appendIfPresent(remark, "电话", order.getTel());
|
||||
appendIfPresent(remark, "申报地关区", order.getDeclareCustoms());
|
||||
appendIfPresent(remark, "进出境关别", order.getEntryExitCustoms());
|
||||
//备注列长度保护
|
||||
return remark.length() > 480 ? remark.substring(0, 480) : remark.toString();
|
||||
}
|
||||
|
||||
private void appendIfPresent(StringBuilder remark, String label, String value) {
|
||||
if (value != null && !value.trim().isEmpty()) {
|
||||
remark.append(remark.length() > 0 ? ";" : "").append(label).append(":").append(value.trim());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 必填项校验:客户、业务员、仓库、商品名称、申报数量,其余字段不强制
|
||||
*/
|
||||
private void validateRequired(OutEntrustOrderDO outEntrustOrderDO) {
|
||||
if (outEntrustOrderDO.getShipperId() == null) {
|
||||
throw new ServiceException("客户不能为空");
|
||||
}
|
||||
if (outEntrustOrderDO.getSalesmanId() == null || outEntrustOrderDO.getSalesmanId().trim().isEmpty()) {
|
||||
throw new ServiceException("业务员不能为空");
|
||||
}
|
||||
if (outEntrustOrderDO.getWarehouseId() == null) {
|
||||
throw new ServiceException("仓库不能为空");
|
||||
}
|
||||
if (outEntrustOrderDO.getMaterialDetailList() == null || outEntrustOrderDO.getMaterialDetailList().isEmpty()) {
|
||||
throw new ServiceException("商品明细不能为空");
|
||||
}
|
||||
int index = 1;
|
||||
for (OutEntrustOrderDetailDO detail : outEntrustOrderDO.getMaterialDetailList()) {
|
||||
//商品名称:物料档案名/委托单品名至少其一
|
||||
boolean nameBlank = (detail.getMaterialName() == null || detail.getMaterialName().trim().isEmpty())
|
||||
&& (detail.getCommodityName() == null || detail.getCommodityName().trim().isEmpty());
|
||||
if (nameBlank) {
|
||||
throw new ServiceException(String.format("商品明细第%d行商品名称不能为空", index));
|
||||
}
|
||||
if (detail.getDeclareQuantity() == null || detail.getDeclareQuantity().compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new ServiceException(String.format("商品明细第%d行申报数量必须大于0", index));
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 仓库信息校验并回填编码/名称
|
||||
*/
|
||||
private void setWarehouseInfo(OutEntrustOrderDO outEntrustOrderDO) {
|
||||
AjaxResult ajaxResult = systemServiceFeign.getWarehouseInfoByWarehouseId(outEntrustOrderDO.getWarehouseId());
|
||||
if (ajaxResult == null || !"200".equals(String.valueOf(ajaxResult.get("code")))) {
|
||||
throw new ServiceException("获取仓库信息失败,请检查仓库是否有效");
|
||||
}
|
||||
WarehouseFeignPO warehouseFeignPO = JSONUtil.toBean(JSONUtil.toJsonStr(ajaxResult.get("data")), WarehouseFeignPO.class);
|
||||
if (warehouseFeignPO == null || Objects.isNull(warehouseFeignPO.getWarehouseId())) {
|
||||
throw new ServiceException("仓库不存在,请重新选择");
|
||||
}
|
||||
outEntrustOrderDO.setWarehouseCode(warehouseFeignPO.getWarehouseCode());
|
||||
outEntrustOrderDO.setWarehouseName(warehouseFeignPO.getWarehouseName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 组织信息填充
|
||||
*/
|
||||
private void setOrganizationInfo(OutEntrustOrderDO outEntrustOrderDO) {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (loginUser != null && loginUser.getUserPo() != null) {
|
||||
outEntrustOrderDO.setOrganizationId(loginUser.getUserPo().getOrganizationId());
|
||||
outEntrustOrderDO.setOrganizationName(loginUser.getUserPo().getOrganizationName());
|
||||
outEntrustOrderDO.setTopOrganizationId(loginUser.getUserPo().getTopOrganizationId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 汇总明细申报数量
|
||||
*/
|
||||
private BigDecimal sumDeclareQuantity(List<OutEntrustOrderDetailDO> detailList) {
|
||||
return detailList.stream()
|
||||
.map(OutEntrustOrderDetailDO::getDeclareQuantity)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.mhd.oms.constant;
|
||||
|
||||
/**
|
||||
* 出仓委托单常量
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-09-02
|
||||
*/
|
||||
public interface OutEntrustOrderConstant {
|
||||
|
||||
/** 出仓委托单号前缀 */
|
||||
String OUT_ENTRUST_NO_PREFIX = "CKWT";
|
||||
|
||||
/** 订单状态:1-待审核(新增默认值;已驳回单重新编辑后也回到此状态) */
|
||||
Integer ORDER_STATUS_PENDING_AUDIT = 1;
|
||||
/** 订单状态:2-已审核 */
|
||||
Integer ORDER_STATUS_AUDITED = 2;
|
||||
/** 订单状态:3-已驳回 */
|
||||
Integer ORDER_STATUS_REJECTED = 3;
|
||||
/** 订单状态:4-进行中(已下发WMS) */
|
||||
Integer ORDER_STATUS_IN_PROGRESS = 4;
|
||||
/** 订单状态:5-已入库 */
|
||||
Integer ORDER_STATUS_STORED = 5;
|
||||
|
||||
/** 下发状态:1-未下发 */
|
||||
Integer ISSUE_STATUS_1 = 1;
|
||||
/** 下发状态:2-已下发 */
|
||||
Integer ISSUE_STATUS_2 = 2;
|
||||
|
||||
/** 下发WMS出库通知单-订单类型编码 */
|
||||
String WMS_ORDER_TYPE_CODE = "pu_tong_chu_ku";
|
||||
/** 下发WMS出库通知单-订单类型名称 */
|
||||
String WMS_ORDER_TYPE_NAME = "普通出库";
|
||||
|
||||
/** 出库通知单类型(NoticeOrderDTO.type:1-入库通知单 2-出库通知单) */
|
||||
Integer NOTICE_TYPE_OUT = 2;
|
||||
}
|
||||
+10
@@ -749,6 +749,16 @@ public class BusinessDocumentOrderDO extends BaseVOEntity{
|
||||
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
|
||||
private Date layDate;
|
||||
|
||||
@ApiModelProperty(value = "预计起运时间起")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date layDateStart;
|
||||
|
||||
@ApiModelProperty(value = "预计起运时间止")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date layDateEnd;
|
||||
|
||||
@ApiModelProperty("收款账户信息")
|
||||
private String accountInformation;
|
||||
|
||||
|
||||
+3
-3
@@ -737,9 +737,9 @@ public class BusinessDocumentOrderDomainService {
|
||||
// boolean istrue=businessDocumentOrderService.update(businessDocumentOrderDO);
|
||||
|
||||
|
||||
//生成货源单号(yyMMdd + 当日顺序号,每日重置) 原更新操作会将 电脑更细为当日的日期。默认和之前的逻辑一样保持不变
|
||||
String orderCode = goodsNumberGenerator.generateUniqueGoodsNumber(tmsOrder.getDocumentDate());
|
||||
tmsOrder.setGoodsNumber(orderCode);
|
||||
//更新操作保留原业务单号,不做重新生成(单号随单据日期与当日顺序生成,编辑时不应变化)
|
||||
//恢复原单号,避免 BeanUtils.copyProperties 时被前端传入值覆盖
|
||||
tmsOrder.setGoodsNumber(businessDocumentOrderService.getById(businessDocumentOrderDO.getId()).getGoodsNumber());
|
||||
|
||||
ReservationStockOutOrder reservationStockOutOrder = reservationStockOutOrderMapper.selectOne(new LambdaQueryWrapper<ReservationStockOutOrder>()
|
||||
.eq(ReservationStockOutOrder::getOutOrderNumber, inOrderNumber)
|
||||
|
||||
+7
@@ -5,6 +5,7 @@ import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
@@ -41,4 +42,10 @@ public class ExecutionOutMaterialDetailWithOrderPO extends ExecutionOutMaterialD
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date orderOutboundTime;
|
||||
|
||||
@ApiModelProperty("保额")
|
||||
private BigDecimal insureAmount;
|
||||
|
||||
@ApiModelProperty("货物类型:0-物料 1-半成品 2-成品")
|
||||
private Integer goodsType;
|
||||
}
|
||||
|
||||
+2
-1
@@ -53,10 +53,11 @@ public interface IExecutionStockInOrderService extends IService<ExecutionStockIn
|
||||
* 解析Excel文件并获取出库数据
|
||||
* @param file Excel文件
|
||||
* @param sheetName 工作表名称(出库导入/参考数据)
|
||||
* @param warehouseId 当前仓库id(非空时校验导入物料是否属于该仓库)
|
||||
* @return 出库数据DTO
|
||||
* @throws Exception 解析异常
|
||||
*/
|
||||
DeliveryDataDTO parseExcelFile(MultipartFile file, String sheetName) throws Exception;
|
||||
DeliveryDataDTO parseExcelFile(MultipartFile file, String sheetName, Long warehouseId) throws Exception;
|
||||
|
||||
/**
|
||||
* 解析入库单Excel文件
|
||||
|
||||
+10
-1
@@ -35,6 +35,7 @@ import com.mhd.oms.domain.executionStockOutOrder.repository.todo.ExecutionStockO
|
||||
import com.mhd.oms.domain.executionStockOutOrder.service.ExecutionStockOutOrderApplicationService;
|
||||
import com.mhd.oms.domain.reservationDeliveryDetailsLink.entity.ReservationDeliveryDetailsLink;
|
||||
import com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.facade.IReservationDeliveryDetailsLinkService;
|
||||
import com.mhd.oms.domain.reservationStockInOrder.repository.mapper.ReservationStockInOrderMapper;
|
||||
import com.mhd.system.api.SystemServiceFeign;
|
||||
import com.mhd.system.api.UserServiceFeign;
|
||||
import com.mhd.system.api.WmsServiceFeign;
|
||||
@@ -80,6 +81,8 @@ public class ExecutionStockInOrderImpl extends ServiceImpl<ExecutionStockInOrder
|
||||
private SystemServiceFeign systemServiceFeign;
|
||||
@Autowired
|
||||
private IExecutionInMaterialDetailService materialDetailService;
|
||||
@Autowired
|
||||
private ReservationStockInOrderMapper reservationStockInOrderMapper;
|
||||
|
||||
/**
|
||||
* 查询入库单列表
|
||||
@@ -207,7 +210,7 @@ public class ExecutionStockInOrderImpl extends ServiceImpl<ExecutionStockInOrder
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeliveryDataDTO parseExcelFile(MultipartFile file, String sheetName) throws Exception {
|
||||
public DeliveryDataDTO parseExcelFile(MultipartFile file, String sheetName, Long warehouseId) throws Exception {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
// 校验文件和工作表名称(逻辑不变)
|
||||
if (file.isEmpty()) {
|
||||
@@ -263,6 +266,12 @@ public class ExecutionStockInOrderImpl extends ServiceImpl<ExecutionStockInOrder
|
||||
reservationStockOutOrderDO.setShipperCode((String) shipperIds.get(0).get("shipperCode"));
|
||||
reservationStockOutOrderDO.setNeedTransportFlag("1");
|
||||
reservationStockOutOrderDO.setNeedDeclareFlag("1");
|
||||
// 指定仓库时回填单据仓库信息
|
||||
if (warehouseId != null && warehouseId > 0) {
|
||||
reservationStockOutOrderDO.setWarehouseId(warehouseId);
|
||||
reservationStockOutOrderDO.setWarehouseCode(reservationStockInOrderMapper.getWarehouseCode(warehouseId));
|
||||
reservationStockOutOrderDO.setWarehouseName(reservationStockInOrderMapper.getWarehouseName(warehouseId));
|
||||
}
|
||||
reservationStockOutOrderDO.setOrganizationId(loginUser.getUserPo().getOrganizationId());
|
||||
reservationStockOutOrderDO.setTopOrganizationId(loginUser.getUserPo().getTopOrganizationId());
|
||||
reservationStockOutOrderDO.setOrganizationName(loginUser.getUserPo().getOrganizationName());
|
||||
|
||||
+5
@@ -626,6 +626,11 @@ public class ExecutionStockInOrderDomainService {
|
||||
inMaterialDetail.setOrganizationId(orgId);
|
||||
inMaterialDetail.setOrganizationName(orgName);
|
||||
inMaterialDetail.setTopOrganizationId(topOrgId);
|
||||
// 过期日期为空且饮用日期有值时,用饮用日期补齐过期日期(食品业务饮用日期即效期),
|
||||
// 确保下发WMS后入库/收货/库存链路的过期日期能正常回显
|
||||
if (inMaterialDetail.getExpiryDate() == null && inMaterialDetail.getDrinkDate() != null) {
|
||||
inMaterialDetail.setExpiryDate(inMaterialDetail.getDrinkDate());
|
||||
}
|
||||
inMaterialDetailList.add(inMaterialDetail);
|
||||
}
|
||||
wmsServiceFeign.omsInOrderAddDetail(inMaterialDetailList);
|
||||
|
||||
+71
-4
@@ -146,6 +146,8 @@ public class ExecutionStockOutOrderDomainService {
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insert(ExecutionStockOutOrderDO stockOutOrderDO) {
|
||||
//建单校验:明细数量必须大于0(0数量单下发WMS后自动分配必失败,出库单会卡死在已审核状态)
|
||||
validateMaterialDetailQuantity(stockOutOrderDO.getMaterialDetailList());
|
||||
//设置单号
|
||||
Long warehouseId = stockOutOrderDO.getWarehouseId();
|
||||
AjaxResult ajaxResult = systemServiceFeign.getWarehouseInfoByWarehouseId(warehouseId);
|
||||
@@ -252,6 +254,8 @@ public class ExecutionStockOutOrderDomainService {
|
||||
stockOutOrderDO.setBusinessOrderNo(prefix + seq);
|
||||
}
|
||||
//设置物料信息
|
||||
//建单校验:明细数量必须大于0(0数量单下发WMS后自动分配必失败,出库单会卡死在已审核状态)
|
||||
validateMaterialDetailQuantity(stockOutOrderDO.getMaterialDetailList());
|
||||
setMaterialDetailInfo(stockOutOrderDO);
|
||||
//统计种类
|
||||
stockOutOrderDO.setMaterialQuantity(stockOutOrderDO.getMaterialDetailList().size());
|
||||
@@ -493,10 +497,20 @@ public class ExecutionStockOutOrderDomainService {
|
||||
.set(ExecutionStockOutOrder::getUpdateTime, new Date())
|
||||
.in(ExecutionStockOutOrder::getOutOrderId, stockOutOrderDO.getOutOrderIds()));
|
||||
|
||||
//todo下发推送到wms
|
||||
//下发前统一校验所有单据:全部通过后再推送WMS,避免部分单据推送成功、部分失败造成两边数据不一致
|
||||
List<Long> inOrderIds = stockOutOrderDO.getOutOrderIds();
|
||||
List<ExecutionStockOutOrder> issueOrderList = new ArrayList<>();
|
||||
for (Long inOrderId : inOrderIds) {
|
||||
ExecutionStockOutOrder byId = stockOutOrderService.getById(inOrderId);
|
||||
if (byId == null) {
|
||||
throw new ServiceException("出库单不存在,outOrderId=" + inOrderId);
|
||||
}
|
||||
validateIssueOrderDetail(byId);
|
||||
issueOrderList.add(byId);
|
||||
}
|
||||
|
||||
//下发推送到wms
|
||||
for (ExecutionStockOutOrder byId : issueOrderList) {
|
||||
StockOutOrder stockOutOrder = new StockOutOrder();
|
||||
BeanUtils.copyProperties(byId, stockOutOrder,"outOrderId");
|
||||
Long warehouseId = stockOutOrder.getWarehouseId();
|
||||
@@ -535,13 +549,64 @@ public class ExecutionStockOutOrderDomainService {
|
||||
String outOrderNumber = byId.getOutOrderNumber();
|
||||
com.mhd.system.api.domain.StockOutOrderDTO stockOutOrderDTO = new com.mhd.system.api.domain.StockOutOrderDTO();
|
||||
stockOutOrderDTO.setOutOrderNumber(outOrderNumber);
|
||||
wmsServiceFeign.execution(stockOutOrderDTO);
|
||||
//WMS自动分配:校验返回值。此前不校验时,WMS侧分配失败被Feign降级吞掉,OMS显示成功但出库单卡死在WMS"已审核"状态,拣货单永远不生成
|
||||
AjaxResult executionResult = wmsServiceFeign.execution(stockOutOrderDTO);
|
||||
if (executionResult == null || !"200".equals(String.valueOf(executionResult.get("code")))) {
|
||||
throw new ServiceException("出库单[" + outOrderNumber + "]WMS自动分配失败:"
|
||||
+ (executionResult == null ? "WMS服务不可用或已降级" : executionResult.get("msg")));
|
||||
}
|
||||
|
||||
wmsServiceFeign.genPickingOrderByOms(stockOutOrderDTO);
|
||||
//WMS生成拣货单:同样校验返回值,失败即中断并回滚,让用户能感知到错误
|
||||
AjaxResult genPickingResult = wmsServiceFeign.genPickingOrderByOms(stockOutOrderDTO);
|
||||
if (genPickingResult == null || !"200".equals(String.valueOf(genPickingResult.get("code")))) {
|
||||
throw new ServiceException("出库单[" + outOrderNumber + "]WMS生成拣货单失败:"
|
||||
+ (genPickingResult == null ? "WMS服务不可用或已降级" : genPickingResult.get("msg")));
|
||||
}
|
||||
}
|
||||
return update;
|
||||
}
|
||||
|
||||
/**
|
||||
* 建单校验:出库明细数量必须大于0
|
||||
* 历史缺陷:0数量明细的单下发到WMS后自动分配必然失败,且异常被吞,出库单卡死在"已审核"状态无法生成拣货单
|
||||
*/
|
||||
private void validateMaterialDetailQuantity(List<ExecutionOutMaterialDetailDO> materialDetailList) {
|
||||
if (CollectionUtils.isEmpty(materialDetailList)) {
|
||||
throw new ServiceException("出库单明细不能为空");
|
||||
}
|
||||
for (ExecutionOutMaterialDetailDO detail : materialDetailList) {
|
||||
if (detail.getQuantity() == null || detail.getQuantity().compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new ServiceException("出库明细数量必须大于0,物料:"
|
||||
+ (detail.getMaterialCode() != null ? detail.getMaterialCode() : detail.getMaterialName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下发前校验出库单明细:
|
||||
* 1.必须有物料明细,且数量大于0;
|
||||
* 2.明细必须已关联库存(materialInventoryId),否则WMS按库存ID自动分配会空指针;
|
||||
* 校验放在推送WMS之前,失败直接报错回滚,避免出库单卡死在WMS"已审核"状态
|
||||
*/
|
||||
private void validateIssueOrderDetail(ExecutionStockOutOrder order) {
|
||||
String outOrderNumber = order.getOutOrderNumber();
|
||||
List<ExecutionOutMaterialDetail> detailList = outMaterialDetailService.list(new QueryWrapper<ExecutionOutMaterialDetail>().lambda()
|
||||
.eq(ExecutionOutMaterialDetail::getOutOrderNumber, outOrderNumber));
|
||||
if (CollectionUtils.isEmpty(detailList)) {
|
||||
throw new ServiceException("出库单[" + outOrderNumber + "]没有物料明细,无法下发");
|
||||
}
|
||||
for (ExecutionOutMaterialDetail detail : detailList) {
|
||||
String materialDesc = detail.getMaterialCode() != null ? detail.getMaterialCode()
|
||||
: (detail.getMaterialName() != null ? detail.getMaterialName() : String.valueOf(detail.getMaterialDetailId()));
|
||||
if (detail.getQuantity() == null || detail.getQuantity().compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new ServiceException("出库单[" + outOrderNumber + "]物料[" + materialDesc + "]数量为0,无法下发");
|
||||
}
|
||||
if (detail.getMaterialInventoryId() == null || detail.getMaterialInventoryId() <= 0) {
|
||||
throw new ServiceException("出库单[" + outOrderNumber + "]物料[" + materialDesc + "]未关联库存,无法下发");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 修改出库单
|
||||
* @author ZhouGY
|
||||
@@ -963,7 +1028,9 @@ public class ExecutionStockOutOrderDomainService {
|
||||
ReservationDeliveryDetailsLink reservationDeliveryDetailsLink = list.get(0);
|
||||
String orderNumber = reservationDeliveryDetailsLink.getOrderNumber();
|
||||
Integer bag = reservationDeliveryDetailsLink.getBag();
|
||||
ExecutionStockOutOrder stockOutOrder = stockOutOrderService.getOne(new QueryWrapper<ExecutionStockOutOrder>().lambda().eq(ExecutionStockOutOrder::getOutOrderNumber, orderNumber), false);
|
||||
ExecutionStockOutOrder stockOutOrder = stockOutOrderService.getOne(new QueryWrapper<ExecutionStockOutOrder>().lambda()
|
||||
.eq(ExecutionStockOutOrder::getOutOrderNumber, orderNumber)
|
||||
.eq(ExecutionStockOutOrder::getDelFlag,1), false);
|
||||
if (stockOutOrder != null) {
|
||||
ExecutionStockInOrder reservationStockInOrder = new ExecutionStockInOrder();
|
||||
reservationStockInOrder.setPictureUrl(pictureUrl);
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.mhd.oms.domain.inboundRank.repository.facade;
|
||||
|
||||
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundMaterialRankPO;
|
||||
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundRankTotalPO;
|
||||
import com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 入库排名统计Service接口
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
public interface IExecutionInboundRankService {
|
||||
|
||||
/**
|
||||
* 商品入库排名列表(仓单+货品汇总,按件数降序)
|
||||
*/
|
||||
List<ExecutionInboundMaterialRankPO> queryMaterialRankList(ExecutionInboundRankDO executionInboundRankDO);
|
||||
|
||||
/**
|
||||
* 商品入库排名合计(不分页)
|
||||
*/
|
||||
ExecutionInboundRankTotalPO queryMaterialRankTotal(ExecutionInboundRankDO executionInboundRankDO);
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.mhd.oms.domain.inboundRank.repository.mapper;
|
||||
|
||||
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundMaterialRankPO;
|
||||
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundRankTotalPO;
|
||||
import com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 入库排名统计Mapper接口
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
public interface ExecutionInboundRankMapper {
|
||||
|
||||
/**
|
||||
* 商品入库排名列表(按物料编码合并,按件数降序,过滤0件行)
|
||||
*/
|
||||
List<ExecutionInboundMaterialRankPO> queryMaterialRankList(ExecutionInboundRankDO executionInboundRankDO);
|
||||
|
||||
/**
|
||||
* 商品入库排名合计(不分页,当前筛选条件全量合计)
|
||||
*/
|
||||
ExecutionInboundRankTotalPO queryMaterialRankTotal(ExecutionInboundRankDO executionInboundRankDO);
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.mhd.oms.domain.inboundRank.repository.persistence;
|
||||
|
||||
import com.mhd.oms.domain.inboundRank.repository.facade.IExecutionInboundRankService;
|
||||
import com.mhd.oms.domain.inboundRank.repository.mapper.ExecutionInboundRankMapper;
|
||||
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundMaterialRankPO;
|
||||
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundRankTotalPO;
|
||||
import com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 入库排名统计Service业务层处理
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Service
|
||||
public class ExecutionInboundRankImpl implements IExecutionInboundRankService {
|
||||
|
||||
@Autowired
|
||||
private ExecutionInboundRankMapper executionInboundRankMapper;
|
||||
|
||||
@Override
|
||||
public List<ExecutionInboundMaterialRankPO> queryMaterialRankList(ExecutionInboundRankDO executionInboundRankDO) {
|
||||
return executionInboundRankMapper.queryMaterialRankList(executionInboundRankDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutionInboundRankTotalPO queryMaterialRankTotal(ExecutionInboundRankDO executionInboundRankDO) {
|
||||
return executionInboundRankMapper.queryMaterialRankTotal(executionInboundRankDO);
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package com.mhd.oms.domain.inboundRank.repository.po;
|
||||
|
||||
import com.mhd.common.core.annotation.Excel;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 入库排名统计PO(一行 = 一个物料的合计(按物料编码合并,查询区间内全部已入库仓单),按件数降序排名)
|
||||
*
|
||||
* <p>数据源跨库取 WMS 入库表(NGWL_TEST_WMS.stock_in_order / in_material_detail,OMS 执行单状态/上架量不回传),
|
||||
* 口径与 WMS 端 /inboundRankApi 完全一致,两边数字一致。
|
||||
* 字段口径:件数=实际上架量(SHELVES_QUANTITY)合计;毛重(吨)=明细总毛重KG/1000;体积=明细合计;
|
||||
* 投保总额=货品档案保额(NGWL_TEST_WMS.material_base_info.insure_amount,物料维度单一值取MAX,不跨仓单累加);
|
||||
* 仓单编号/客户编号/存货人/入仓日期为单据级字段,按物料合并后单值失真,仅作查询条件不返回</p>
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "入库排名统计对象", description = "入库排名统计响应对象")
|
||||
public class ExecutionInboundMaterialRankPO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("物料编码(合并依据)")
|
||||
@Excel(name = "物料编码")
|
||||
private String materialCode;
|
||||
|
||||
@ApiModelProperty("货名/商品名称")
|
||||
@Excel(name = "货名")
|
||||
private String materialName;
|
||||
|
||||
@ApiModelProperty("规格型号")
|
||||
@Excel(name = "规格型号")
|
||||
private String specificationModel;
|
||||
|
||||
@ApiModelProperty("单位")
|
||||
@Excel(name = "单位")
|
||||
private String unitName;
|
||||
|
||||
@ApiModelProperty("件数(该物料实际上架量合计 SHELVES_QUANTITY,排名依据)")
|
||||
@Excel(name = "件数")
|
||||
private BigDecimal pieceCount;
|
||||
|
||||
@ApiModelProperty("毛重(吨)(该物料合计,KG/1000)")
|
||||
@Excel(name = "毛重(吨)")
|
||||
private BigDecimal grossWeightTon;
|
||||
|
||||
@ApiModelProperty("投保总额(货品档案保额,物料维度单一值)")
|
||||
@Excel(name = "投保总额")
|
||||
private BigDecimal insureAmount;
|
||||
|
||||
@ApiModelProperty("体积(CBM)(该物料合计)")
|
||||
@Excel(name = "体积(CBM)")
|
||||
private BigDecimal totalVolume;
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.mhd.oms.domain.inboundRank.repository.po;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 入库排名统计合计PO
|
||||
*
|
||||
* <p>当前筛选条件下不分页的全量合计,随分页列表一起返回
|
||||
* (totalPieceCount/totalGrossWeightTon/totalInsureAmount/totalVolume/totalOrderCount)</p>
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "入库排名统计合计对象", description = "入库排名统计合计响应对象")
|
||||
public class ExecutionInboundRankTotalPO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("件数合计(实际上架量)")
|
||||
private BigDecimal totalPieceCount;
|
||||
|
||||
@ApiModelProperty("毛重(吨)合计")
|
||||
private BigDecimal totalGrossWeightTon;
|
||||
|
||||
@ApiModelProperty("投保总额合计(各物料保额之和,每物料计一次)")
|
||||
private BigDecimal totalInsureAmount;
|
||||
|
||||
@ApiModelProperty("体积(CBM)合计")
|
||||
private BigDecimal totalVolume;
|
||||
|
||||
@ApiModelProperty("涉及仓单数(当前筛选条件下去重统计)")
|
||||
private Long totalOrderCount;
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.mhd.oms.domain.inboundRank.repository.todo;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 入库排名统计查询DO
|
||||
*
|
||||
* <p>topOrganizationId 由服务端按登录用户注入,前端不传</p>
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Data
|
||||
public class ExecutionInboundRankDO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("入仓日期-开始 yyyy-MM-dd(必填)")
|
||||
private String warehouseDateStart;
|
||||
|
||||
@ApiModelProperty("入仓日期-结束 yyyy-MM-dd(必填)")
|
||||
private String warehouseDateEnd;
|
||||
|
||||
@ApiModelProperty("仓单编号/入库单号(模糊)")
|
||||
private String inOrderNumber;
|
||||
|
||||
@ApiModelProperty("客户编号(模糊)")
|
||||
private String shipperCode;
|
||||
|
||||
@ApiModelProperty("存货人/客户名称(模糊)")
|
||||
private String shipperName;
|
||||
|
||||
@ApiModelProperty("商品货号(模糊)")
|
||||
private String materialCode;
|
||||
|
||||
@ApiModelProperty("货名/商品名称(模糊)")
|
||||
private String materialName;
|
||||
|
||||
@ApiModelProperty("仓库名称(模糊)")
|
||||
private String warehouseName;
|
||||
|
||||
@ApiModelProperty("一级组织表ID(登录用户隔离,非1时注入)")
|
||||
private Long topOrganizationId;
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.mhd.oms.domain.inboundRank.repository.todo;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 入库排名统计入参DTO
|
||||
*
|
||||
* <p>数据源跨库取 WMS 入库链路(NGWL_TEST_WMS.stock_in_order / in_material_detail,OMS 执行单状态/上架量不回传)</p>
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Data
|
||||
public class ExecutionInboundRankDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("入仓日期-开始 yyyy-MM-dd(必填,按入仓单入仓日期筛选)")
|
||||
private String warehouseDateStart;
|
||||
|
||||
@ApiModelProperty("入仓日期-结束 yyyy-MM-dd(必填,按入仓单入仓日期筛选)")
|
||||
private String warehouseDateEnd;
|
||||
|
||||
@ApiModelProperty("仓单编号/入库单号(模糊)")
|
||||
private String inOrderNumber;
|
||||
|
||||
@ApiModelProperty("客户编号(模糊)")
|
||||
private String shipperCode;
|
||||
|
||||
@ApiModelProperty("存货人/客户名称(模糊)")
|
||||
private String shipperName;
|
||||
|
||||
@ApiModelProperty("商品货号(模糊)")
|
||||
private String materialCode;
|
||||
|
||||
@ApiModelProperty("货名/商品名称(模糊)")
|
||||
private String materialName;
|
||||
|
||||
@ApiModelProperty("仓库名称(模糊)")
|
||||
private String warehouseName;
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package com.mhd.oms.domain.inboundRank.service;
|
||||
|
||||
import com.mhd.common.core.exception.ServiceException;
|
||||
import com.mhd.common.core.utils.StringUtils;
|
||||
import com.mhd.common.security.utils.SecurityUtils;
|
||||
import com.mhd.system.api.model.LoginUser;
|
||||
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundMaterialRankPO;
|
||||
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundRankTotalPO;
|
||||
import com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 入库排名统计应用服务
|
||||
*
|
||||
* <p>负责登录用户组织隔离注入、日期区间校验,查询逻辑在 ExecutionInboundRankDomainService → ExecutionInboundRankMapper.xml</p>
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class ExecutionInboundRankApplicationService {
|
||||
|
||||
@Autowired
|
||||
private ExecutionInboundRankDomainService executionInboundRankDomainService;
|
||||
|
||||
/**
|
||||
* 分页查询商品入库排名列表(仓单+货品汇总,按件数降序)
|
||||
*/
|
||||
public List<ExecutionInboundMaterialRankPO> queryMaterialRankList(ExecutionInboundRankDO executionInboundRankDO) {
|
||||
fillLoginContext(executionInboundRankDO);
|
||||
return executionInboundRankDomainService.queryMaterialRankList(executionInboundRankDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品入库排名合计(当前筛选条件全量,不分页)
|
||||
*/
|
||||
public ExecutionInboundRankTotalPO queryMaterialRankTotal(ExecutionInboundRankDO executionInboundRankDO) {
|
||||
return executionInboundRankDomainService.queryMaterialRankTotal(executionInboundRankDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出商品入库排名(不分页全量)
|
||||
*/
|
||||
public List<ExecutionInboundMaterialRankPO> exportMaterialRank(ExecutionInboundRankDO executionInboundRankDO) {
|
||||
fillLoginContext(executionInboundRankDO);
|
||||
return executionInboundRankDomainService.queryMaterialRankList(executionInboundRankDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入登录用户组织隔离,并校验入仓日期区间必填
|
||||
*/
|
||||
private void fillLoginContext(ExecutionInboundRankDO executionInboundRankDO) {
|
||||
if (StringUtils.isEmpty(executionInboundRankDO.getWarehouseDateStart()) || StringUtils.isEmpty(executionInboundRankDO.getWarehouseDateEnd())) {
|
||||
throw new ServiceException("请选择统计日期区间");
|
||||
}
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (loginUser != null && loginUser.getUserPo() != null) {
|
||||
Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId();
|
||||
if (topOrganizationId != null && topOrganizationId != 1) {
|
||||
executionInboundRankDO.setTopOrganizationId(topOrganizationId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.mhd.oms.domain.inboundRank.service;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.mhd.common.core.exception.digitalLogisticsException.DigitalLogisticsException;
|
||||
import com.mhd.common.core.exception.digitalLogisticsException.UserError;
|
||||
import com.mhd.common.core.utils.IgnoreNullUtil;
|
||||
import com.mhd.common.core.utils.bean.BeanUtils;
|
||||
import com.mhd.common.security.utils.SecurityUtils;
|
||||
import com.mhd.system.api.model.LoginUser;
|
||||
import com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDO;
|
||||
import com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDTO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 入库排名统计Assembler
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Component
|
||||
public class ExecutionInboundRankAssembler {
|
||||
|
||||
/**
|
||||
* 转换实体
|
||||
*/
|
||||
public ExecutionInboundRankDO toDO(ExecutionInboundRankDTO executionInboundRankDTO) {
|
||||
ExecutionInboundRankDO executionInboundRankDO = new ExecutionInboundRankDO();
|
||||
// 拷贝
|
||||
BeanUtils.copyProperties(executionInboundRankDTO, executionInboundRankDO, IgnoreNullUtil.getNullPropertyNames(executionInboundRankDTO));
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (ObjectUtil.isNull(loginUser)) {
|
||||
throw new DigitalLogisticsException(UserError.TIMEOUT);
|
||||
}
|
||||
return executionInboundRankDO;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.mhd.oms.domain.inboundRank.service;
|
||||
|
||||
import com.mhd.oms.domain.inboundRank.repository.facade.IExecutionInboundRankService;
|
||||
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundMaterialRankPO;
|
||||
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundRankTotalPO;
|
||||
import com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 入库排名统计领域服务
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class ExecutionInboundRankDomainService {
|
||||
|
||||
@Autowired
|
||||
private IExecutionInboundRankService executionInboundRankService;
|
||||
|
||||
/**
|
||||
* 商品入库排名列表(仓单+货品汇总,按件数降序)
|
||||
*/
|
||||
public List<ExecutionInboundMaterialRankPO> queryMaterialRankList(ExecutionInboundRankDO executionInboundRankDO) {
|
||||
return executionInboundRankService.queryMaterialRankList(executionInboundRankDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品入库排名合计(不分页)
|
||||
*/
|
||||
public ExecutionInboundRankTotalPO queryMaterialRankTotal(ExecutionInboundRankDO executionInboundRankDO) {
|
||||
return executionInboundRankService.queryMaterialRankTotal(executionInboundRankDO);
|
||||
}
|
||||
}
|
||||
+22
-4
@@ -5,6 +5,7 @@ import com.mhd.oms.domain.reservationMaterialInventory.entity.ReservationMateria
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.mapper.ReservationMaterialInventoryMapper;
|
||||
import com.mhd.oms.domain.reservationStockInOrder.repository.mapper.ReservationStockInOrderMapper;
|
||||
import com.mhd.oms.domain.reservationStockInOrder.repository.po.MaterialBaseInfoPO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -13,6 +14,7 @@ import org.springframework.stereotype.Service;
|
||||
* 解决客户端推单把 materialInventoryId 传进 materialBaseInfoId 字段、或漏传导致明细存脏ID的问题。
|
||||
* 三级解析:1.按baseId直查 → 2.按库存ID反查纠正 → 3.按物料编码+组织兜底
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class MaterialBaseInfoResolveService {
|
||||
|
||||
@@ -30,11 +32,23 @@ public class MaterialBaseInfoResolveService {
|
||||
* @return 解析成功返回物料信息(调用方需用 po.getMaterialBaseInfoId() 回填纠正后的ID);全部失败返回 null
|
||||
*/
|
||||
public MaterialBaseInfoPO resolve(Long materialBaseInfoId, Long materialInventoryId, String materialCode, Long organizationId) {
|
||||
// 1. 优先按 baseId 直查(有效ID不受影响)
|
||||
// 1. 优先按 baseId 直查(有效ID不受影响)。
|
||||
// 防张冠李戴:客户端曾把库存ID传进 materialBaseInfoId,若该库存ID恰好撞上另一条有效档案的主键
|
||||
// (如库存1201撞上编码=名称的脏档案"水箱"),按ID查到的档案编码会与明细编码对不上。
|
||||
// 此时 baseId 不可信,继续走库存反查/编码兜底;两级都落空时才退回按ID查到的档案
|
||||
MaterialBaseInfoPO byId = null;
|
||||
if (materialBaseInfoId != null && materialBaseInfoId > 0) {
|
||||
MaterialBaseInfoPO po = reservationStockInOrderMapper.getinfo(materialBaseInfoId);
|
||||
if (po != null) {
|
||||
return po;
|
||||
if (StringUtils.isNotBlank(materialCode) && po.getMaterialCode() != null
|
||||
&& !po.getMaterialCode().trim().equals(materialCode.trim())) {
|
||||
log.warn("物料ID疑似误传:baseInfoId={} 按主键查到档案[{}({})],与明细编码[{}]不一致," +
|
||||
"转库存反查/编码兜底(客户端可能把库存ID传进了materialBaseInfoId)",
|
||||
materialBaseInfoId, po.getMaterialCode(), po.getMaterialName(), materialCode);
|
||||
byId = po;
|
||||
} else {
|
||||
return po;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2. baseId 无效但带了库存ID → 反查库存表拿正确 baseInfoId(修复"库存ID传进baseId"场景)
|
||||
@@ -50,8 +64,12 @@ public class MaterialBaseInfoResolveService {
|
||||
}
|
||||
// 3. 兜底:按物料编码+组织查(修复 baseId=0 漏传场景)
|
||||
if (StringUtils.isNotBlank(materialCode) && organizationId != null) {
|
||||
return reservationStockInOrderMapper.getByCode(materialCode.trim(), organizationId);
|
||||
MaterialBaseInfoPO po = reservationStockInOrderMapper.getByCode(materialCode.trim(), organizationId);
|
||||
if (po != null) {
|
||||
return po;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
// 编码不一致已证明 baseId 不可信,但库存反查/编码兜底都落空时,退回按ID查到的档案,保持原有兜底行为
|
||||
return byId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
package com.mhd.oms.domain.outEntrustOrder.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.mhd.common.core.web.domain.BaseVOEntity;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 出仓委托单对象 oms_out_entrust_order
|
||||
* 表单字段对齐WMS出库管理新增(报关/委托信息块)
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-09-02
|
||||
*/
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@TableName("oms_out_entrust_order")
|
||||
public class OutEntrustOrder extends BaseVOEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("编号")
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty("委托单号")
|
||||
private String entrustNumber;
|
||||
|
||||
@ApiModelProperty("出库仓库ID")
|
||||
private Long warehouseId;
|
||||
@ApiModelProperty("仓库编码")
|
||||
private String warehouseCode;
|
||||
@ApiModelProperty("仓库名称")
|
||||
private String warehouseName;
|
||||
|
||||
@ApiModelProperty("客户ID(货主)")
|
||||
private Long shipperId;
|
||||
@ApiModelProperty("客户编码")
|
||||
private String shipperCode;
|
||||
@ApiModelProperty("客户名称")
|
||||
private String shipperName;
|
||||
|
||||
@ApiModelProperty("业务员ID")
|
||||
private String salesmanId;
|
||||
@ApiModelProperty("业务员名称")
|
||||
private String salesmanName;
|
||||
|
||||
@ApiModelProperty("订单类型编码(字典outDocumentType)")
|
||||
private String orderTypeCode;
|
||||
@ApiModelProperty("订单类型名称")
|
||||
private String orderTypeName;
|
||||
|
||||
@ApiModelProperty("结算币种")
|
||||
private String settlementCurrency;
|
||||
|
||||
@ApiModelProperty("优先级别编码")
|
||||
private String priorityLevelCode;
|
||||
@ApiModelProperty("优先级别名称")
|
||||
private String priorityLevelName;
|
||||
|
||||
@ApiModelProperty("是否直接出库: 1-是 2-否")
|
||||
private Integer directWarehouse;
|
||||
|
||||
@ApiModelProperty("是否复核: 1-是 2-否")
|
||||
private Integer review;
|
||||
|
||||
@ApiModelProperty("预计出货时间")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expectTime;
|
||||
|
||||
@ApiModelProperty("出仓日期")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date outboundDate;
|
||||
|
||||
@ApiModelProperty("orderDate")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date orderDate;
|
||||
|
||||
@ApiModelProperty("完成时间")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date completeTime;
|
||||
|
||||
@ApiModelProperty("To(收货人)")
|
||||
@TableField("to_receiver")
|
||||
private String toReceiver;
|
||||
|
||||
@ApiModelProperty("联系人")
|
||||
private String contactPerson;
|
||||
|
||||
@ApiModelProperty("车型及车牌")
|
||||
private String vehicleModelPlate;
|
||||
|
||||
@ApiModelProperty("委托编号(客户委托单号)")
|
||||
private String entrustNo;
|
||||
|
||||
@ApiModelProperty("Tel电话")
|
||||
private String tel;
|
||||
|
||||
@ApiModelProperty("Fax传真")
|
||||
private String fax;
|
||||
|
||||
@ApiModelProperty("司机签名")
|
||||
private String driverSign;
|
||||
|
||||
@ApiModelProperty("承运商编码")
|
||||
private String carrier;
|
||||
@ApiModelProperty("承运商名称")
|
||||
private String carrierName;
|
||||
|
||||
@ApiModelProperty("柜号")
|
||||
private String containerNo;
|
||||
@ApiModelProperty("柜")
|
||||
private String containerNoName;
|
||||
|
||||
@ApiModelProperty("运输方式编码")
|
||||
private String transportModeCode;
|
||||
@ApiModelProperty("运输方式名称")
|
||||
private String transportModeName;
|
||||
|
||||
@ApiModelProperty("监管方式编码")
|
||||
private String supervisionModeCode;
|
||||
@ApiModelProperty("监管方式名称")
|
||||
private String supervisionModeName;
|
||||
|
||||
@ApiModelProperty("申报地关区")
|
||||
private String declareCustoms;
|
||||
|
||||
@ApiModelProperty("进出境关别")
|
||||
private String entryExitCustoms;
|
||||
|
||||
@ApiModelProperty("起运/运抵国(地区)")
|
||||
private String departureArrivalCountry;
|
||||
|
||||
@ApiModelProperty("海关是否查验货物")
|
||||
private String customsInspectionFlag;
|
||||
|
||||
@ApiModelProperty("是否需要报关(预留,页面隐藏)")
|
||||
private String needDeclareFlag;
|
||||
@ApiModelProperty("是否需要运输(预留,页面隐藏)")
|
||||
private String needTransportFlag;
|
||||
|
||||
@ApiModelProperty("生产商")
|
||||
private String manufacturer;
|
||||
|
||||
@ApiModelProperty("报关员姓名及联系方式")
|
||||
private String customsDeclarerInfo;
|
||||
|
||||
@ApiModelProperty("送货地址")
|
||||
private String deliveryAddr;
|
||||
|
||||
@ApiModelProperty("附件url")
|
||||
private String annexUrl;
|
||||
|
||||
@ApiModelProperty("订单状态:1-待审核 2-已审核 3-已驳回 4-进行中 5-已入库")
|
||||
private Integer orderStatus;
|
||||
|
||||
@ApiModelProperty("审核人ID")
|
||||
private Long auditBy;
|
||||
@ApiModelProperty("审核人姓名")
|
||||
private String auditByName;
|
||||
@ApiModelProperty("审核时间")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date auditTime;
|
||||
@ApiModelProperty("审核备注(驳回原因)")
|
||||
private String auditRemark;
|
||||
|
||||
@ApiModelProperty("下发状态:1-未下发 2-已下发")
|
||||
private Integer issueStatus;
|
||||
@ApiModelProperty("下发人ID")
|
||||
private Long issueBy;
|
||||
@ApiModelProperty("下发人姓名")
|
||||
private String issueByName;
|
||||
@ApiModelProperty("下发时间")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date issueTime;
|
||||
|
||||
@ApiModelProperty("申报总数量(明细申报数量之和)")
|
||||
private BigDecimal declareTotalQuantity;
|
||||
|
||||
@ApiModelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ApiModelProperty("组织ID")
|
||||
private Long organizationId;
|
||||
@ApiModelProperty("组织名称")
|
||||
private String organizationName;
|
||||
@ApiModelProperty("一级组织ID")
|
||||
private Long topOrganizationId;
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package com.mhd.oms.domain.outEntrustOrder.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.mhd.common.core.web.domain.BaseVOEntity;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 出仓委托单明细对象 oms_out_entrust_order_detail
|
||||
* 明细列对齐WMS出库管理新增(出仓委托单明细列)
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-09-02
|
||||
*/
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@TableName("oms_out_entrust_order_detail")
|
||||
public class OutEntrustOrderDetail extends BaseVOEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("编号")
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty("委托单号(关联主表)")
|
||||
private String entrustNumber;
|
||||
|
||||
@ApiModelProperty("物料基础信息ID")
|
||||
private Long materialBaseInfoId;
|
||||
@ApiModelProperty("商品编码")
|
||||
private String materialCode;
|
||||
@ApiModelProperty("商品名称(物料档案名)")
|
||||
private String materialName;
|
||||
@ApiModelProperty("商品条形码")
|
||||
private String barCode;
|
||||
|
||||
@ApiModelProperty("商品名称(委托单品名)")
|
||||
private String commodityName;
|
||||
@ApiModelProperty("商品编号")
|
||||
private String commodityNo;
|
||||
@ApiModelProperty("料号")
|
||||
private String materialNo;
|
||||
@ApiModelProperty("规格型号")
|
||||
private String specificationModel;
|
||||
@ApiModelProperty("SKU码")
|
||||
private String skucode;
|
||||
@ApiModelProperty("发票号")
|
||||
private String invoiceNo;
|
||||
|
||||
@ApiModelProperty("申报单位")
|
||||
private String declareUnit;
|
||||
@ApiModelProperty("申报数量")
|
||||
private BigDecimal declareQuantity;
|
||||
@ApiModelProperty("单价")
|
||||
private BigDecimal unitPrice;
|
||||
|
||||
@ApiModelProperty("单位")
|
||||
private String unit;
|
||||
@ApiModelProperty("升")
|
||||
private String liter;
|
||||
@ApiModelProperty("尺寸")
|
||||
private String size;
|
||||
@ApiModelProperty("箱数")
|
||||
private BigDecimal caseCount;
|
||||
@ApiModelProperty("件数")
|
||||
private BigDecimal pieceCount;
|
||||
@ApiModelProperty("出库数量")
|
||||
private BigDecimal outboundQuantity;
|
||||
@ApiModelProperty("总净重(千克)")
|
||||
private BigDecimal totalNetWeight;
|
||||
@ApiModelProperty("总毛重(千克)")
|
||||
private BigDecimal totalGrossWeight;
|
||||
@ApiModelProperty("总体积(立方米)")
|
||||
private BigDecimal totalVolume;
|
||||
@ApiModelProperty("总面积")
|
||||
private BigDecimal totalArea;
|
||||
@ApiModelProperty("总金额")
|
||||
private BigDecimal totalAmount;
|
||||
@ApiModelProperty("币制")
|
||||
private String currency;
|
||||
@ApiModelProperty("批次")
|
||||
private String batchRefNo;
|
||||
@ApiModelProperty("单据参考号")
|
||||
private String sheetRefNo;
|
||||
@ApiModelProperty("原产国")
|
||||
private String countryOfOrigin;
|
||||
@ApiModelProperty("箱/托盘号")
|
||||
private String boxPalletNo;
|
||||
|
||||
@ApiModelProperty("备注")
|
||||
private String remark;
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.mhd.oms.domain.outEntrustOrder.repository.facade;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.mhd.oms.domain.outEntrustOrder.entity.OutEntrustOrderDetail;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.po.OutEntrustOrderDetailPO;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDetailDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 出仓委托单明细Service接口
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-09-02
|
||||
*/
|
||||
public interface IOutEntrustOrderDetailService extends IService<OutEntrustOrderDetail> {
|
||||
|
||||
/**
|
||||
* 根据委托单号查询有效明细
|
||||
*/
|
||||
List<OutEntrustOrderDetailPO> queryByEntrustNumber(String entrustNumber);
|
||||
|
||||
/**
|
||||
* 根据委托单号集合查询有效明细
|
||||
*/
|
||||
List<OutEntrustOrderDetailPO> queryByEntrustNumbers(List<String> entrustNumbers);
|
||||
|
||||
/**
|
||||
* 批量保存明细
|
||||
*/
|
||||
Boolean batchInsert(String entrustNumber, List<OutEntrustOrderDetailDO> detailDOList);
|
||||
|
||||
/**
|
||||
* 根据委托单号逻辑删除明细
|
||||
*/
|
||||
Boolean deleteByEntrustNumber(String entrustNumber);
|
||||
|
||||
/**
|
||||
* 根据委托单号集合逻辑删除明细
|
||||
*/
|
||||
Boolean deleteByEntrustNumbers(List<String> entrustNumbers);
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package com.mhd.oms.domain.outEntrustOrder.repository.facade;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.mhd.oms.domain.outEntrustOrder.entity.OutEntrustOrder;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.po.OutEntrustOrderPO;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 出仓委托单Service接口
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-09-02
|
||||
*/
|
||||
public interface IOutEntrustOrderService extends IService<OutEntrustOrder> {
|
||||
|
||||
/**
|
||||
* 分页查询出仓委托单列表
|
||||
*/
|
||||
List<OutEntrustOrderPO> queryList(OutEntrustOrderDO outEntrustOrderDO);
|
||||
|
||||
/**
|
||||
* 新增出仓委托单
|
||||
*/
|
||||
Boolean insert(OutEntrustOrderDO outEntrustOrderDO);
|
||||
|
||||
/**
|
||||
* 修改出仓委托单
|
||||
*/
|
||||
Boolean update(OutEntrustOrderDO outEntrustOrderDO);
|
||||
|
||||
/**
|
||||
* 批量删除出仓委托单
|
||||
*/
|
||||
Boolean delete(Long[] ids);
|
||||
|
||||
/**
|
||||
* 获取出仓委托单详情(含明细)
|
||||
*/
|
||||
OutEntrustOrderPO getInfo(Long id);
|
||||
|
||||
/**
|
||||
* 根据编号集合查询出仓委托单列表
|
||||
*/
|
||||
List<OutEntrustOrderPO> getInfoByIds(List<Long> ids);
|
||||
|
||||
/**
|
||||
* 审核:待审核/已驳回 → 已审核(2)或已驳回(3)
|
||||
*/
|
||||
Boolean audit(List<Long> ids, Integer targetStatus, Long auditBy, String auditByName, String auditRemark);
|
||||
|
||||
/**
|
||||
* 下发:置为已下发,订单状态置为进行中
|
||||
*/
|
||||
Boolean issue(List<Long> ids, Long issueBy, String issueByName);
|
||||
|
||||
/**
|
||||
* 完成:进行中 → 已入库
|
||||
*/
|
||||
Boolean finish(List<Long> ids, Long updateBy, String updateByName);
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.mhd.oms.domain.outEntrustOrder.repository.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.mhd.oms.domain.outEntrustOrder.entity.OutEntrustOrderDetail;
|
||||
|
||||
/**
|
||||
* 出仓委托单明细Mapper接口
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-09-02
|
||||
*/
|
||||
public interface OutEntrustOrderDetailMapper extends BaseMapper<OutEntrustOrderDetail> {
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.mhd.oms.domain.outEntrustOrder.repository.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.mhd.oms.domain.outEntrustOrder.entity.OutEntrustOrder;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.po.OutEntrustOrderPO;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 出仓委托单Mapper接口
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-09-02
|
||||
*/
|
||||
public interface OutEntrustOrderMapper extends BaseMapper<OutEntrustOrder> {
|
||||
|
||||
/**
|
||||
* 查询出仓委托单列表
|
||||
*/
|
||||
List<OutEntrustOrderPO> queryList(OutEntrustOrderDO outEntrustOrderDO);
|
||||
|
||||
/**
|
||||
* 根据编号集合查询出仓委托单列表
|
||||
*/
|
||||
List<OutEntrustOrderPO> getInfoByIds(@Param("idList") List<Long> idList);
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package com.mhd.oms.domain.outEntrustOrder.repository.persistence;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.mhd.oms.domain.outEntrustOrder.entity.OutEntrustOrderDetail;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.facade.IOutEntrustOrderDetailService;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.mapper.OutEntrustOrderDetailMapper;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.po.OutEntrustOrderDetailPO;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDetailDO;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 出仓委托单明细Service业务层处理
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-09-02
|
||||
*/
|
||||
@Service
|
||||
public class OutEntrustOrderDetailImpl extends ServiceImpl<OutEntrustOrderDetailMapper, OutEntrustOrderDetail> implements IOutEntrustOrderDetailService {
|
||||
|
||||
/**
|
||||
* 根据委托单号查询有效明细
|
||||
*/
|
||||
@Override
|
||||
public List<OutEntrustOrderDetailPO> queryByEntrustNumber(String entrustNumber) {
|
||||
List<OutEntrustOrderDetail> detailList = this.list(new LambdaQueryWrapper<OutEntrustOrderDetail>()
|
||||
.eq(OutEntrustOrderDetail::getEntrustNumber, entrustNumber)
|
||||
.eq(OutEntrustOrderDetail::getDelFlag, 1)
|
||||
.orderByAsc(OutEntrustOrderDetail::getId));
|
||||
return detailList.stream().map(detail -> {
|
||||
OutEntrustOrderDetailPO detailPO = new OutEntrustOrderDetailPO();
|
||||
BeanUtils.copyProperties(detail, detailPO);
|
||||
return detailPO;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据委托单号集合查询有效明细
|
||||
*/
|
||||
@Override
|
||||
public List<OutEntrustOrderDetailPO> queryByEntrustNumbers(List<String> entrustNumbers) {
|
||||
if (entrustNumbers == null || entrustNumbers.isEmpty()) {
|
||||
return new java.util.ArrayList<>();
|
||||
}
|
||||
List<OutEntrustOrderDetail> detailList = this.list(new LambdaQueryWrapper<OutEntrustOrderDetail>()
|
||||
.in(OutEntrustOrderDetail::getEntrustNumber, entrustNumbers)
|
||||
.eq(OutEntrustOrderDetail::getDelFlag, 1)
|
||||
.orderByAsc(OutEntrustOrderDetail::getId));
|
||||
return detailList.stream().map(detail -> {
|
||||
OutEntrustOrderDetailPO detailPO = new OutEntrustOrderDetailPO();
|
||||
BeanUtils.copyProperties(detail, detailPO);
|
||||
return detailPO;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存明细
|
||||
*/
|
||||
@Override
|
||||
public Boolean batchInsert(String entrustNumber, List<OutEntrustOrderDetailDO> detailDOList) {
|
||||
List<OutEntrustOrderDetail> detailList = detailDOList.stream().map(detailDO -> {
|
||||
OutEntrustOrderDetail detail = new OutEntrustOrderDetail();
|
||||
BeanUtils.copyProperties(detailDO, detail);
|
||||
detail.setEntrustNumber(entrustNumber);
|
||||
detail.setDelFlag(1);
|
||||
return detail;
|
||||
}).collect(Collectors.toList());
|
||||
return this.saveBatch(detailList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据委托单号逻辑删除明细
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteByEntrustNumber(String entrustNumber) {
|
||||
return this.update(new LambdaUpdateWrapper<OutEntrustOrderDetail>()
|
||||
.eq(OutEntrustOrderDetail::getEntrustNumber, entrustNumber)
|
||||
.eq(OutEntrustOrderDetail::getDelFlag, 1)
|
||||
.set(OutEntrustOrderDetail::getDelFlag, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据委托单号集合逻辑删除明细
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteByEntrustNumbers(List<String> entrustNumbers) {
|
||||
return this.update(new LambdaUpdateWrapper<OutEntrustOrderDetail>()
|
||||
.in(OutEntrustOrderDetail::getEntrustNumber, entrustNumbers)
|
||||
.eq(OutEntrustOrderDetail::getDelFlag, 1)
|
||||
.set(OutEntrustOrderDetail::getDelFlag, 2));
|
||||
}
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
package com.mhd.oms.domain.outEntrustOrder.repository.persistence;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.mhd.common.core.exception.ServiceException;
|
||||
import com.mhd.common.security.utils.SecurityUtils;
|
||||
import com.mhd.oms.constant.OutEntrustOrderConstant;
|
||||
import com.mhd.oms.domain.outEntrustOrder.entity.OutEntrustOrder;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.facade.IOutEntrustOrderService;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.mapper.OutEntrustOrderMapper;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.po.OutEntrustOrderPO;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDO;
|
||||
import com.mhd.system.api.model.LoginUser;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 出仓委托单Service业务层处理
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-09-02
|
||||
*/
|
||||
@Service
|
||||
public class OutEntrustOrderImpl extends ServiceImpl<OutEntrustOrderMapper, OutEntrustOrder> implements IOutEntrustOrderService {
|
||||
|
||||
/**
|
||||
* 查询出仓委托单列表
|
||||
*/
|
||||
@Override
|
||||
public List<OutEntrustOrderPO> queryList(OutEntrustOrderDO outEntrustOrderDO) {
|
||||
return baseMapper.queryList(outEntrustOrderDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增出仓委托单
|
||||
*/
|
||||
@Override
|
||||
public Boolean insert(OutEntrustOrderDO outEntrustOrderDO) {
|
||||
OutEntrustOrder outEntrustOrder = new OutEntrustOrder();
|
||||
BeanUtils.copyProperties(outEntrustOrderDO, outEntrustOrder);
|
||||
return this.save(outEntrustOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改出仓委托单
|
||||
*/
|
||||
@Override
|
||||
public Boolean update(OutEntrustOrderDO outEntrustOrderDO) {
|
||||
OutEntrustOrder outEntrustOrder = new OutEntrustOrder();
|
||||
BeanUtils.copyProperties(outEntrustOrderDO, outEntrustOrder);
|
||||
return this.updateById(outEntrustOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除出仓委托单(仅待审核/已驳回且未下发可删除)
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean delete(Long[] ids) {
|
||||
List<OutEntrustOrder> outEntrustOrders = this.listByIds(Arrays.asList(ids));
|
||||
//只有待审核/已驳回且未下发的委托单才可以删除
|
||||
List<OutEntrustOrder> unableDeleteList = outEntrustOrders.stream()
|
||||
.filter(order -> !(OutEntrustOrderConstant.ORDER_STATUS_PENDING_AUDIT.equals(order.getOrderStatus())
|
||||
|| OutEntrustOrderConstant.ORDER_STATUS_REJECTED.equals(order.getOrderStatus()))
|
||||
|| !OutEntrustOrderConstant.ISSUE_STATUS_1.equals(order.getIssueStatus()))
|
||||
.collect(Collectors.toList());
|
||||
if (!unableDeleteList.isEmpty()) {
|
||||
throw new ServiceException("已审核/进行中/已入库或已下发的委托单不允许删除");
|
||||
}
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
Date now = new Date();
|
||||
for (OutEntrustOrder outEntrustOrder : outEntrustOrders) {
|
||||
outEntrustOrder.setDelFlag(2);
|
||||
outEntrustOrder.setUpdateBy(loginUser == null ? null : loginUser.getUserid());
|
||||
outEntrustOrder.setUpdateByName(loginUser == null ? null : loginUser.getUsername());
|
||||
outEntrustOrder.setUpdateTime(now);
|
||||
}
|
||||
return this.updateBatchById(outEntrustOrders);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取出仓委托单详情
|
||||
*/
|
||||
@Override
|
||||
public OutEntrustOrderPO getInfo(Long id) {
|
||||
OutEntrustOrder outEntrustOrder = this.getOne(new LambdaQueryWrapper<OutEntrustOrder>()
|
||||
.eq(OutEntrustOrder::getId, id).eq(OutEntrustOrder::getDelFlag, 1));
|
||||
if (outEntrustOrder == null) {
|
||||
throw new ServiceException("出仓委托单不存在");
|
||||
}
|
||||
OutEntrustOrderPO outEntrustOrderPO = new OutEntrustOrderPO();
|
||||
BeanUtils.copyProperties(outEntrustOrder, outEntrustOrderPO);
|
||||
return outEntrustOrderPO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据编号集合查询出仓委托单列表
|
||||
*/
|
||||
@Override
|
||||
public List<OutEntrustOrderPO> getInfoByIds(List<Long> ids) {
|
||||
return baseMapper.getInfoByIds(ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核:待审核/已驳回 → 已审核(2)或已驳回(3)
|
||||
*/
|
||||
@Override
|
||||
public Boolean audit(List<Long> ids, Integer targetStatus, Long auditBy, String auditByName, String auditRemark) {
|
||||
return this.update(new LambdaUpdateWrapper<OutEntrustOrder>()
|
||||
.in(OutEntrustOrder::getId, ids)
|
||||
.eq(OutEntrustOrder::getDelFlag, 1)
|
||||
.set(OutEntrustOrder::getOrderStatus, targetStatus)
|
||||
.set(OutEntrustOrder::getAuditBy, auditBy)
|
||||
.set(OutEntrustOrder::getAuditByName, auditByName)
|
||||
.set(OutEntrustOrder::getAuditTime, new Date())
|
||||
.set(OutEntrustOrder::getAuditRemark, auditRemark)
|
||||
.set(OutEntrustOrder::getUpdateBy, auditBy)
|
||||
.set(OutEntrustOrder::getUpdateByName, auditByName)
|
||||
.set(OutEntrustOrder::getUpdateTime, new Date()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下发:下发状态置为已下发(订单状态保持已审核)
|
||||
*/
|
||||
@Override
|
||||
public Boolean issue(List<Long> ids, Long issueBy, String issueByName) {
|
||||
return this.update(new LambdaUpdateWrapper<OutEntrustOrder>()
|
||||
.in(OutEntrustOrder::getId, ids)
|
||||
.eq(OutEntrustOrder::getDelFlag, 1)
|
||||
.set(OutEntrustOrder::getIssueStatus, OutEntrustOrderConstant.ISSUE_STATUS_2)
|
||||
.set(OutEntrustOrder::getIssueBy, issueBy)
|
||||
.set(OutEntrustOrder::getIssueByName, issueByName)
|
||||
.set(OutEntrustOrder::getIssueTime, new Date())
|
||||
.set(OutEntrustOrder::getUpdateBy, issueBy)
|
||||
.set(OutEntrustOrder::getUpdateByName, issueByName)
|
||||
.set(OutEntrustOrder::getUpdateTime, new Date()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成:已审核(已下发)/进行中 → 已入库
|
||||
*/
|
||||
@Override
|
||||
public Boolean finish(List<Long> ids, Long updateBy, String updateByName) {
|
||||
return this.update(new LambdaUpdateWrapper<OutEntrustOrder>()
|
||||
.in(OutEntrustOrder::getId, ids)
|
||||
.eq(OutEntrustOrder::getDelFlag, 1)
|
||||
.set(OutEntrustOrder::getOrderStatus, OutEntrustOrderConstant.ORDER_STATUS_STORED)
|
||||
.set(OutEntrustOrder::getUpdateBy, updateBy)
|
||||
.set(OutEntrustOrder::getUpdateByName, updateByName)
|
||||
.set(OutEntrustOrder::getUpdateTime, new Date()));
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package com.mhd.oms.domain.outEntrustOrder.repository.po;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.mhd.common.core.web.domain.BaseVOEntity;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 出仓委托单明细对象 oms_out_entrust_order_detail
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-09-02
|
||||
*/
|
||||
|
||||
@Data
|
||||
@ApiModel(value = "出仓委托单明细对象", description = "出仓委托单明细响应对象")
|
||||
public class OutEntrustOrderDetailPO extends BaseVOEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("编号")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty("委托单号(关联主表)")
|
||||
private String entrustNumber;
|
||||
|
||||
@ApiModelProperty("物料基础信息ID")
|
||||
private Long materialBaseInfoId;
|
||||
@ApiModelProperty("商品编码")
|
||||
private String materialCode;
|
||||
@ApiModelProperty("商品名称(物料档案名)")
|
||||
private String materialName;
|
||||
@ApiModelProperty("商品条形码")
|
||||
private String barCode;
|
||||
|
||||
@ApiModelProperty("商品名称(委托单品名)")
|
||||
private String commodityName;
|
||||
@ApiModelProperty("商品编号")
|
||||
private String commodityNo;
|
||||
@ApiModelProperty("料号")
|
||||
private String materialNo;
|
||||
@ApiModelProperty("规格型号")
|
||||
private String specificationModel;
|
||||
@ApiModelProperty("SKU码")
|
||||
private String skucode;
|
||||
@ApiModelProperty("发票号")
|
||||
private String invoiceNo;
|
||||
|
||||
@ApiModelProperty("申报单位")
|
||||
private String declareUnit;
|
||||
@ApiModelProperty("申报数量")
|
||||
private BigDecimal declareQuantity;
|
||||
@ApiModelProperty("单价")
|
||||
private BigDecimal unitPrice;
|
||||
|
||||
@ApiModelProperty("单位")
|
||||
private String unit;
|
||||
@ApiModelProperty("升")
|
||||
private String liter;
|
||||
@ApiModelProperty("尺寸")
|
||||
private String size;
|
||||
@ApiModelProperty("箱数")
|
||||
private BigDecimal caseCount;
|
||||
@ApiModelProperty("件数")
|
||||
private BigDecimal pieceCount;
|
||||
@ApiModelProperty("出库数量")
|
||||
private BigDecimal outboundQuantity;
|
||||
@ApiModelProperty("总净重(千克)")
|
||||
private BigDecimal totalNetWeight;
|
||||
@ApiModelProperty("总毛重(千克)")
|
||||
private BigDecimal totalGrossWeight;
|
||||
@ApiModelProperty("总体积(立方米)")
|
||||
private BigDecimal totalVolume;
|
||||
@ApiModelProperty("总面积")
|
||||
private BigDecimal totalArea;
|
||||
@ApiModelProperty("总金额")
|
||||
private BigDecimal totalAmount;
|
||||
@ApiModelProperty("币制")
|
||||
private String currency;
|
||||
@ApiModelProperty("批次")
|
||||
private String batchRefNo;
|
||||
@ApiModelProperty("单据参考号")
|
||||
private String sheetRefNo;
|
||||
@ApiModelProperty("原产国")
|
||||
private String countryOfOrigin;
|
||||
@ApiModelProperty("箱/托盘号")
|
||||
private String boxPalletNo;
|
||||
|
||||
@ApiModelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ApiModelProperty("创建时间")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
}
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
package com.mhd.oms.domain.outEntrustOrder.repository.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.mhd.common.core.web.domain.BaseVOEntity;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 出仓委托单对象 oms_out_entrust_order
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-09-02
|
||||
*/
|
||||
|
||||
@Data
|
||||
@ApiModel(value = "出仓委托单对象", description = "出仓委托单响应对象")
|
||||
public class OutEntrustOrderPO extends BaseVOEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("编号")
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty("委托单号")
|
||||
private String entrustNumber;
|
||||
|
||||
@ApiModelProperty("出库仓库ID")
|
||||
private Long warehouseId;
|
||||
@ApiModelProperty("仓库编码")
|
||||
private String warehouseCode;
|
||||
@ApiModelProperty("仓库名称")
|
||||
private String warehouseName;
|
||||
|
||||
@ApiModelProperty("客户ID(货主)")
|
||||
private Long shipperId;
|
||||
@ApiModelProperty("客户编码")
|
||||
private String shipperCode;
|
||||
@ApiModelProperty("客户名称")
|
||||
private String shipperName;
|
||||
|
||||
@ApiModelProperty("业务员ID")
|
||||
private String salesmanId;
|
||||
@ApiModelProperty("业务员名称")
|
||||
private String salesmanName;
|
||||
|
||||
@ApiModelProperty("订单类型编码(字典outDocumentType)")
|
||||
private String orderTypeCode;
|
||||
@ApiModelProperty("订单类型名称")
|
||||
private String orderTypeName;
|
||||
|
||||
@ApiModelProperty("结算币种")
|
||||
private String settlementCurrency;
|
||||
|
||||
@ApiModelProperty("优先级别编码")
|
||||
private String priorityLevelCode;
|
||||
@ApiModelProperty("优先级别名称")
|
||||
private String priorityLevelName;
|
||||
|
||||
@ApiModelProperty("是否直接出库: 1-是 2-否")
|
||||
private Integer directWarehouse;
|
||||
|
||||
@ApiModelProperty("是否复核: 1-是 2-否")
|
||||
private Integer review;
|
||||
|
||||
@ApiModelProperty("预计出货时间")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expectTime;
|
||||
|
||||
@ApiModelProperty("出仓日期")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date outboundDate;
|
||||
|
||||
@ApiModelProperty("orderDate")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date orderDate;
|
||||
|
||||
@ApiModelProperty("完成时间")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date completeTime;
|
||||
|
||||
@ApiModelProperty("To(收货人)")
|
||||
@JsonProperty("to")
|
||||
private String toReceiver;
|
||||
|
||||
@ApiModelProperty("联系人")
|
||||
private String contactPerson;
|
||||
|
||||
@ApiModelProperty("车型及车牌")
|
||||
private String vehicleModelPlate;
|
||||
|
||||
@ApiModelProperty("委托编号(客户委托单号)")
|
||||
private String entrustNo;
|
||||
|
||||
@ApiModelProperty("Tel电话")
|
||||
private String tel;
|
||||
|
||||
@ApiModelProperty("Fax传真")
|
||||
private String fax;
|
||||
|
||||
@ApiModelProperty("司机签名")
|
||||
private String driverSign;
|
||||
|
||||
@ApiModelProperty("承运商编码")
|
||||
private String carrier;
|
||||
@ApiModelProperty("承运商名称")
|
||||
private String carrierName;
|
||||
|
||||
@ApiModelProperty("柜号")
|
||||
private String containerNo;
|
||||
@ApiModelProperty("柜")
|
||||
private String containerNoName;
|
||||
|
||||
@ApiModelProperty("运输方式编码")
|
||||
private String transportModeCode;
|
||||
@ApiModelProperty("运输方式名称")
|
||||
private String transportModeName;
|
||||
|
||||
@ApiModelProperty("监管方式编码")
|
||||
private String supervisionModeCode;
|
||||
@ApiModelProperty("监管方式名称")
|
||||
private String supervisionModeName;
|
||||
|
||||
@ApiModelProperty("申报地关区")
|
||||
private String declareCustoms;
|
||||
|
||||
@ApiModelProperty("进出境关别")
|
||||
private String entryExitCustoms;
|
||||
|
||||
@ApiModelProperty("起运/运抵国(地区)")
|
||||
private String departureArrivalCountry;
|
||||
|
||||
@ApiModelProperty("海关是否查验货物")
|
||||
private String customsInspectionFlag;
|
||||
|
||||
@ApiModelProperty("是否需要报关(预留,页面隐藏)")
|
||||
private String needDeclareFlag;
|
||||
@ApiModelProperty("是否需要运输(预留,页面隐藏)")
|
||||
private String needTransportFlag;
|
||||
|
||||
@ApiModelProperty("生产商")
|
||||
private String manufacturer;
|
||||
|
||||
@ApiModelProperty("报关员姓名及联系方式")
|
||||
private String customsDeclarerInfo;
|
||||
|
||||
@ApiModelProperty("送货地址")
|
||||
private String deliveryAddr;
|
||||
|
||||
@ApiModelProperty("附件url")
|
||||
private String annexUrl;
|
||||
|
||||
@ApiModelProperty("订单状态:1-待审核 2-已审核 3-已驳回 4-进行中 5-已入库")
|
||||
private Integer orderStatus;
|
||||
@ApiModelProperty("订单状态")
|
||||
private String orderStatusStr;
|
||||
|
||||
@ApiModelProperty("审核人ID")
|
||||
private Long auditBy;
|
||||
@ApiModelProperty("审核人姓名")
|
||||
private String auditByName;
|
||||
@ApiModelProperty("审核时间")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date auditTime;
|
||||
@ApiModelProperty("审核备注(驳回原因)")
|
||||
private String auditRemark;
|
||||
|
||||
@ApiModelProperty("下发状态:1-未下发 2-已下发")
|
||||
private Integer issueStatus;
|
||||
@ApiModelProperty("下发状态")
|
||||
private String issueStatusStr;
|
||||
@ApiModelProperty("下发人ID")
|
||||
private Long issueBy;
|
||||
@ApiModelProperty("下发人姓名")
|
||||
private String issueByName;
|
||||
@ApiModelProperty("下发时间")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date issueTime;
|
||||
|
||||
@ApiModelProperty("申报总数量(明细申报数量之和)")
|
||||
private BigDecimal declareTotalQuantity;
|
||||
|
||||
@ApiModelProperty("发票号(列表展示用,明细发票号逗号拼接;详情请看明细)")
|
||||
private String invoiceNo;
|
||||
|
||||
@ApiModelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ApiModelProperty("组织ID")
|
||||
private Long organizationId;
|
||||
@ApiModelProperty("组织名称")
|
||||
private String organizationName;
|
||||
@ApiModelProperty("一级组织ID")
|
||||
private Long topOrganizationId;
|
||||
|
||||
@ApiModelProperty("商品明细")
|
||||
private List<OutEntrustOrderDetailPO> materialDetailList;
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
package com.mhd.oms.domain.outEntrustOrder.repository.todo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.mhd.common.core.web.domain.BaseVOEntity;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 出仓委托单对象 oms_out_entrust_order
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-09-02
|
||||
*/
|
||||
|
||||
@Data
|
||||
public class OutEntrustOrderDO extends BaseVOEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("编号")
|
||||
private Long id;
|
||||
@ApiModelProperty("编号集合")
|
||||
private List<Long> idList;
|
||||
|
||||
@ApiModelProperty(name = "组织ID集合")
|
||||
private List<Long> organizationIdList;
|
||||
|
||||
@ApiModelProperty(name = "权限菜单ID")
|
||||
private Long permissionMenuId;
|
||||
|
||||
@ApiModelProperty("委托单号")
|
||||
private String entrustNumber;
|
||||
|
||||
@ApiModelProperty("出库仓库ID")
|
||||
private Long warehouseId;
|
||||
@ApiModelProperty("仓库编码")
|
||||
private String warehouseCode;
|
||||
@ApiModelProperty("仓库名称")
|
||||
private String warehouseName;
|
||||
|
||||
@ApiModelProperty("客户ID(货主)")
|
||||
private Long shipperId;
|
||||
@ApiModelProperty("客户编码")
|
||||
private String shipperCode;
|
||||
@ApiModelProperty("客户名称")
|
||||
private String shipperName;
|
||||
|
||||
@ApiModelProperty("业务员ID")
|
||||
private String salesmanId;
|
||||
@ApiModelProperty("业务员名称")
|
||||
private String salesmanName;
|
||||
|
||||
@ApiModelProperty("订单类型编码(字典outDocumentType)")
|
||||
private String orderTypeCode;
|
||||
@ApiModelProperty("订单类型名称")
|
||||
private String orderTypeName;
|
||||
|
||||
@ApiModelProperty("结算币种")
|
||||
private String settlementCurrency;
|
||||
|
||||
@ApiModelProperty("优先级别编码")
|
||||
private String priorityLevelCode;
|
||||
@ApiModelProperty("优先级别名称")
|
||||
private String priorityLevelName;
|
||||
|
||||
@ApiModelProperty("是否直接出库: 1-是 2-否")
|
||||
private Integer directWarehouse;
|
||||
|
||||
@ApiModelProperty("是否复核: 1-是 2-否")
|
||||
private Integer review;
|
||||
|
||||
@ApiModelProperty("预计出货时间")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expectTime;
|
||||
|
||||
@ApiModelProperty("出仓日期")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date outboundDate;
|
||||
|
||||
@ApiModelProperty("orderDate")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date orderDate;
|
||||
|
||||
@ApiModelProperty("完成时间")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date completeTime;
|
||||
|
||||
@ApiModelProperty("To(收货人)")
|
||||
private String toReceiver;
|
||||
|
||||
@ApiModelProperty("联系人")
|
||||
private String contactPerson;
|
||||
|
||||
@ApiModelProperty("车型及车牌")
|
||||
private String vehicleModelPlate;
|
||||
|
||||
@ApiModelProperty("委托编号(客户委托单号)")
|
||||
private String entrustNo;
|
||||
|
||||
@ApiModelProperty("Tel电话")
|
||||
private String tel;
|
||||
|
||||
@ApiModelProperty("Fax传真")
|
||||
private String fax;
|
||||
|
||||
@ApiModelProperty("司机签名")
|
||||
private String driverSign;
|
||||
|
||||
@ApiModelProperty("承运商编码")
|
||||
private String carrier;
|
||||
@ApiModelProperty("承运商名称")
|
||||
private String carrierName;
|
||||
|
||||
@ApiModelProperty("柜号")
|
||||
private String containerNo;
|
||||
@ApiModelProperty("柜")
|
||||
private String containerNoName;
|
||||
|
||||
@ApiModelProperty("运输方式编码")
|
||||
private String transportModeCode;
|
||||
@ApiModelProperty("运输方式名称")
|
||||
private String transportModeName;
|
||||
|
||||
@ApiModelProperty("监管方式编码")
|
||||
private String supervisionModeCode;
|
||||
@ApiModelProperty("监管方式名称")
|
||||
private String supervisionModeName;
|
||||
|
||||
@ApiModelProperty("申报地关区")
|
||||
private String declareCustoms;
|
||||
|
||||
@ApiModelProperty("进出境关别")
|
||||
private String entryExitCustoms;
|
||||
|
||||
@ApiModelProperty("起运/运抵国(地区)")
|
||||
private String departureArrivalCountry;
|
||||
|
||||
@ApiModelProperty("海关是否查验货物")
|
||||
private String customsInspectionFlag;
|
||||
|
||||
@ApiModelProperty("是否需要报关(预留,页面隐藏)")
|
||||
private String needDeclareFlag;
|
||||
@ApiModelProperty("是否需要运输(预留,页面隐藏)")
|
||||
private String needTransportFlag;
|
||||
|
||||
@ApiModelProperty("生产商")
|
||||
private String manufacturer;
|
||||
|
||||
@ApiModelProperty("报关员姓名及联系方式")
|
||||
private String customsDeclarerInfo;
|
||||
|
||||
@ApiModelProperty("送货地址")
|
||||
private String deliveryAddr;
|
||||
|
||||
@ApiModelProperty("附件url")
|
||||
private String annexUrl;
|
||||
|
||||
@ApiModelProperty("订单状态:1-待审核 2-已审核 3-已驳回 4-进行中 5-已入库")
|
||||
private Integer orderStatus;
|
||||
|
||||
@ApiModelProperty("审核人ID")
|
||||
private Long auditBy;
|
||||
@ApiModelProperty("审核人姓名")
|
||||
private String auditByName;
|
||||
@ApiModelProperty("审核时间")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date auditTime;
|
||||
@ApiModelProperty("审核备注(驳回原因)")
|
||||
private String auditRemark;
|
||||
|
||||
@ApiModelProperty("下发状态:1-未下发 2-已下发")
|
||||
private Integer issueStatus;
|
||||
@ApiModelProperty("下发人ID")
|
||||
private Long issueBy;
|
||||
@ApiModelProperty("下发人姓名")
|
||||
private String issueByName;
|
||||
@ApiModelProperty("下发时间")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date issueTime;
|
||||
|
||||
@ApiModelProperty("申报总数量(明细申报数量之和)")
|
||||
private BigDecimal declareTotalQuantity;
|
||||
|
||||
@ApiModelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ApiModelProperty("组织ID")
|
||||
private Long organizationId;
|
||||
@ApiModelProperty("组织名称")
|
||||
private String organizationName;
|
||||
@ApiModelProperty("一级组织ID")
|
||||
private Long topOrganizationId;
|
||||
|
||||
@ApiModelProperty("查询-创建开始时间(年月日yyyy-MM-dd)")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||
private Date beginCreateTime;
|
||||
@ApiModelProperty("查询-创建结束时间(年月日yyyy-MM-dd,含当天)")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||
private Date endCreateTime;
|
||||
|
||||
@ApiModelProperty("查询-发票号(模糊匹配明细发票号)")
|
||||
private String invoiceNo;
|
||||
|
||||
@ApiModelProperty("商品明细")
|
||||
private List<OutEntrustOrderDetailDO> materialDetailList;
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package com.mhd.oms.domain.outEntrustOrder.repository.todo;
|
||||
|
||||
import com.mhd.common.core.web.domain.BaseVOEntity;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 出仓委托单明细对象 oms_out_entrust_order_detail
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-09-02
|
||||
*/
|
||||
|
||||
@Data
|
||||
public class OutEntrustOrderDetailDO extends BaseVOEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("编号")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty("委托单号(关联主表)")
|
||||
private String entrustNumber;
|
||||
|
||||
@ApiModelProperty("物料基础信息ID")
|
||||
private Long materialBaseInfoId;
|
||||
@ApiModelProperty("商品编码")
|
||||
private String materialCode;
|
||||
@ApiModelProperty("商品名称(物料档案名)")
|
||||
private String materialName;
|
||||
@ApiModelProperty("商品条形码")
|
||||
private String barCode;
|
||||
|
||||
@ApiModelProperty("商品名称(委托单品名)")
|
||||
private String commodityName;
|
||||
@ApiModelProperty("商品编号")
|
||||
private String commodityNo;
|
||||
@ApiModelProperty("料号")
|
||||
private String materialNo;
|
||||
@ApiModelProperty("规格型号")
|
||||
private String specificationModel;
|
||||
@ApiModelProperty("SKU码")
|
||||
private String skucode;
|
||||
@ApiModelProperty("发票号")
|
||||
private String invoiceNo;
|
||||
|
||||
@ApiModelProperty("申报单位")
|
||||
private String declareUnit;
|
||||
@ApiModelProperty("申报数量")
|
||||
private BigDecimal declareQuantity;
|
||||
@ApiModelProperty("单价")
|
||||
private BigDecimal unitPrice;
|
||||
|
||||
@ApiModelProperty("单位")
|
||||
private String unit;
|
||||
@ApiModelProperty("升")
|
||||
private String liter;
|
||||
@ApiModelProperty("尺寸")
|
||||
private String size;
|
||||
@ApiModelProperty("箱数")
|
||||
private BigDecimal caseCount;
|
||||
@ApiModelProperty("件数")
|
||||
private BigDecimal pieceCount;
|
||||
@ApiModelProperty("出库数量")
|
||||
private BigDecimal outboundQuantity;
|
||||
@ApiModelProperty("总净重(千克)")
|
||||
private BigDecimal totalNetWeight;
|
||||
@ApiModelProperty("总毛重(千克)")
|
||||
private BigDecimal totalGrossWeight;
|
||||
@ApiModelProperty("总体积(立方米)")
|
||||
private BigDecimal totalVolume;
|
||||
@ApiModelProperty("总面积")
|
||||
private BigDecimal totalArea;
|
||||
@ApiModelProperty("总金额")
|
||||
private BigDecimal totalAmount;
|
||||
@ApiModelProperty("币制")
|
||||
private String currency;
|
||||
@ApiModelProperty("批次")
|
||||
private String batchRefNo;
|
||||
@ApiModelProperty("单据参考号")
|
||||
private String sheetRefNo;
|
||||
@ApiModelProperty("原产国")
|
||||
private String countryOfOrigin;
|
||||
@ApiModelProperty("箱/托盘号")
|
||||
private String boxPalletNo;
|
||||
|
||||
@ApiModelProperty("备注")
|
||||
private String remark;
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
package com.mhd.oms.domain.outEntrustOrder.service;
|
||||
|
||||
import com.mhd.common.core.exception.ServiceException;
|
||||
import com.mhd.common.core.utils.OrderSequence;
|
||||
import com.mhd.oms.constant.OutEntrustOrderConstant;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.facade.IOutEntrustOrderDetailService;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.facade.IOutEntrustOrderService;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.po.OutEntrustOrderDetailPO;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.po.OutEntrustOrderPO;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDO;
|
||||
import com.mhd.oms.domain.outEntrustOrder.repository.todo.OutEntrustOrderDetailDO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 出仓委托单
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-09-02
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class OutEntrustOrderDomainService {
|
||||
|
||||
@Autowired
|
||||
private IOutEntrustOrderService outEntrustOrderService;
|
||||
|
||||
@Autowired
|
||||
private IOutEntrustOrderDetailService outEntrustOrderDetailService;
|
||||
|
||||
/**
|
||||
* 分页查询出仓委托单列表
|
||||
*/
|
||||
public List<OutEntrustOrderPO> queryList(OutEntrustOrderDO outEntrustOrderDO) {
|
||||
return outEntrustOrderService.queryList(outEntrustOrderDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增出仓委托单(主单+明细)
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insert(OutEntrustOrderDO outEntrustOrderDO) {
|
||||
//生成委托单号,初始状态:待审核、未下发;新单无审核信息
|
||||
outEntrustOrderDO.setEntrustNumber(OrderSequence.getOrderCode(OutEntrustOrderConstant.OUT_ENTRUST_NO_PREFIX));
|
||||
outEntrustOrderDO.setOrderStatus(OutEntrustOrderConstant.ORDER_STATUS_PENDING_AUDIT);
|
||||
outEntrustOrderDO.setIssueStatus(OutEntrustOrderConstant.ISSUE_STATUS_1);
|
||||
outEntrustOrderDO.setAuditRemark(null);
|
||||
outEntrustOrderService.insert(outEntrustOrderDO);
|
||||
outEntrustOrderDetailService.batchInsert(outEntrustOrderDO.getEntrustNumber(), buildDetailList(outEntrustOrderDO));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改出仓委托单(仅已创建且未下发可修改,明细全量替换)
|
||||
*/
|
||||
/**
|
||||
* 修改出仓委托单(仅待审核/已驳回且未下发可修改,明细全量替换)
|
||||
* 已驳回单重新编辑后状态重置为待审核,待重新审核
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean update(OutEntrustOrderDO outEntrustOrderDO) {
|
||||
OutEntrustOrderPO orderPO = checkEditable(outEntrustOrderDO.getId());
|
||||
//已驳回 → 编辑后回到待审核;待审核 → 保持不变(置null,updateById跳过该列)
|
||||
outEntrustOrderDO.setOrderStatus(OutEntrustOrderConstant.ORDER_STATUS_REJECTED.equals(orderPO.getOrderStatus())
|
||||
? OutEntrustOrderConstant.ORDER_STATUS_PENDING_AUDIT : null);
|
||||
outEntrustOrderDO.setEntrustNumber(orderPO.getEntrustNumber());
|
||||
outEntrustOrderService.update(outEntrustOrderDO);
|
||||
//明细全量替换:逻辑删除旧明细后重新保存
|
||||
outEntrustOrderDetailService.deleteByEntrustNumber(outEntrustOrderDO.getEntrustNumber());
|
||||
outEntrustOrderDetailService.batchInsert(outEntrustOrderDO.getEntrustNumber(), buildDetailList(outEntrustOrderDO));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除出仓委托单(仅待审核/已驳回且未下发可删除)
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean delete(Long[] ids) {
|
||||
List<OutEntrustOrderPO> orderList = outEntrustOrderService.getInfoByIds(java.util.Arrays.stream(ids).collect(Collectors.toList()));
|
||||
List<String> entrustNumbers = orderList.stream().map(OutEntrustOrderPO::getEntrustNumber).collect(Collectors.toList());
|
||||
outEntrustOrderService.delete(ids);
|
||||
outEntrustOrderDetailService.deleteByEntrustNumbers(entrustNumbers);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取出仓委托单详情(含明细)
|
||||
*/
|
||||
public OutEntrustOrderPO getInfo(Long id) {
|
||||
OutEntrustOrderPO outEntrustOrderPO = outEntrustOrderService.getInfo(id);
|
||||
List<OutEntrustOrderDetailPO> detailList = outEntrustOrderDetailService.queryByEntrustNumber(outEntrustOrderPO.getEntrustNumber());
|
||||
outEntrustOrderPO.setMaterialDetailList(detailList);
|
||||
return outEntrustOrderPO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量审核:待审核/已驳回 → 已审核(2)或已驳回(3)
|
||||
*/
|
||||
public Boolean audit(List<Long> ids, Integer targetStatus, Long auditBy, String auditByName, String auditRemark) {
|
||||
return outEntrustOrderService.audit(ids, targetStatus, auditBy, auditByName, auditRemark);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量下发:订单状态保持已审核,下发状态置为已下发(WMS侧单据由ApplicationService推送)
|
||||
*/
|
||||
public Boolean issue(List<Long> ids, Long issueBy, String issueByName) {
|
||||
return outEntrustOrderService.issue(ids, issueBy, issueByName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成:已审核(已下发) → 已入库(OMS人工推进)
|
||||
*/
|
||||
public Boolean finish(List<Long> ids, Long updateBy, String updateByName) {
|
||||
return outEntrustOrderService.finish(ids, updateBy, updateByName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据委托单号查询明细
|
||||
*/
|
||||
public List<OutEntrustOrderDetailPO> getDetailByEntrustNumber(String entrustNumber) {
|
||||
return outEntrustOrderDetailService.queryByEntrustNumber(entrustNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据委托单号集合查询明细
|
||||
*/
|
||||
public List<OutEntrustOrderDetailPO> getDetailByEntrustNumbers(List<String> entrustNumbers) {
|
||||
return outEntrustOrderDetailService.queryByEntrustNumbers(entrustNumbers);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据编号集合查询出仓委托单列表
|
||||
*/
|
||||
public List<OutEntrustOrderPO> queryByIds(List<Long> ids) {
|
||||
return outEntrustOrderService.getInfoByIds(ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 明细补充审计信息(取主单的创建/更新人)
|
||||
*/
|
||||
private List<OutEntrustOrderDetailDO> buildDetailList(OutEntrustOrderDO outEntrustOrderDO) {
|
||||
List<OutEntrustOrderDetailDO> detailDOList = outEntrustOrderDO.getMaterialDetailList();
|
||||
for (OutEntrustOrderDetailDO detailDO : detailDOList) {
|
||||
detailDO.setCreateBy(outEntrustOrderDO.getCreateBy());
|
||||
detailDO.setCreateByName(outEntrustOrderDO.getCreateByName());
|
||||
detailDO.setCreateTime(outEntrustOrderDO.getCreateTime());
|
||||
detailDO.setUpdateBy(outEntrustOrderDO.getUpdateBy());
|
||||
detailDO.setUpdateByName(outEntrustOrderDO.getUpdateByName());
|
||||
detailDO.setUpdateTime(outEntrustOrderDO.getUpdateTime());
|
||||
detailDO.setDelFlag(1);
|
||||
}
|
||||
return detailDOList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验委托单是否可编辑/删除:仅待审核/已驳回 且未下发
|
||||
*
|
||||
* @return 委托单信息(供调用方复用当前状态/委托单号)
|
||||
*/
|
||||
private OutEntrustOrderPO checkEditable(Long id) {
|
||||
OutEntrustOrderPO outEntrustOrderPO = outEntrustOrderService.getInfo(id);
|
||||
boolean editableStatus = OutEntrustOrderConstant.ORDER_STATUS_PENDING_AUDIT.equals(outEntrustOrderPO.getOrderStatus())
|
||||
|| OutEntrustOrderConstant.ORDER_STATUS_REJECTED.equals(outEntrustOrderPO.getOrderStatus());
|
||||
if (!editableStatus) {
|
||||
throw new ServiceException("已审核/进行中/已入库的委托单不允许修改");
|
||||
}
|
||||
if (!OutEntrustOrderConstant.ISSUE_STATUS_1.equals(outEntrustOrderPO.getIssueStatus())) {
|
||||
throw new ServiceException("已下发的委托单不允许修改");
|
||||
}
|
||||
return outEntrustOrderPO;
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.mhd.oms.domain.outboundRank.repository.facade;
|
||||
|
||||
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundDetailRankTotalPO;
|
||||
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundLineRankPO;
|
||||
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundMaterialRankPO;
|
||||
import com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 出库排名统计Service接口
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
public interface IExecutionOutboundRankService {
|
||||
|
||||
/**
|
||||
* 商品出库排名列表(货品聚合)
|
||||
*/
|
||||
List<ExecutionOutboundMaterialRankPO> queryMaterialRankList(ExecutionOutboundRankDO executionOutboundRankDO);
|
||||
|
||||
/**
|
||||
* 商品出库排名合计(不分页)
|
||||
*/
|
||||
ExecutionOutboundDetailRankTotalPO queryMaterialRankTotal(ExecutionOutboundRankDO executionOutboundRankDO);
|
||||
|
||||
/**
|
||||
* 线路出库排名列表(线路×货品聚合)
|
||||
*/
|
||||
List<ExecutionOutboundLineRankPO> queryLineRankList(ExecutionOutboundRankDO executionOutboundRankDO);
|
||||
|
||||
/**
|
||||
* 线路出库排名合计(不分页)
|
||||
*/
|
||||
ExecutionOutboundDetailRankTotalPO queryLineRankTotal(ExecutionOutboundRankDO executionOutboundRankDO);
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.mhd.oms.domain.outboundRank.repository.mapper;
|
||||
|
||||
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundDetailRankTotalPO;
|
||||
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundLineRankPO;
|
||||
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundMaterialRankPO;
|
||||
import com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 出库排名统计Mapper接口
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
public interface ExecutionOutboundRankMapper {
|
||||
|
||||
/**
|
||||
* 商品出库排名列表(一行=一个货品,跨单汇总,按实际出库量降序)
|
||||
*/
|
||||
List<ExecutionOutboundMaterialRankPO> queryMaterialRankList(ExecutionOutboundRankDO executionOutboundRankDO);
|
||||
|
||||
/**
|
||||
* 商品出库排名合计(不分页,当前筛选条件全量合计)
|
||||
*/
|
||||
ExecutionOutboundDetailRankTotalPO queryMaterialRankTotal(ExecutionOutboundRankDO executionOutboundRankDO);
|
||||
|
||||
/**
|
||||
* 线路出库排名列表(一行=线路×货品,按实际出库量降序,仅统计带线路的单)
|
||||
*/
|
||||
List<ExecutionOutboundLineRankPO> queryLineRankList(ExecutionOutboundRankDO executionOutboundRankDO);
|
||||
|
||||
/**
|
||||
* 线路出库排名合计(不分页,当前筛选条件全量合计)
|
||||
*/
|
||||
ExecutionOutboundDetailRankTotalPO queryLineRankTotal(ExecutionOutboundRankDO executionOutboundRankDO);
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.mhd.oms.domain.outboundRank.repository.persistence;
|
||||
|
||||
import com.mhd.oms.domain.outboundRank.repository.facade.IExecutionOutboundRankService;
|
||||
import com.mhd.oms.domain.outboundRank.repository.mapper.ExecutionOutboundRankMapper;
|
||||
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundDetailRankTotalPO;
|
||||
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundLineRankPO;
|
||||
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundMaterialRankPO;
|
||||
import com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 出库排名统计Service业务层处理
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Service
|
||||
public class ExecutionOutboundRankImpl implements IExecutionOutboundRankService {
|
||||
|
||||
@Autowired
|
||||
private ExecutionOutboundRankMapper executionOutboundRankMapper;
|
||||
|
||||
@Override
|
||||
public List<ExecutionOutboundMaterialRankPO> queryMaterialRankList(ExecutionOutboundRankDO executionOutboundRankDO) {
|
||||
return executionOutboundRankMapper.queryMaterialRankList(executionOutboundRankDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutionOutboundDetailRankTotalPO queryMaterialRankTotal(ExecutionOutboundRankDO executionOutboundRankDO) {
|
||||
return executionOutboundRankMapper.queryMaterialRankTotal(executionOutboundRankDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ExecutionOutboundLineRankPO> queryLineRankList(ExecutionOutboundRankDO executionOutboundRankDO) {
|
||||
return executionOutboundRankMapper.queryLineRankList(executionOutboundRankDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutionOutboundDetailRankTotalPO queryLineRankTotal(ExecutionOutboundRankDO executionOutboundRankDO) {
|
||||
return executionOutboundRankMapper.queryLineRankTotal(executionOutboundRankDO);
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.mhd.oms.domain.outboundRank.repository.po;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 出库排名统计合计PO
|
||||
*
|
||||
* <p>当前筛选条件下不分页的全量合计,随分页列表一起返回
|
||||
* (totalPieceCount/totalGrossWeightTon/totalVolume/totalOrderCount/totalLineCount)</p>
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "出库排名统计合计对象", description = "出库排名统计合计响应对象")
|
||||
public class ExecutionOutboundDetailRankTotalPO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("件数合计")
|
||||
private BigDecimal totalPieceCount;
|
||||
|
||||
@ApiModelProperty("毛重(吨)合计")
|
||||
private BigDecimal totalGrossWeightTon;
|
||||
|
||||
@ApiModelProperty("体积(CBM)合计")
|
||||
private BigDecimal totalVolume;
|
||||
|
||||
@ApiModelProperty("仓单数合计(去重出库单数)")
|
||||
private Long totalOrderCount;
|
||||
|
||||
@ApiModelProperty("线路数合计(去重线路数,线路排名使用)")
|
||||
private Long totalLineCount;
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.mhd.oms.domain.outboundRank.repository.po;
|
||||
|
||||
import com.mhd.common.core.annotation.Excel;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 线路出库排名PO(一行 = 线路×货品,日期区间内汇总)
|
||||
*
|
||||
* <p>按线路+物料编码合并:多张出库单合并为一行,单据级字段(种类/仓单编号/入库单号/客户编号/收货人/出仓日期)不提供;
|
||||
* 仅统计带线路的出库单(line_code 为 2026-08-20 新增冗余字段,历史单为空,不参与线路排名);
|
||||
* 件数 = SUM(CHECK_QUANTITY) 复核确认的实际出库量(与入库排名的实际上架量对称);
|
||||
* 退保总额为占位字段,后端暂无对应数据来源,恒为 null</p>
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "线路出库排名对象", description = "线路出库排名响应对象")
|
||||
public class ExecutionOutboundLineRankPO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("线路编码")
|
||||
@Excel(name = "线路编码")
|
||||
private String lineCode;
|
||||
|
||||
@ApiModelProperty("线路")
|
||||
@Excel(name = "线路")
|
||||
private String lineName;
|
||||
|
||||
@ApiModelProperty("商品货号")
|
||||
@Excel(name = "商品货号")
|
||||
private String materialCode;
|
||||
|
||||
@ApiModelProperty("货名")
|
||||
@Excel(name = "货名")
|
||||
private String materialName;
|
||||
|
||||
@ApiModelProperty("件数(复核确认的实际出库量合计,与入库排名的实际上架量对称)")
|
||||
@Excel(name = "件数")
|
||||
private BigDecimal pieceCount;
|
||||
|
||||
@ApiModelProperty("毛重(吨)")
|
||||
@Excel(name = "毛重(吨)")
|
||||
private BigDecimal grossWeightTon;
|
||||
|
||||
@ApiModelProperty("体积(CBM)")
|
||||
@Excel(name = "体积(CBM)")
|
||||
private BigDecimal totalVolume;
|
||||
|
||||
@ApiModelProperty("退保总额(占位字段,后端暂无数据来源,恒为 null)")
|
||||
@Excel(name = "退保总额")
|
||||
private BigDecimal depositRefundAmount;
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.mhd.oms.domain.outboundRank.repository.po;
|
||||
|
||||
import com.mhd.common.core.annotation.Excel;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 商品出库排名PO(一行 = 一个货品,日期区间内跨出库单汇总)
|
||||
*
|
||||
* <p>按物料编码合并:一个货品会走多条线路/多张单,单据级字段(种类/仓单编号/入库单号/客户编号/收货人/出仓日期)与
|
||||
* 线路字段均无单值可返,不提供;件数 = SUM(CHECK_QUANTITY) 复核确认的实际出库量(与入库排名的实际上架量对称);
|
||||
* 退保总额为占位字段,后端暂无对应数据来源,恒为 null</p>
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "商品出库排名对象", description = "商品出库排名响应对象")
|
||||
public class ExecutionOutboundMaterialRankPO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("商品货号")
|
||||
@Excel(name = "商品货号")
|
||||
private String materialCode;
|
||||
|
||||
@ApiModelProperty("货名")
|
||||
@Excel(name = "货名")
|
||||
private String materialName;
|
||||
|
||||
@ApiModelProperty("件数(复核确认的实际出库量合计,与入库排名的实际上架量对称)")
|
||||
@Excel(name = "件数")
|
||||
private BigDecimal pieceCount;
|
||||
|
||||
@ApiModelProperty("毛重(吨)")
|
||||
@Excel(name = "毛重(吨)")
|
||||
private BigDecimal grossWeightTon;
|
||||
|
||||
@ApiModelProperty("体积(CBM)")
|
||||
@Excel(name = "体积(CBM)")
|
||||
private BigDecimal totalVolume;
|
||||
|
||||
@ApiModelProperty("退保总额(占位字段,后端暂无数据来源,恒为 null)")
|
||||
@Excel(name = "退保总额")
|
||||
private BigDecimal depositRefundAmount;
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package com.mhd.oms.domain.outboundRank.repository.todo;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 出库排名统计(明细)查询DO
|
||||
*
|
||||
* <p>topOrganizationId 由服务端按登录用户注入,前端不传</p>
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Data
|
||||
public class ExecutionOutboundRankDO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("出库日期-开始 yyyy-MM-dd(必填)")
|
||||
private String createTimeStart;
|
||||
|
||||
@ApiModelProperty("出库日期-结束 yyyy-MM-dd(必填)")
|
||||
private String createTimeEnd;
|
||||
|
||||
@ApiModelProperty("仓单编号/出库单号(模糊)")
|
||||
private String outOrderNumber;
|
||||
|
||||
@ApiModelProperty("入库单号(模糊)")
|
||||
private String inOrderNumber;
|
||||
|
||||
@ApiModelProperty("商品货号(模糊)")
|
||||
private String materialCode;
|
||||
|
||||
@ApiModelProperty("货名/商品名称(模糊)")
|
||||
private String materialName;
|
||||
|
||||
@ApiModelProperty("线路编码(模糊)")
|
||||
private String lineCode;
|
||||
|
||||
@ApiModelProperty("线路名称(模糊)")
|
||||
private String lineName;
|
||||
|
||||
@ApiModelProperty("仓库名称(模糊)")
|
||||
private String warehouseName;
|
||||
|
||||
@ApiModelProperty("客户名称(模糊)")
|
||||
private String customerName;
|
||||
|
||||
@ApiModelProperty("货主名称(模糊)")
|
||||
private String shipperName;
|
||||
|
||||
@ApiModelProperty("一级组织表ID(登录用户隔离,非1时注入)")
|
||||
private Long topOrganizationId;
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package com.mhd.oms.domain.outboundRank.repository.todo;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 出库排名统计(明细)入参DTO
|
||||
*
|
||||
* <p>按日期区间筛选出库明细列表,一行 = 仓单×货品(件数/毛重/体积按行汇总,件数降序)。
|
||||
* OMS 库出库单状态不流转(无"已出库"数据),数据源跨库取 WMS 库出库表,与 WMS 端 /outboundRankApi 同口径</p>
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Data
|
||||
public class ExecutionOutboundRankDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("出库日期-开始 yyyy-MM-dd(必填,按出库单创建时间筛选)")
|
||||
private String createTimeStart;
|
||||
|
||||
@ApiModelProperty("出库日期-结束 yyyy-MM-dd(必填,按出库单创建时间筛选)")
|
||||
private String createTimeEnd;
|
||||
|
||||
@ApiModelProperty("仓单编号/出库单号(模糊)")
|
||||
private String outOrderNumber;
|
||||
|
||||
@ApiModelProperty("入库单号(模糊)")
|
||||
private String inOrderNumber;
|
||||
|
||||
@ApiModelProperty("商品货号(模糊)")
|
||||
private String materialCode;
|
||||
|
||||
@ApiModelProperty("货名/商品名称(模糊)")
|
||||
private String materialName;
|
||||
|
||||
@ApiModelProperty("线路编码(模糊)")
|
||||
private String lineCode;
|
||||
|
||||
@ApiModelProperty("线路名称(模糊)")
|
||||
private String lineName;
|
||||
|
||||
@ApiModelProperty("仓库名称(模糊)")
|
||||
private String warehouseName;
|
||||
|
||||
@ApiModelProperty("客户名称(模糊)")
|
||||
private String customerName;
|
||||
|
||||
@ApiModelProperty("货主名称(模糊)")
|
||||
private String shipperName;
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package com.mhd.oms.domain.outboundRank.service;
|
||||
|
||||
import com.mhd.common.core.exception.ServiceException;
|
||||
import com.mhd.common.core.utils.StringUtils;
|
||||
import com.mhd.common.security.utils.SecurityUtils;
|
||||
import com.mhd.system.api.model.LoginUser;
|
||||
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundDetailRankTotalPO;
|
||||
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundLineRankPO;
|
||||
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundMaterialRankPO;
|
||||
import com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 出库排名统计应用服务
|
||||
*
|
||||
* <p>负责登录用户组织隔离注入、日期区间校验,查询逻辑在 ExecutionOutboundRankDomainService → ExecutionOutboundRankMapper.xml(跨库取 WMS 出库数据)</p>
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class ExecutionOutboundRankApplicationService {
|
||||
|
||||
@Autowired
|
||||
private ExecutionOutboundRankDomainService executionOutboundRankDomainService;
|
||||
|
||||
/**
|
||||
* 分页查询商品出库排名列表(货品聚合)
|
||||
*/
|
||||
public List<ExecutionOutboundMaterialRankPO> queryMaterialRankList(ExecutionOutboundRankDO executionOutboundRankDO) {
|
||||
fillLoginContext(executionOutboundRankDO);
|
||||
return executionOutboundRankDomainService.queryMaterialRankList(executionOutboundRankDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品出库排名合计(当前筛选条件全量,不分页)
|
||||
*/
|
||||
public ExecutionOutboundDetailRankTotalPO queryMaterialRankTotal(ExecutionOutboundRankDO executionOutboundRankDO) {
|
||||
return executionOutboundRankDomainService.queryMaterialRankTotal(executionOutboundRankDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出商品出库排名(不分页全量)
|
||||
*/
|
||||
public List<ExecutionOutboundMaterialRankPO> exportMaterialRankList(ExecutionOutboundRankDO executionOutboundRankDO) {
|
||||
fillLoginContext(executionOutboundRankDO);
|
||||
return executionOutboundRankDomainService.queryMaterialRankList(executionOutboundRankDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询线路出库排名列表(线路×货品聚合)
|
||||
*/
|
||||
public List<ExecutionOutboundLineRankPO> queryLineRankList(ExecutionOutboundRankDO executionOutboundRankDO) {
|
||||
fillLoginContext(executionOutboundRankDO);
|
||||
return executionOutboundRankDomainService.queryLineRankList(executionOutboundRankDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 线路出库排名合计(当前筛选条件全量,不分页)
|
||||
*/
|
||||
public ExecutionOutboundDetailRankTotalPO queryLineRankTotal(ExecutionOutboundRankDO executionOutboundRankDO) {
|
||||
return executionOutboundRankDomainService.queryLineRankTotal(executionOutboundRankDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出线路出库排名(不分页全量)
|
||||
*/
|
||||
public List<ExecutionOutboundLineRankPO> exportLineRankList(ExecutionOutboundRankDO executionOutboundRankDO) {
|
||||
fillLoginContext(executionOutboundRankDO);
|
||||
return executionOutboundRankDomainService.queryLineRankList(executionOutboundRankDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入登录用户组织隔离,并校验日期区间必填
|
||||
*/
|
||||
private void fillLoginContext(ExecutionOutboundRankDO executionOutboundRankDO) {
|
||||
if (StringUtils.isEmpty(executionOutboundRankDO.getCreateTimeStart()) || StringUtils.isEmpty(executionOutboundRankDO.getCreateTimeEnd())) {
|
||||
throw new ServiceException("请选择统计日期区间");
|
||||
}
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (loginUser != null && loginUser.getUserPo() != null) {
|
||||
Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId();
|
||||
if (topOrganizationId != null && topOrganizationId != 1) {
|
||||
executionOutboundRankDO.setTopOrganizationId(topOrganizationId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.mhd.oms.domain.outboundRank.service;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.mhd.common.core.exception.digitalLogisticsException.DigitalLogisticsException;
|
||||
import com.mhd.common.core.exception.digitalLogisticsException.UserError;
|
||||
import com.mhd.common.core.utils.IgnoreNullUtil;
|
||||
import com.mhd.common.core.utils.bean.BeanUtils;
|
||||
import com.mhd.common.security.utils.SecurityUtils;
|
||||
import com.mhd.system.api.model.LoginUser;
|
||||
import com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDO;
|
||||
import com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDTO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 出库排名统计Assembler
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Component
|
||||
public class ExecutionOutboundRankAssembler {
|
||||
|
||||
/**
|
||||
* 转换实体
|
||||
*/
|
||||
public ExecutionOutboundRankDO toDO(ExecutionOutboundRankDTO executionOutboundRankDTO) {
|
||||
ExecutionOutboundRankDO executionOutboundRankDO = new ExecutionOutboundRankDO();
|
||||
// 拷贝
|
||||
BeanUtils.copyProperties(executionOutboundRankDTO, executionOutboundRankDO, IgnoreNullUtil.getNullPropertyNames(executionOutboundRankDTO));
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (ObjectUtil.isNull(loginUser)) {
|
||||
throw new DigitalLogisticsException(UserError.TIMEOUT);
|
||||
}
|
||||
return executionOutboundRankDO;
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package com.mhd.oms.domain.outboundRank.service;
|
||||
|
||||
import com.mhd.oms.domain.outboundRank.repository.facade.IExecutionOutboundRankService;
|
||||
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundDetailRankTotalPO;
|
||||
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundLineRankPO;
|
||||
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundMaterialRankPO;
|
||||
import com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 出库排名统计领域服务
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class ExecutionOutboundRankDomainService {
|
||||
|
||||
@Autowired
|
||||
private IExecutionOutboundRankService executionOutboundRankService;
|
||||
|
||||
/**
|
||||
* 商品出库排名列表(货品聚合)
|
||||
*/
|
||||
public List<ExecutionOutboundMaterialRankPO> queryMaterialRankList(ExecutionOutboundRankDO executionOutboundRankDO) {
|
||||
return executionOutboundRankService.queryMaterialRankList(executionOutboundRankDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品出库排名合计(不分页)
|
||||
*/
|
||||
public ExecutionOutboundDetailRankTotalPO queryMaterialRankTotal(ExecutionOutboundRankDO executionOutboundRankDO) {
|
||||
return executionOutboundRankService.queryMaterialRankTotal(executionOutboundRankDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 线路出库排名列表(线路×货品聚合)
|
||||
*/
|
||||
public List<ExecutionOutboundLineRankPO> queryLineRankList(ExecutionOutboundRankDO executionOutboundRankDO) {
|
||||
return executionOutboundRankService.queryLineRankList(executionOutboundRankDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 线路出库排名合计(不分页)
|
||||
*/
|
||||
public ExecutionOutboundDetailRankTotalPO queryLineRankTotal(ExecutionOutboundRankDO executionOutboundRankDO) {
|
||||
return executionOutboundRankService.queryLineRankTotal(executionOutboundRankDO);
|
||||
}
|
||||
}
|
||||
+6
@@ -210,6 +210,12 @@ public class ReservationMaterialInventory extends BaseVOEntity {
|
||||
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date inventoryDate;
|
||||
|
||||
@ApiModelProperty("进货日期")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date purchaseDate;
|
||||
|
||||
@ApiModelProperty("ExtAttr3")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
|
||||
+6
@@ -2,6 +2,7 @@ package com.mhd.oms.domain.reservationMaterialInventory.repository.facade;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.entity.ReservationMaterialInventory;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.MaterialInventoryExpiryBatchPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryQueryListPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.todo.MaterialInventoryParamDO;
|
||||
@@ -113,4 +114,9 @@ public interface IReservationMaterialInventoryService extends IService<Reservati
|
||||
public void xgkc(MaterialInventoryOmsParamDO materialInventoryParamDO);
|
||||
|
||||
public void kctzjl(MaterialInventoryOmsParamDO materialInventoryParamDO,String adjustAction,LoginUser loginUser);
|
||||
|
||||
/**
|
||||
* 根据物料编号查询库存批次最近5条过期日期+批次号(入库业务单新增页面下拉回显)
|
||||
*/
|
||||
public List<MaterialInventoryExpiryBatchPO> selectRecentExpiryBatches(String materialCode, Long topOrganizationId);
|
||||
}
|
||||
+4
@@ -2,6 +2,7 @@ package com.mhd.oms.domain.reservationMaterialInventory.repository.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.entity.ReservationMaterialInventory;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.MaterialInventoryExpiryBatchPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.todo.MaterialBarCodePO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.todo.MaterialBaseInfo;
|
||||
@@ -36,4 +37,7 @@ public interface ReservationMaterialInventoryMapper extends BaseMapper<Reservati
|
||||
public MaterialBaseInfo getByInfoId(@Param("id") Long id);
|
||||
|
||||
public List<MaterialBarCodePO> getInfoByMaterialBaseInfoIds(@Param("id") Long id);
|
||||
|
||||
//入库业务单新增:根据物料编号查询库存批次去重后的过期日期+批次号(最近5条),供前端下拉回显
|
||||
public List<MaterialInventoryExpiryBatchPO> selectRecentExpiryBatches(@Param("materialCode") String materialCode, @Param("topOrganizationId") Long topOrganizationId);
|
||||
}
|
||||
+18
@@ -13,6 +13,7 @@ import com.mhd.oms.domain.reservationInventoryAdjustmentRecord.repository.mapper
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.entity.ReservationMaterialInventory;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.facade.IReservationMaterialInventoryService;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.mapper.ReservationMaterialInventoryMapper;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.MaterialInventoryExpiryBatchPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryQueryListPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.todo.MaterialBarCodePO;
|
||||
@@ -120,6 +121,14 @@ public class ReservationMaterialInventoryImpl extends ServiceImpl<ReservationMat
|
||||
return materialInventoryPO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据物料编号查询库存批次最近5条过期日期+批次号(入库业务单新增页面下拉回显)
|
||||
*/
|
||||
@Override
|
||||
public List<MaterialInventoryExpiryBatchPO> selectRecentExpiryBatches(String materialCode, Long topOrganizationId) {
|
||||
return materialInventoryMapper.selectRecentExpiryBatches(materialCode, topOrganizationId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean omsEdit(MaterialInventoryOmsParamDO materialInventoryOmsParamDO) {
|
||||
String inOrderNumber = materialInventoryOmsParamDO.getInOrderNumber();
|
||||
@@ -127,6 +136,9 @@ public class ReservationMaterialInventoryImpl extends ServiceImpl<ReservationMat
|
||||
String batchNumber = materialInventoryOmsParamDO.getBatchNumber();
|
||||
Long materialBaseInfoId = materialInventoryOmsParamDO.getMaterialBaseInfoId();
|
||||
Long warehouseId = materialInventoryOmsParamDO.getWarehouseId();
|
||||
// 进货日期参与行匹配:WMS 库存行按批次属性(含进货日期)分行后,OMS 同步按进货日期区分,避免不同进货日期误合并;
|
||||
// 为空时匹配 purchase_date 为空的行,存量行为不变
|
||||
Date purchaseDate = materialInventoryOmsParamDO.getPurchaseDate();
|
||||
LambdaQueryWrapper<ReservationMaterialInventory> param = new LambdaQueryWrapper<ReservationMaterialInventory>()
|
||||
// .eq(ReservationMaterialInventory::getInOrderNumber, inOrderNumber)
|
||||
//.eq(ReservationMaterialInventory::getLotNumber, lotNumber)
|
||||
@@ -148,6 +160,11 @@ public class ReservationMaterialInventoryImpl extends ServiceImpl<ReservationMat
|
||||
} else {
|
||||
param.isNull(ReservationMaterialInventory::getBatchNumber);
|
||||
}
|
||||
if (purchaseDate != null) {
|
||||
param.eq(ReservationMaterialInventory::getPurchaseDate, purchaseDate);
|
||||
} else {
|
||||
param.isNull(ReservationMaterialInventory::getPurchaseDate);
|
||||
}
|
||||
List<ReservationMaterialInventory> reservationMaterialInventories = materialInventoryMapper.selectList(param);
|
||||
if (reservationMaterialInventories != null && reservationMaterialInventories.size() > 0) {
|
||||
ReservationMaterialInventory reservationMaterialInventory = reservationMaterialInventories.get(0);
|
||||
@@ -542,6 +559,7 @@ public class ReservationMaterialInventoryImpl extends ServiceImpl<ReservationMat
|
||||
// materialInventory.setExtAttr4(materialInventoryParamDO.getExtAttr4());
|
||||
materialInventory.setExpiryDate(materialInventoryParamDO.getExpiryDate());
|
||||
// materialInventory.setInventoryDate(materialInventoryParamDO.getInventoryDate());
|
||||
materialInventory.setPurchaseDate(materialInventoryParamDO.getPurchaseDate());
|
||||
materialInventory.setBatchNumber(materialInventoryParamDO.getBatchNumber());
|
||||
String batchNo = materialInventoryParamDO.getBatchNo();
|
||||
if (batchNo != null) {
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.mhd.oms.domain.reservationMaterialInventory.repository.po;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 物料库存批次过期日期对象(入库业务单新增页面下拉回显)
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "物料库存批次过期日期", description = "物料库存批次过期日期响应对象")
|
||||
public class MaterialInventoryExpiryBatchPO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("过期日期 yyyy-MM-dd")
|
||||
private String expiryDate;
|
||||
|
||||
@ApiModelProperty("批次号")
|
||||
private String batchNumber;
|
||||
}
|
||||
+33
-11
@@ -174,34 +174,41 @@ public class ReservationMaterialInventoryPO extends MaterialBasePO {
|
||||
@Excel(name = "扩展字段2")
|
||||
private String extAttr2;
|
||||
|
||||
// 以下业务日期字段只保留年月日:导出复用 /list 接口,前端取 JSON 值直接进 Excel,去掉时分秒(与 WMS 库存查询一致)
|
||||
@ApiModelProperty("生产日期")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "生产日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "生产日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date productionDate;
|
||||
|
||||
@ApiModelProperty("过期日期")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "过期日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "过期日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date expiryDate;
|
||||
|
||||
@ApiModelProperty("存货日期")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date inventoryDate;
|
||||
|
||||
@ApiModelProperty("ExtAttr3")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@ApiModelProperty("进货日期")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "ExtAttr3", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date purchaseDate;
|
||||
|
||||
@ApiModelProperty("ExtAttr3")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "ExtAttr3", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date extAttr3;
|
||||
|
||||
@ApiModelProperty("ExtAttr4")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "ExtAttr4", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "ExtAttr4", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date extAttr4;
|
||||
|
||||
|
||||
@@ -225,4 +232,19 @@ public class ReservationMaterialInventoryPO extends MaterialBasePO {
|
||||
@ApiModelProperty("LOT编号")
|
||||
@Excel(name = "LOT编号")
|
||||
private String lotNumber;
|
||||
|
||||
// 入库时间(createTime)/更新时间(updateTime)继承自 BaseVOEntity,默认带时分秒;
|
||||
// 库存查询导出复用 /list 接口,前端取 JSON 值直接进 Excel,这里覆盖 getter 只保留年月日。
|
||||
// 不改 BaseVOEntity(全系统基类,其他模块时间列仍需时分秒),仅收窄本 PO 的序列化格式(与 WMS 库存查询一致)
|
||||
@Override
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
public Date getCreateTime() {
|
||||
return super.getCreateTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
public Date getUpdateTime() {
|
||||
return super.getUpdateTime();
|
||||
}
|
||||
}
|
||||
+15
@@ -217,6 +217,12 @@ public class ReservationMaterialInventoryDO extends MaterialBaseDO {
|
||||
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date inventoryDate;
|
||||
|
||||
@ApiModelProperty("进货日期")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date purchaseDate;
|
||||
|
||||
@ApiModelProperty("ExtAttr3")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@@ -253,9 +259,18 @@ public class ReservationMaterialInventoryDO extends MaterialBaseDO {
|
||||
private String createTimeEnd;
|
||||
private String expiryDateStr;
|
||||
private String productionDateStr;
|
||||
private String purchaseDateStr;
|
||||
private String expiryDateStart;
|
||||
private String expiryDateEnd;
|
||||
private String purchaseDateStart;
|
||||
private String purchaseDateEnd;
|
||||
|
||||
@ApiModelProperty("导出按到期日分组排序标识:仅 queryRule=wlExpiry 时生效,true-到期日倒序(日期近的在前、空值最后、0库存靠后);不传-保持原有排序")
|
||||
private Boolean orderByExpiryDate;
|
||||
|
||||
@ApiModelProperty("导出排除库存为0标识:true-排除库存数量汇总为0的数据(所有查询维度 queryRule 均生效);不传-不过滤")
|
||||
private Boolean excludeZeroInventoryQuantity;
|
||||
|
||||
@ApiModelProperty("导出标识:导出与查询共用 /list,true-本次为导出请求,启用导出融合排序(到期日倒序优先→同到期日按物料编码升序→0库存靠后→更新时间倒序);不传-保持原有排序")
|
||||
private Boolean isExport;
|
||||
}
|
||||
+2
@@ -193,5 +193,7 @@ public class ReservationMaterialInventoryDTO extends MaterialBaseDTO {
|
||||
private String createTimeEnd;
|
||||
private String expiryDate;
|
||||
private String productionDate;
|
||||
/** 进货日期(yyyy-MM-dd,单日精确查询) */
|
||||
private String purchaseDate;
|
||||
|
||||
}
|
||||
+17
@@ -8,6 +8,7 @@ import com.mhd.common.core.utils.bean.BeanUtils;
|
||||
import com.mhd.common.core.web.domain.AjaxResult;
|
||||
import com.mhd.common.security.utils.SecurityUtils;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.mapper.ReservationMaterialInventoryMapper;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.MaterialInventoryExpiryBatchPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryQueryListPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.todo.MaterialBarCodePO;
|
||||
@@ -185,6 +186,22 @@ public class ReservationMaterialInventoryApplicationService {
|
||||
return materialInventoryDomainService.getInventoryByStorageLocationIdAndMaterialBaseInfoId(storageLocationId, materialBaseInfoId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据物料编号查询库存批次最近5条过期日期+批次号(入库业务单新增页面下拉回显)
|
||||
* 与 queryList 一致:按登录用户一级组织(租户)过滤,一级组织(平台)不限制
|
||||
*/
|
||||
public List<MaterialInventoryExpiryBatchPO> selectRecentExpiryBatches(String materialCode) {
|
||||
Long topOrganizationId = null;
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (loginUser != null && loginUser.getUserPo() != null) {
|
||||
Long loginTopOrgId = loginUser.getUserPo().getTopOrganizationId();
|
||||
if (loginTopOrgId != null && loginTopOrgId != 1) {
|
||||
topOrganizationId = loginTopOrgId;
|
||||
}
|
||||
}
|
||||
return materialInventoryDomainService.selectRecentExpiryBatches(materialCode, topOrganizationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description:库存查询列表
|
||||
* @param materialInventoryQueryListDO
|
||||
|
||||
+8
@@ -3,6 +3,7 @@ package com.mhd.oms.domain.reservationMaterialInventory.service;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.entity.ReservationMaterialInventory;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.facade.IReservationMaterialInventoryService;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.MaterialInventoryExpiryBatchPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryQueryListPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.todo.ReservationMaterialInventoryDO;
|
||||
@@ -88,6 +89,13 @@ public class ReservationMaterialInventoryDomainService {
|
||||
return materialInventory.getInventoryQuantity();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据物料编号查询库存批次最近5条过期日期+批次号(入库业务单新增页面下拉回显)
|
||||
*/
|
||||
public List<MaterialInventoryExpiryBatchPO> selectRecentExpiryBatches(String materialCode, Long topOrganizationId) {
|
||||
return materialInventoryService.selectRecentExpiryBatches(materialCode, topOrganizationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description:库存查询列表
|
||||
* @param materialInventoryQueryListDO
|
||||
|
||||
+12
-8
@@ -6,6 +6,7 @@ import com.mhd.oms.domain.reservationStockInOrder.entity.ReservationStockInOrder
|
||||
import com.mhd.oms.domain.reservationStockInOrder.repository.po.ReservationStockInOrderPO;
|
||||
import com.mhd.oms.domain.reservationStockInOrder.repository.todo.ReservationStockInOrderDO;
|
||||
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.DeliveryDataDTO;
|
||||
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.SheetImportResultDTO;
|
||||
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.StockInDataDTO;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@@ -50,20 +51,23 @@ public interface IReservationStockInOrderService extends IService<ReservationSto
|
||||
List<ReservationInMaterialDetailPO> queryNoticePrintDetail(ReservationStockInOrderDO stockInOrderDTO);
|
||||
|
||||
/**
|
||||
* 解析Excel文件并获取出库数据
|
||||
* 解析Excel文件并导入出库单(多页签:文件中每个工作表独立解析为一张出库单,单页签文件行为不变)。
|
||||
* 任一页签校验失败则整体不落库,报错聚合列出所有失败页签
|
||||
* @param file Excel文件
|
||||
* @param sheetName 工作表名称(出库导入/参考数据)
|
||||
* @return 出库数据DTO
|
||||
* @param sheetName 兼容参数(多页签模式下不再按名字筛选,传空即可)
|
||||
* @param warehouseId 当前仓库id(非空时校验导入物料是否属于该仓库,并按该仓库过滤批次库存)
|
||||
* @return 每个工作表的导入结果(含生成的出库单号与解析数据)
|
||||
* @throws Exception 解析异常
|
||||
*/
|
||||
DeliveryDataDTO parseExcelFile(MultipartFile file, String sheetName) throws Exception;
|
||||
List<SheetImportResultDTO> parseExcelFile(MultipartFile file, String sheetName, Long warehouseId) throws Exception;
|
||||
|
||||
/**
|
||||
* 解析入库单Excel文件
|
||||
* 解析并导入入库单Excel(多页签:未指定页签名时文件中每个工作表独立解析为一张入库单,单页签文件行为不变)。
|
||||
* 任一页签校验失败则整体不落库,报错聚合列出所有失败页签
|
||||
* @param file Excel文件
|
||||
* @param sheetName 工作表名称(默认:入库模版)
|
||||
* @return 入库单完整数据(表头+明细)
|
||||
* @param sheetName 显式指定页签时保持单页签行为;不传/为空则为多页签模式
|
||||
* @return 每个工作表的导入结果(含生成的入库单号与解析数据)
|
||||
* @throws Exception 解析异常
|
||||
*/
|
||||
StockInDataDTO parseStockInExcel(MultipartFile file, String sheetName) throws Exception;
|
||||
List<SheetImportResultDTO> parseStockInExcel(MultipartFile file, String sheetName) throws Exception;
|
||||
}
|
||||
+358
-167
@@ -21,20 +21,47 @@ import java.util.Map;
|
||||
/**
|
||||
* 维他奶出库Excel解析监听器(新模板版)
|
||||
* 新模板特点:货品明细与客户配送明细左右分栏展示
|
||||
* 核心功能:
|
||||
* 1. 左侧栏:第1-3行解析表头(送货日期/送货线号/车次),第5行解析纪录合共,第7行起解析货品明细
|
||||
* 2. 右侧栏:第2行解析发票张数/小计,第4行起解析客户配送明细
|
||||
* 3. 自动处理文本型数字转换(String→Integer/BigDecimal)
|
||||
* 4. 完整的异常处理和日志记录
|
||||
* 核心功能(行列位置均不写死,模板调整行/列顺序不影响解析):
|
||||
* 1. 表头标签(送货日期/送货线号/车次/发票张数/小计/纪录合共):按标签文字定位标签所在列,取右侧相邻列的值
|
||||
* 2. 货品明细:按列头文字(貨品編號/簡稱/箱/支等,繁简体归一化)定位各列,列头行之后的行按数据解析
|
||||
* 3. 客户配送明细:按列头文字(類别/發票編號/客户編號/名稱/箱/包/金額等)定位各列
|
||||
* 4. 列头行识别失败时兜底:按老模板固定布局(左栏A-D列、右栏F-L列、行号1-7)解析,保证老模板不受影响
|
||||
* 5. 自动处理文本型数字转换(String→Integer/BigDecimal)
|
||||
*/
|
||||
public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<String, Object>> {
|
||||
// 日志实例
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDeliveryExcelListener.class);
|
||||
|
||||
// 关键字定义(Excel标签匹配)
|
||||
private static final String KEYWORD_TOTAL_RECORD = "紀錄合共";
|
||||
private static final String KEYWORD_INVOICE_COUNT = "發票張數";
|
||||
private static final String KEYWORD_SUBTOTAL = "小計";
|
||||
// ---------------------- 关键字/列头别名定义(归一化后匹配,兼容简繁体) ----------------------
|
||||
// 表头标签关键字
|
||||
private static final String[] KEYWORD_DELIVERY_DATE = {"送货日期"};
|
||||
private static final String[] KEYWORD_DELIVERY_LINE = {"送货线号", "送货线路"};
|
||||
private static final String[] KEYWORD_TRAIN = {"车次"};
|
||||
private static final String[] KEYWORD_TOTAL_RECORD = {"纪录合共"};
|
||||
private static final String[] KEYWORD_INVOICE_COUNT = {"发票张数"};
|
||||
private static final String[] KEYWORD_SUBTOTAL = {"小计"};
|
||||
|
||||
// 货品明细列头别名
|
||||
private static final String[] PRODUCT_CODE_ALIASES = {"货品编号", "产品编号", "物料编号", "货品编码", "产品编码", "物料编码", "料号"};
|
||||
private static final String[] PRODUCT_NAME_ALIASES = {"简称", "货品名称", "产品名称", "物料名称", "品名", "名称"};
|
||||
private static final String[] PRODUCT_BOX_ALIASES = {"箱数", "箱"};
|
||||
private static final String[] PRODUCT_PIECE_ALIASES = {"支数", "件数", "支"};
|
||||
|
||||
// 客户配送明细列头别名
|
||||
private static final String[] CATEGORY_ALIASES = {"类别"};
|
||||
private static final String[] INVOICE_NO_ALIASES = {"发票编号", "发票号", "发票"};
|
||||
private static final String[] CUSTOMER_CODE_ALIASES = {"客户编号", "客户编码", "客户代码"};
|
||||
private static final String[] CUSTOMER_NAME_ALIASES = {"客户名称", "名称"};
|
||||
private static final String[] CUSTOMER_BOX_ALIASES = {"箱数", "箱"};
|
||||
private static final String[] CUSTOMER_PACKAGE_ALIASES = {"包数", "包"};
|
||||
private static final String[] CUSTOMER_AMOUNT_ALIASES = {"金额", "总额"};
|
||||
|
||||
// 列头行认定门槛:锚点列(编号列)必须识别到,且识别列总数达标,防止数据行误判为列头行
|
||||
private static final int HEADER_MIN_MATCH_COUNT = 2;
|
||||
// 老模板兜底:货品明细数据固定从第7行、客户配送明细数据固定从第4行开始(列头未识别到时启用固定列位)
|
||||
private static final int PRODUCT_DATA_START_ROW = 7;
|
||||
private static final int CUSTOMER_DATA_START_ROW = 4;
|
||||
|
||||
// 支持的日期格式(String转Date)
|
||||
private static final String[] SUPPORTED_DATE_FORMATS = {"yyyy-MM-dd", "yyyy/MM/dd", "yyyyMMdd", "yyyy-MM-dd HH:mm:ss"};
|
||||
|
||||
@@ -43,6 +70,26 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
private final List<ProductDetail> productDetailList = new ArrayList<>();
|
||||
private final List<CustomerDeliveryDetail> customerDeliveryDetailList = new ArrayList<>();
|
||||
|
||||
// 货品明细列索引(-1表示未识别该列)
|
||||
private int productCodeCol = -1;
|
||||
private int productNameCol = -1;
|
||||
private int productBoxCol = -1;
|
||||
private int productPieceCol = -1;
|
||||
private boolean productColumnsResolved = false;
|
||||
|
||||
// 客户配送明细列索引(-1表示未识别该列)
|
||||
private int categoryCol = -1;
|
||||
private int invoiceNoCol = -1;
|
||||
private int customerCodeCol = -1;
|
||||
private int customerNameCol = -1;
|
||||
private int customerBoxCol = -1;
|
||||
private int customerPackageCol = -1;
|
||||
private int customerAmountCol = -1;
|
||||
private boolean customerColumnsResolved = false;
|
||||
|
||||
// 发票张数/小计行是否已解析
|
||||
private boolean invoiceRowParsed = false;
|
||||
|
||||
// 当前行号(1-based,与Excel行号一致)
|
||||
private int currentRowNum = 0;
|
||||
|
||||
@@ -53,10 +100,10 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
try {
|
||||
LOGGER.debug("第{}行:开始解析,所有单元格数据:{}", currentRowNum, data);
|
||||
|
||||
// 左侧区域解析(A-E列)
|
||||
// 左侧区域解析(货品相关信息)
|
||||
parseLeftSection(data);
|
||||
|
||||
// 右侧区域解析(F-M列)
|
||||
// 右侧区域解析(客户配送相关信息)
|
||||
parseRightSection(data);
|
||||
|
||||
} catch (ExcelDataConvertException e) {
|
||||
@@ -71,67 +118,66 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
|
||||
// ---------------------- 左侧区域解析(货品相关信息) ----------------------
|
||||
private void parseLeftSection(Map<String, Object> data) {
|
||||
switch (currentRowNum) {
|
||||
case 1:
|
||||
// 第1行:送货日期(A列=标签,B列=值)
|
||||
parseDeliveryDate(data);
|
||||
break;
|
||||
case 2:
|
||||
// 第2行:送货线号(A列=标签,B列=值)
|
||||
parseDeliveryLineNo(data);
|
||||
break;
|
||||
case 3:
|
||||
// 第3行:车次(A列=标签,B列=值)
|
||||
parseTrainNumber(data);
|
||||
break;
|
||||
case 4:
|
||||
// 第4行:货品明细标题行,无需解析
|
||||
LOGGER.debug("第{}行:货品明细标题行,跳过", currentRowNum);
|
||||
break;
|
||||
case 5:
|
||||
// 第5行:纪录合共(A列=标签,B列=总记录数,D列=总箱数,E列=总支数)
|
||||
parseTotalRecord(data);
|
||||
break;
|
||||
case 6:
|
||||
// 第6行:货品明细列标题(貨品編號/簡稱/箱/支),跳过
|
||||
LOGGER.debug("第{}行:货品明细列标题行,跳过", currentRowNum);
|
||||
break;
|
||||
default:
|
||||
// 第7行起:货品明细数据行(遇到空行自动结束)
|
||||
if (currentRowNum >= 7 && isValidProductDetailRow(data)) {
|
||||
parseProductDetail(data, currentRowNum);
|
||||
}
|
||||
break;
|
||||
// 1. 表头标签:送货日期/送货线号/车次(按标签文字定位,行位置不限)
|
||||
parseHeaderLabels(data);
|
||||
// 2. 纪录合共(按标签文字定位,行位置不限)
|
||||
parseTotalRecord(data);
|
||||
// 3. 货品明细:列布局已确定时按数据行解析
|
||||
if (productColumnsResolved) {
|
||||
if (isValidProductDetailRow(data)) {
|
||||
parseProductDetail(data, currentRowNum);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 4. 尝试识别货品明细列头行;识别成功则本行即列头行,跳过数据解析
|
||||
if (tryResolveProductColumns(data)) {
|
||||
return;
|
||||
}
|
||||
// 5. 超过老模板数据起始行仍未识别到列头行:启用固定列位兜底,从当前行开始按数据行解析
|
||||
if (currentRowNum >= PRODUCT_DATA_START_ROW) {
|
||||
applyProductFallbackColumns();
|
||||
if (isValidProductDetailRow(data)) {
|
||||
parseProductDetail(data, currentRowNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- 右侧区域解析(客户配送相关信息) ----------------------
|
||||
private void parseRightSection(Map<String, Object> data) {
|
||||
switch (currentRowNum) {
|
||||
case 1:
|
||||
// 第1行:客户配送明细标题行,无需解析
|
||||
LOGGER.debug("第{}行:客户配送明细标题行,跳过", currentRowNum);
|
||||
break;
|
||||
case 2:
|
||||
// 第2行:发票张数/小计(F列=發票張數标签,G列=数值;J列=小計标签,K/L/M列=数值)
|
||||
parseInvoiceCount(data);
|
||||
break;
|
||||
case 3:
|
||||
// 第3行:客户配送明细列标题(類别/發票編號/客户編號/名稱/箱/包/金額),跳过
|
||||
LOGGER.debug("第{}行:客户配送明细列标题行,跳过", currentRowNum);
|
||||
break;
|
||||
default:
|
||||
// 第4行起:客户配送明细数据行(遇到空行自动结束)
|
||||
if (currentRowNum >= 4 && isValidCustomerDetailRow(data)) {
|
||||
parseCustomerDeliveryDetail(data, currentRowNum);
|
||||
}
|
||||
break;
|
||||
// 1. 发票张数/小计(按标签文字定位,行位置不限,解析成功后不再重复解析)
|
||||
if (!invoiceRowParsed) {
|
||||
parseInvoiceRow(data);
|
||||
}
|
||||
// 2. 客户配送明细:列布局已确定时按数据行解析
|
||||
if (customerColumnsResolved) {
|
||||
if (isValidCustomerDetailRow(data)) {
|
||||
parseCustomerDeliveryDetail(data, currentRowNum);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 3. 尝试识别客户配送明细列头行;识别成功则本行即列头行,跳过数据解析
|
||||
if (tryResolveCustomerColumns(data)) {
|
||||
return;
|
||||
}
|
||||
// 4. 超过老模板数据起始行仍未识别到列头行:启用固定列位兜底,从当前行开始按数据行解析
|
||||
if (currentRowNum >= CUSTOMER_DATA_START_ROW) {
|
||||
applyCustomerFallbackColumns();
|
||||
if (isValidCustomerDetailRow(data)) {
|
||||
parseCustomerDeliveryDetail(data, currentRowNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- 解析完成回调 ----------------------
|
||||
@Override
|
||||
public void doAfterAllAnalysed(AnalysisContext context) {
|
||||
// 小计标签始终未识别到时保持与原逻辑一致:小计相关字段置0
|
||||
if (deliveryHeader.getSubtotalAmount() == null) {
|
||||
LOGGER.warn("未找到【小计】标签,小计信息置空");
|
||||
deliveryHeader.setSubtotalBoxCount(0);
|
||||
deliveryHeader.setSubtotalPieceCount(0);
|
||||
deliveryHeader.setSubtotalAmount(BigDecimal.ZERO);
|
||||
}
|
||||
LOGGER.info("Excel解析全部完成!最终统计结果:");
|
||||
LOGGER.info("1. 表头信息:送货日期={},送货线号={},车次={},发票张数={},总记录数={},小计金额={}",
|
||||
deliveryHeader.getDeliveryDate(),
|
||||
@@ -144,47 +190,52 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
LOGGER.info("3. 客户配送明细:共{}条记录", customerDeliveryDetailList.size());
|
||||
}
|
||||
|
||||
// ---------------------- 左侧:表头信息解析 ----------------------
|
||||
private void parseDeliveryDate(Map<String, Object> data) {
|
||||
Object value = getValueSafely(data, 1); // B列
|
||||
if (value != null) {
|
||||
Date deliveryDate = convertToDate(value);
|
||||
deliveryHeader.setDeliveryDate(deliveryDate);
|
||||
LOGGER.debug("第{}行:送货日期解析完成,值={}", currentRowNum, deliveryDate);
|
||||
// ---------------------- 左侧:表头信息解析(标签定位,行位置不限) ----------------------
|
||||
private void parseHeaderLabels(Map<String, Object> data) {
|
||||
// 送货日期
|
||||
if (deliveryHeader.getDeliveryDate() == null) {
|
||||
Integer keywordIndex = findKeywordIndex(data, KEYWORD_DELIVERY_DATE);
|
||||
if (keywordIndex != null) {
|
||||
Date deliveryDate = convertToDate(getValueSafely(data, keywordIndex + 1));
|
||||
if (deliveryDate != null) {
|
||||
deliveryHeader.setDeliveryDate(deliveryDate);
|
||||
LOGGER.debug("第{}行:送货日期解析完成,值={}", currentRowNum, deliveryDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 送货线号
|
||||
if (deliveryHeader.getDeliveryLineNo() == null || deliveryHeader.getDeliveryLineNo().trim().isEmpty()) {
|
||||
Integer keywordIndex = findKeywordIndex(data, KEYWORD_DELIVERY_LINE);
|
||||
if (keywordIndex != null) {
|
||||
Object value = getValueSafely(data, keywordIndex + 1);
|
||||
if (value != null && !value.toString().trim().isEmpty()) {
|
||||
deliveryHeader.setDeliveryLineNo(value.toString().trim());
|
||||
LOGGER.debug("第{}行:送货线号解析完成,值={}", currentRowNum, deliveryHeader.getDeliveryLineNo());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 车次
|
||||
if (deliveryHeader.getTrainNumber() == null) {
|
||||
Integer keywordIndex = findKeywordIndex(data, KEYWORD_TRAIN);
|
||||
if (keywordIndex != null) {
|
||||
deliveryHeader.setTrainNumber(convertToInteger(getValueSafely(data, keywordIndex + 1)));
|
||||
LOGGER.debug("第{}行:车次解析完成,值={}", currentRowNum, deliveryHeader.getTrainNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseDeliveryLineNo(Map<String, Object> data) {
|
||||
Object value = getValueSafely(data, 1); // B列
|
||||
if (value != null) {
|
||||
String deliveryLineNo = value.toString().trim();
|
||||
deliveryHeader.setDeliveryLineNo(deliveryLineNo);
|
||||
LOGGER.debug("第{}行:送货线号解析完成,值={}", currentRowNum, deliveryLineNo);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseTrainNumber(Map<String, Object> data) {
|
||||
Object value = getValueSafely(data, 1); // B列
|
||||
Integer trainNumber = convertToInteger(value);
|
||||
deliveryHeader.setTrainNumber(trainNumber);
|
||||
LOGGER.debug("第{}行:车次解析完成,值={}", currentRowNum, trainNumber);
|
||||
}
|
||||
|
||||
// ---------------------- 左侧:纪录合共解析 ----------------------
|
||||
// ---------------------- 左侧:纪录合共解析(标签定位,行位置不限) ----------------------
|
||||
/**
|
||||
* 第5行:纪录合共
|
||||
* 列对应关系:0=A列=紀錄合共标签,1=B列=总记录数,3=D列=总箱数,4=E列=总支数
|
||||
* 纪录合共:标签右侧第1列=总记录数,第3列=总箱数,第4列=总支数(与老模板B/D/E列偏移一致)
|
||||
*/
|
||||
private void parseTotalRecord(Map<String, Object> data) {
|
||||
Object totalRecordLabel = getValueSafely(data, 0);
|
||||
if (totalRecordLabel == null || !totalRecordLabel.toString().trim().contains(KEYWORD_TOTAL_RECORD)) {
|
||||
LOGGER.warn("第{}行:不是纪录合共行,跳过解析,第一列内容:{}", currentRowNum, totalRecordLabel);
|
||||
Integer keywordIndex = findKeywordIndex(data, KEYWORD_TOTAL_RECORD);
|
||||
if (keywordIndex == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Integer totalRecordCount = convertToInteger(getValueSafely(data, 1)); // B列
|
||||
Integer totalBoxCount = convertToInteger(getValueSafely(data, 3)); // D列
|
||||
Integer totalPieceCount = convertToInteger(getValueSafely(data, 4)); // E列
|
||||
Integer totalRecordCount = convertToInteger(getValueSafely(data, keywordIndex + 1));
|
||||
Integer totalBoxCount = convertToInteger(getValueSafely(data, keywordIndex + 3));
|
||||
Integer totalPieceCount = convertToInteger(getValueSafely(data, keywordIndex + 4));
|
||||
|
||||
deliveryHeader.setTotalRecordCount(totalRecordCount);
|
||||
deliveryHeader.setTotalBoxCount(totalBoxCount);
|
||||
@@ -194,27 +245,66 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
currentRowNum, totalRecordCount, totalBoxCount, totalPieceCount);
|
||||
}
|
||||
|
||||
// ---------------------- 左侧:货品明细解析 ----------------------
|
||||
// ---------------------- 左侧:货品明细列头识别与数据解析 ----------------------
|
||||
/**
|
||||
* 校验货品明细行是否有效(排除表头和空行)
|
||||
* 尝试将当前行识别为货品明细列头行:以货品编号列为锚点,按列头文字(简繁归一化)匹配各列
|
||||
*/
|
||||
private boolean isValidProductDetailRow(Map<String, Object> data) {
|
||||
Object productCode = getValueSafely(data, 0); // A列
|
||||
return productCode != null
|
||||
&& !productCode.toString().trim().isEmpty()
|
||||
&& !"貨品編號".equals(productCode.toString().trim());
|
||||
private boolean tryResolveProductColumns(Map<String, Object> data) {
|
||||
int codeCol = findColumnByAliases(data, PRODUCT_CODE_ALIASES);
|
||||
if (codeCol < 0) {
|
||||
return false;
|
||||
}
|
||||
int nameCol = findColumnByAliases(data, PRODUCT_NAME_ALIASES);
|
||||
int boxCol = findColumnByAliases(data, PRODUCT_BOX_ALIASES);
|
||||
int pieceCol = findColumnByAliases(data, PRODUCT_PIECE_ALIASES);
|
||||
int matched = 1 + (nameCol >= 0 ? 1 : 0) + (boxCol >= 0 ? 1 : 0) + (pieceCol >= 0 ? 1 : 0);
|
||||
if (matched < HEADER_MIN_MATCH_COUNT) {
|
||||
return false;
|
||||
}
|
||||
productCodeCol = codeCol;
|
||||
productNameCol = nameCol;
|
||||
productBoxCol = boxCol;
|
||||
productPieceCol = pieceCol;
|
||||
productColumnsResolved = true;
|
||||
LOGGER.info("第{}行识别为货品明细列头行,列映射:货品编号={},名称={},箱={},支={}",
|
||||
currentRowNum, productCodeCol, productNameCol, productBoxCol, productPieceCol);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析货品明细(第7行起)
|
||||
* 列对应关系:0=A列=貨品編號,1=B列=簡稱,2=C列=箱,3=D列=支
|
||||
* 兜底列位:老模板货品明细固定布局(0=A列=貨品編號,1=B列=簡稱,2=C列=箱,3=D列=支)
|
||||
*/
|
||||
private void applyProductFallbackColumns() {
|
||||
productCodeCol = 0;
|
||||
productNameCol = 1;
|
||||
productBoxCol = 2;
|
||||
productPieceCol = 3;
|
||||
productColumnsResolved = true;
|
||||
LOGGER.warn("第{}行前未识别到货品明细列头行,启用老模板固定列位兜底解析", currentRowNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验货品明细行是否有效(排除列头行和空行)
|
||||
*/
|
||||
private boolean isValidProductDetailRow(Map<String, Object> data) {
|
||||
Object productCode = getValueSafely(data, productCodeCol);
|
||||
if (productCode == null || productCode.toString().trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 列头文字本身不算数据行(防止列头行被当作数据重复解析)
|
||||
String text = normalizeHeaderText(productCode);
|
||||
return !matchesAny(text, PRODUCT_CODE_ALIASES);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析货品明细(列头行之后的行)
|
||||
*/
|
||||
private void parseProductDetail(Map<String, Object> data, int rowNum) {
|
||||
ProductDetail productDetail = new ProductDetail();
|
||||
productDetail.setProductCode(convertToString(getValueSafely(data, 0)));
|
||||
productDetail.setProductName(convertToString(getValueSafely(data, 1)));
|
||||
productDetail.setBoxCount(convertToInteger(getValueSafely(data, 2)));
|
||||
productDetail.setPieceCount(convertToInteger(getValueSafely(data, 3)));
|
||||
productDetail.setProductCode(convertToString(getValueSafely(data, productCodeCol)));
|
||||
productDetail.setProductName(convertToString(getValueSafely(data, productNameCol)));
|
||||
productDetail.setBoxCount(convertToInteger(getValueSafely(data, productBoxCol)));
|
||||
productDetail.setPieceCount(convertToInteger(getValueSafely(data, productPieceCol)));
|
||||
|
||||
productDetailList.add(productDetail);
|
||||
LOGGER.debug("第{}行:解析货品明细完成,货品编号={},名称={},箱数={},支数={}",
|
||||
@@ -222,67 +312,111 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
productDetail.getBoxCount(), productDetail.getPieceCount());
|
||||
}
|
||||
|
||||
// ---------------------- 右侧:发票张数与小计解析 ----------------------
|
||||
/**
|
||||
* 第2行:发票张数与小计
|
||||
* 通过关键字定位标签,再读取右侧相邻列,避免空列导致索引偏移
|
||||
*/
|
||||
private void parseInvoiceCount(Map<String, Object> data) {
|
||||
// 1. 定位并解析发票张数
|
||||
Integer invoiceCount = findValueAfterKeyword(data, KEYWORD_INVOICE_COUNT);
|
||||
if (invoiceCount != null) {
|
||||
deliveryHeader.setInvoiceCount(invoiceCount);
|
||||
// ---------------------- 右侧:发票张数与小计解析(标签定位,行位置不限) ----------------------
|
||||
private void parseInvoiceRow(Map<String, Object> data) {
|
||||
// 1. 发票张数:标签右侧相邻列
|
||||
Integer invoiceIndex = findKeywordIndex(data, KEYWORD_INVOICE_COUNT);
|
||||
if (invoiceIndex != null) {
|
||||
deliveryHeader.setInvoiceCount(convertToInteger(getValueSafely(data, invoiceIndex + 1)));
|
||||
}
|
||||
|
||||
// 2. 定位并解析小计(标签右侧第1列=箱,第2列=支,第3列=金额)
|
||||
Integer subtotalStartIndex = findKeywordIndex(data, KEYWORD_SUBTOTAL);
|
||||
if (subtotalStartIndex != null && subtotalStartIndex >= 0) {
|
||||
Integer subtotalBoxCount = convertToInteger(getValueSafely(data, subtotalStartIndex + 1));
|
||||
Integer subtotalPieceCount = convertToInteger(getValueSafely(data, subtotalStartIndex + 2));
|
||||
BigDecimal subtotalAmount = convertToBigDecimal(getValueSafely(data, subtotalStartIndex + 3));
|
||||
// 2. 小计:标签右侧第1列=箱,第2列=支,第3列=金额
|
||||
Integer subtotalIndex = findKeywordIndex(data, KEYWORD_SUBTOTAL);
|
||||
if (subtotalIndex != null && subtotalIndex >= 0) {
|
||||
Integer subtotalBoxCount = convertToInteger(getValueSafely(data, subtotalIndex + 1));
|
||||
Integer subtotalPieceCount = convertToInteger(getValueSafely(data, subtotalIndex + 2));
|
||||
BigDecimal subtotalAmount = convertToBigDecimal(getValueSafely(data, subtotalIndex + 3));
|
||||
|
||||
deliveryHeader.setSubtotalBoxCount(subtotalBoxCount);
|
||||
deliveryHeader.setSubtotalPieceCount(subtotalPieceCount);
|
||||
deliveryHeader.setSubtotalAmount(subtotalAmount);
|
||||
invoiceRowParsed = true;
|
||||
|
||||
LOGGER.debug("第{}行:解析小计完成,箱数={},支数={},金额={}",
|
||||
currentRowNum, subtotalBoxCount, subtotalPieceCount, subtotalAmount);
|
||||
} else {
|
||||
LOGGER.warn("第{}行:未找到小计标签,小计信息置空", currentRowNum);
|
||||
deliveryHeader.setSubtotalBoxCount(0);
|
||||
deliveryHeader.setSubtotalPieceCount(0);
|
||||
deliveryHeader.setSubtotalAmount(BigDecimal.ZERO);
|
||||
LOGGER.debug("第{}行:解析发票张数行完成,发票张数={},小计箱数={},支数={},金额={}",
|
||||
currentRowNum, deliveryHeader.getInvoiceCount(), subtotalBoxCount, subtotalPieceCount, subtotalAmount);
|
||||
}
|
||||
|
||||
LOGGER.debug("第{}行:解析发票张数行完成,发票张数={},小计金额={}",
|
||||
currentRowNum, deliveryHeader.getInvoiceCount(), deliveryHeader.getSubtotalAmount());
|
||||
}
|
||||
|
||||
// ---------------------- 右侧:客户配送明细解析 ----------------------
|
||||
// ---------------------- 右侧:客户配送明细列头识别与数据解析 ----------------------
|
||||
/**
|
||||
* 校验客户配送明细行是否有效(排除表头和空行)
|
||||
* 尝试将当前行识别为客户配送明细列头行:以客户编号列为锚点,按列头文字(简繁归一化)匹配各列
|
||||
*/
|
||||
private boolean tryResolveCustomerColumns(Map<String, Object> data) {
|
||||
int codeCol = findColumnByAliases(data, CUSTOMER_CODE_ALIASES);
|
||||
if (codeCol < 0) {
|
||||
return false;
|
||||
}
|
||||
int categoryColTmp = findColumnByAliases(data, CATEGORY_ALIASES);
|
||||
int invoiceNoColTmp = findColumnByAliases(data, INVOICE_NO_ALIASES);
|
||||
int nameColTmp = findColumnByAliases(data, CUSTOMER_NAME_ALIASES);
|
||||
int boxColTmp = findColumnByAliases(data, CUSTOMER_BOX_ALIASES);
|
||||
int packageColTmp = findColumnByAliases(data, CUSTOMER_PACKAGE_ALIASES);
|
||||
int amountColTmp = findColumnByAliases(data, CUSTOMER_AMOUNT_ALIASES);
|
||||
int matched = 1 + (categoryColTmp >= 0 ? 1 : 0) + (invoiceNoColTmp >= 0 ? 1 : 0) + (nameColTmp >= 0 ? 1 : 0)
|
||||
+ (boxColTmp >= 0 ? 1 : 0) + (packageColTmp >= 0 ? 1 : 0) + (amountColTmp >= 0 ? 1 : 0);
|
||||
if (matched < HEADER_MIN_MATCH_COUNT) {
|
||||
return false;
|
||||
}
|
||||
categoryCol = categoryColTmp;
|
||||
invoiceNoCol = invoiceNoColTmp;
|
||||
customerCodeCol = codeCol;
|
||||
customerNameCol = nameColTmp;
|
||||
customerBoxCol = boxColTmp;
|
||||
customerPackageCol = packageColTmp;
|
||||
customerAmountCol = amountColTmp;
|
||||
customerColumnsResolved = true;
|
||||
// 关键列未识别到时告警,避免发票编号等字段静默丢失
|
||||
if (invoiceNoColTmp < 0 || amountColTmp < 0) {
|
||||
LOGGER.warn("第{}行客户配送明细列头识别到但存在缺失列:发票编号列={},金额列={}," +
|
||||
"对应数据将解析为空,请检查模板列头文字是否在归一化映射范围内",
|
||||
currentRowNum, invoiceNoColTmp, amountColTmp);
|
||||
}
|
||||
LOGGER.info("第{}行识别为客户配送明细列头行,列映射:类别={},发票编号={},客户编号={},名称={},箱={},包={},金额={}",
|
||||
currentRowNum, categoryCol, invoiceNoCol, customerCodeCol, customerNameCol, customerBoxCol, customerPackageCol, customerAmountCol);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 兜底列位:老模板客户配送明细固定布局(5=F列=類别,6=G列=發票編號,7=H列=客户編號,8=I列=名稱,
|
||||
* 9=J列=箱,10=K列=包,11=L列=金額)
|
||||
*/
|
||||
private void applyCustomerFallbackColumns() {
|
||||
categoryCol = 5;
|
||||
invoiceNoCol = 6;
|
||||
customerCodeCol = 7;
|
||||
customerNameCol = 8;
|
||||
customerBoxCol = 9;
|
||||
customerPackageCol = 10;
|
||||
customerAmountCol = 11;
|
||||
customerColumnsResolved = true;
|
||||
LOGGER.warn("第{}行前未识别到客户配送明细列头行,启用老模板固定列位兜底解析", currentRowNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验客户配送明细行是否有效(排除列头行和空行)
|
||||
*/
|
||||
private boolean isValidCustomerDetailRow(Map<String, Object> data) {
|
||||
Object category = getValueSafely(data, 5); // F列
|
||||
return category != null
|
||||
&& !category.toString().trim().isEmpty()
|
||||
&& !"類别".equals(category.toString().trim());
|
||||
Object category = getValueSafely(data, categoryCol);
|
||||
if (category == null || category.toString().trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 列头文字本身不算数据行(防止列头行被当作数据重复解析)
|
||||
String text = normalizeHeaderText(category);
|
||||
return !matchesAny(text, CATEGORY_ALIASES);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析客户配送明细(第4行起)
|
||||
* 列对应关系:5=F列=類别,6=G列=發票編號,7=H列=客户編號,8=I列=名稱,
|
||||
* 9=J列=箱,10=K列=包,11=L列=金額
|
||||
* 解析客户配送明细(列头行之后的行)
|
||||
*/
|
||||
private void parseCustomerDeliveryDetail(Map<String, Object> data, int rowNum) {
|
||||
CustomerDeliveryDetail customerDetail = new CustomerDeliveryDetail();
|
||||
customerDetail.setCategory(convertToString(getValueSafely(data, 5)));
|
||||
customerDetail.setInvoiceNo(convertToString(getValueSafely(data, 6)));
|
||||
customerDetail.setCustomerCode(convertToString(getValueSafely(data, 7)));
|
||||
customerDetail.setCustomerName(convertToString(getValueSafely(data, 8)));
|
||||
customerDetail.setBoxCount(convertToInteger(getValueSafely(data, 9)));
|
||||
customerDetail.setPackageCount(convertToInteger(getValueSafely(data, 10)));
|
||||
customerDetail.setAmount(convertToBigDecimal(getValueSafely(data, 11)));
|
||||
customerDetail.setCategory(convertToString(getValueSafely(data, categoryCol)));
|
||||
customerDetail.setInvoiceNo(convertToString(getValueSafely(data, invoiceNoCol)));
|
||||
customerDetail.setCustomerCode(convertToString(getValueSafely(data, customerCodeCol)));
|
||||
customerDetail.setCustomerName(convertToString(getValueSafely(data, customerNameCol)));
|
||||
customerDetail.setBoxCount(convertToInteger(getValueSafely(data, customerBoxCol)));
|
||||
customerDetail.setPackageCount(convertToInteger(getValueSafely(data, customerPackageCol)));
|
||||
customerDetail.setAmount(convertToBigDecimal(getValueSafely(data, customerAmountCol)));
|
||||
|
||||
customerDeliveryDetailList.add(customerDetail);
|
||||
LOGGER.debug("第{}行:解析客户配送明细完成,类别={},发票编号={},客户编号={},名称={},箱数={},包数={},金额={}",
|
||||
@@ -299,7 +433,7 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
* 此时key不是数字字符串,需跳过这些key,走位置兜底逻辑
|
||||
*/
|
||||
private Object getValueSafely(Map<String, Object> data, int columnIndex) {
|
||||
if (data == null || data.isEmpty()) {
|
||||
if (columnIndex < 0 || data == null || data.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -333,9 +467,9 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
return "未知列";
|
||||
}
|
||||
int index = 0;
|
||||
for (String key : data.keySet()) {
|
||||
for (Object key : data.keySet()) {
|
||||
if (index == columnIndex) {
|
||||
return key;
|
||||
return String.valueOf(key);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
@@ -343,22 +477,28 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
}
|
||||
|
||||
/**
|
||||
* 在指定行中查找关键字所在的列索引
|
||||
* 注意:Map的key可能是非数字字符串(如表头文本"客户配送明细"),此时跳过该key,不抛出异常
|
||||
* 在指定行中查找关键字所在的列索引(关键字归一化后包含匹配,兼容简繁体与冒号等符号)
|
||||
* 注意:Map的key可能是非数字字符串(如表头文本),此时跳过该key,不抛出异常
|
||||
*/
|
||||
private Integer findKeywordIndex(Map<String, Object> data, String keyword) {
|
||||
if (data == null || data.isEmpty() || keyword == null) {
|
||||
private Integer findKeywordIndex(Map<String, Object> data, String[] keywords) {
|
||||
if (data == null || data.isEmpty() || keywords == null) {
|
||||
return null;
|
||||
}
|
||||
for (Object keyObj : data.keySet()) {
|
||||
Object value = data.get(keyObj);
|
||||
if (value != null && value.toString().trim().contains(keyword)) {
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
String text = normalizeHeaderText(value);
|
||||
if (text.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (matchesAny(text, keywords)) {
|
||||
try {
|
||||
return Integer.parseInt(keyObj.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
// key不是数字(可能是表头文本如"客户配送明细"),跳过,不打印警告避免日志刷屏
|
||||
LOGGER.debug("第{}行:关键字'{}'所在列key不是数字,key={},跳过",
|
||||
currentRowNum, keyword, keyObj);
|
||||
// key不是数字(可能是表头文本),跳过,不打印警告避免日志刷屏
|
||||
LOGGER.debug("第{}行:关键字所在列key不是数字,key={},跳过", currentRowNum, keyObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -366,15 +506,66 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找关键字所在列的右侧相邻列值,并转为Integer
|
||||
* 在指定行中按列头别名查找列索引(列头文字归一化后包含匹配,返回首个命中的列;未命中返回-1)
|
||||
*/
|
||||
private Integer findValueAfterKeyword(Map<String, Object> data, String keyword) {
|
||||
Integer keywordIndex = findKeywordIndex(data, keyword);
|
||||
if (keywordIndex == null) {
|
||||
LOGGER.warn("第{}行:未找到关键字'{}'", currentRowNum, keyword);
|
||||
return null;
|
||||
private int findColumnByAliases(Map<String, Object> data, String[] aliases) {
|
||||
if (data == null || data.isEmpty() || aliases == null) {
|
||||
return -1;
|
||||
}
|
||||
return convertToInteger(getValueSafely(data, keywordIndex + 1));
|
||||
for (Object keyObj : data.keySet()) {
|
||||
Object value = data.get(keyObj);
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
String text = normalizeHeaderText(value);
|
||||
if (text.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (matchesAny(text, aliases)) {
|
||||
try {
|
||||
return Integer.parseInt(keyObj.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
// key不是数字(可能是表头文本),跳过
|
||||
LOGGER.debug("第{}行:列头所在列key不是数字,key={},跳过", currentRowNum, keyObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 列头/标签文字归一化:去空格/冒号/括号等符号、转小写、繁体转简体(覆盖模板常见用字)
|
||||
*/
|
||||
private String normalizeHeaderText(Object value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
String s = value.toString().trim().toLowerCase();
|
||||
s = s.replace(":", "").replace(":", "")
|
||||
.replace(" ", "").replace(" ", "")
|
||||
.replace("(", "").replace(")", "")
|
||||
.replace("(", "").replace(")", "")
|
||||
.replace("/", "").replace("\\", "");
|
||||
s = s.replace('貨', '货').replace('編', '编').replace('號', '号').replace('稱', '称')
|
||||
.replace('數', '数').replace('飲', '饮').replace('規', '规').replace('戶', '户')
|
||||
.replace('紀', '纪').replace('錄', '录').replace('張', '张').replace('計', '计')
|
||||
.replace('車', '车').replace('線', '线').replace('類', '类').replace('別', '别')
|
||||
.replace('簡', '简').replace('產', '产').replace('單', '单')
|
||||
// 維他奶模板「發票編號/發票張數/金額」用字:缺了这两项会导致發票編號列识别失败、发票编号不落库
|
||||
.replace('發', '发').replace('額', '额');
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断归一化后的文字是否命中任一别名
|
||||
*/
|
||||
private boolean matchesAny(String text, String[] aliases) {
|
||||
for (String alias : aliases) {
|
||||
if (text.contains(alias)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+206
-40
@@ -17,7 +17,11 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* 入库单Excel解析监听器
|
||||
* 功能:1. 解析第一行表头数据 2. 跳过第二行合计行、第三行列名行 3. 解析第四行开始的产品明细 4. 支持文本型数字转换
|
||||
* 功能:1. 解析第一行表头数据(入库日期、OrderNO,按标签定位,位置不敏感)
|
||||
* 2. 动态定位列头行:扫描前几行,识别出"產品編號"等已知列头文字的行即认定为列头行(行位置不敏感)
|
||||
* 3. 列按列头文字动态定位(兼容繁体列头:產品編號/客戶編號/件數/飲用日期等),列顺序调整不影响解析
|
||||
* 4. 列头无法识别时兜底:按老模板固定列位(0=编号,1=名称,2=规格,3=件数,4=饮用日期)
|
||||
* 5. 支持文本型数字转换
|
||||
*/
|
||||
public class StockInExcelListener extends AnalysisEventListener<Map<String, Object>> {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(StockInExcelListener.class);
|
||||
@@ -35,6 +39,29 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
"dd.MM.yyyy", "d-MMM-yyyy", "yyyyMMddHHmmss"
|
||||
};
|
||||
|
||||
// ====== 列头别名(归一化后匹配,兼容简繁体及常见叫法) ======
|
||||
private static final String[] PRODUCT_CODE_ALIASES = {"产品编号", "物料编号", "产品编码", "物料编码", "料号"};
|
||||
private static final String[] PRODUCT_NAME_ALIASES = {"产品名称", "物料名称", "商品名称", "品名"};
|
||||
private static final String[] CUSTOMER_CODE_ALIASES = {"客户编号", "客户编码", "客户代码"};
|
||||
private static final String[] DRINK_DATE_ALIASES = {"饮用日期", "饮用时间"};
|
||||
private static final String[] PIECE_COUNT_ALIASES = {"件数"};
|
||||
private static final String[] SPEC_ALIASES = {"规格", "箱含量"};
|
||||
|
||||
// 列头行至少需识别出的列数(防止把日期标签行/合计行误判为列头行)
|
||||
private static final int HEADER_MIN_MATCH_COUNT = 2;
|
||||
// 老模板列头行固定在第3行:超过该行仍未识别到列头行时,启用固定列位兜底
|
||||
private static final int FALLBACK_AFTER_ROW_NUM = 3;
|
||||
|
||||
// 动态解析出的列索引(-1表示未识别该列)
|
||||
private int productCodeCol = -1;
|
||||
private int productNameCol = -1;
|
||||
private int customerCodeCol = -1;
|
||||
private int drinkDateCol = -1;
|
||||
private int pieceCountCol = -1;
|
||||
private int specCol = -1;
|
||||
// 列布局是否已确定(列头行识别成功或已启用兜底)
|
||||
private boolean columnResolved = false;
|
||||
|
||||
// 行号标记(用于区分第一行和表格行)
|
||||
private int currentRowNum = 0;
|
||||
|
||||
@@ -48,21 +75,21 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
if (currentRowNum == 1) {
|
||||
parseHeaderData(data);
|
||||
}
|
||||
// 2. 跳过第二行:合计行(Total: xxx)
|
||||
else if (currentRowNum == 2) {
|
||||
LOGGER.debug("第{}行是合计行,跳过解析", currentRowNum);
|
||||
}
|
||||
// 3. 跳过第三行:表格列名行(產品編號、產品名稱...)
|
||||
else if (currentRowNum == 3) {
|
||||
LOGGER.debug("第{}行是表格列名行,跳过解析", currentRowNum);
|
||||
}
|
||||
// 4. 解析第四行及以后:产品明细
|
||||
else if (currentRowNum >= 4) {
|
||||
if (isValidProductRow(data)) {
|
||||
StockInProductDetail detail = parseProductDetail(data);
|
||||
productDetailList.add(detail);
|
||||
// 2. 列布局未确定:尝试识别列头行;识别成功则本行即为列头行,跳过数据解析
|
||||
else if (!columnResolved) {
|
||||
if (!tryResolveHeaderRow(data)) {
|
||||
// 3. 超过老模板列头行位置仍未识别到列头行:启用固定列位兜底,从当前行开始按数据行解析
|
||||
if (currentRowNum > FALLBACK_AFTER_ROW_NUM) {
|
||||
applyFallbackColumns();
|
||||
parseDataRow(data);
|
||||
}
|
||||
// 否则(合计行Total等):跳过
|
||||
}
|
||||
}
|
||||
// 4. 列布局已确定:按数据行解析
|
||||
else {
|
||||
parseDataRow(data);
|
||||
}
|
||||
|
||||
} catch (ExcelDataConvertException e) {
|
||||
// Excel内置转换异常(如日期格式错误)
|
||||
@@ -79,6 +106,16 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据行解析:排除合计行/空行后解析产品明细
|
||||
*/
|
||||
private void parseDataRow(Map<String, Object> data) {
|
||||
if (isValidProductRow(data)) {
|
||||
StockInProductDetail detail = parseProductDetail(data);
|
||||
productDetailList.add(detail);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析第一行表头数据
|
||||
* 模板中标签与值相邻,分别搜索"入库日期"和"OrderNO"标签后一列的值,兼容位置调整
|
||||
@@ -86,15 +123,17 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
private void parseHeaderData(Map<String, Object> data) {
|
||||
// 入库日期:查找包含"入库日期"标签的下一个值
|
||||
Object stockInDateObj = getValueAfterLabel(data, "入庫日期:");
|
||||
if (stockInDateObj == null) {
|
||||
stockInDateObj = getValueAfterLabel(data, "入库日期");
|
||||
}
|
||||
if (stockInDateObj != null) {
|
||||
Date stockInDate = convertToDate(stockInDateObj);
|
||||
stockInHeader.setStockInDate(stockInDate);
|
||||
LOGGER.debug("第1行:入库日期解析完成,值:{}", stockInDate);
|
||||
}
|
||||
|
||||
// OrderNO:优先查找"OrderNO"标签后的值(老模板布局,标签与值相邻);
|
||||
// 新模板第1行无"OrderNO"标签,单号值直接放在单元格中(如E1),
|
||||
// 此时取第1行中除标签、入库日期以外的第一个非空字符串值
|
||||
// OrderNO:优先查找"OrderNO"标签后的值(标签与值相邻);
|
||||
// 新模板第1行无"OrderNO"标签时,取第1行中除标签、入库日期以外的第一个非空字符串值
|
||||
Object orderNoObj = getValueAfterLabel(data, "OrderNO");
|
||||
if (orderNoObj == null) {
|
||||
for (Object value : data.values()) {
|
||||
@@ -103,7 +142,7 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
}
|
||||
String str = value.toString().trim();
|
||||
// 跳过标签单元格与已被识别为入库日期的值
|
||||
if (str.isEmpty() || str.contains("入庫日期") || str.contains("OrderNO") || str.contains("Total")) {
|
||||
if (str.isEmpty() || str.contains("入庫日期") || str.contains("入库日期") || str.contains("OrderNO") || str.contains("Total")) {
|
||||
continue;
|
||||
}
|
||||
if (value == stockInDateObj) {
|
||||
@@ -125,26 +164,113 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试将当前行识别为列头行:按归一化后的列头文字匹配已知列别名
|
||||
* 识别成功(至少识别出产品编号列且总数达标)返回true并记录各列索引
|
||||
*/
|
||||
private boolean tryResolveHeaderRow(Map<String, Object> data) {
|
||||
if (data == null || data.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
int tmpProductCodeCol = -1;
|
||||
int tmpProductNameCol = -1;
|
||||
int tmpCustomerCodeCol = -1;
|
||||
int tmpDrinkDateCol = -1;
|
||||
int tmpPieceCountCol = -1;
|
||||
int tmpSpecCol = -1;
|
||||
int matched = 0;
|
||||
|
||||
for (Object keyObj : data.keySet()) {
|
||||
Object value = data.get(keyObj);
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
String text = normalizeHeaderText(value);
|
||||
if (text.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
int colIndex;
|
||||
try {
|
||||
colIndex = Integer.parseInt(keyObj.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
// key不是数字(如表头文本),跳过
|
||||
continue;
|
||||
}
|
||||
// 按组依次匹配(先具体后一般,避免同一列头命中多组别名时归属错乱)
|
||||
if (tmpProductCodeCol < 0 && matchesAny(text, PRODUCT_CODE_ALIASES)) {
|
||||
tmpProductCodeCol = colIndex;
|
||||
matched++;
|
||||
} else if (tmpCustomerCodeCol < 0 && matchesAny(text, CUSTOMER_CODE_ALIASES)) {
|
||||
tmpCustomerCodeCol = colIndex;
|
||||
matched++;
|
||||
} else if (tmpProductNameCol < 0 && matchesAny(text, PRODUCT_NAME_ALIASES)) {
|
||||
tmpProductNameCol = colIndex;
|
||||
matched++;
|
||||
} else if (tmpDrinkDateCol < 0 && matchesAny(text, DRINK_DATE_ALIASES)) {
|
||||
tmpDrinkDateCol = colIndex;
|
||||
matched++;
|
||||
} else if (tmpPieceCountCol < 0 && matchesAny(text, PIECE_COUNT_ALIASES)) {
|
||||
tmpPieceCountCol = colIndex;
|
||||
matched++;
|
||||
} else if (tmpSpecCol < 0 && matchesAny(text, SPEC_ALIASES)) {
|
||||
tmpSpecCol = colIndex;
|
||||
matched++;
|
||||
}
|
||||
}
|
||||
|
||||
// 产品编号列是数据解析的锚点,必须识别到;且识别总数达标才认定为列头行
|
||||
if (tmpProductCodeCol < 0 || matched < HEADER_MIN_MATCH_COUNT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
productCodeCol = tmpProductCodeCol;
|
||||
productNameCol = tmpProductNameCol;
|
||||
customerCodeCol = tmpCustomerCodeCol;
|
||||
drinkDateCol = tmpDrinkDateCol;
|
||||
pieceCountCol = tmpPieceCountCol;
|
||||
specCol = tmpSpecCol;
|
||||
columnResolved = true;
|
||||
LOGGER.info("第{}行识别为列头行,列映射:产品编号={},产品名称={},客户编号={},规格={},件数={},饮用日期={}",
|
||||
currentRowNum, productCodeCol, productNameCol, customerCodeCol, specCol, pieceCountCol, drinkDateCol);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 兜底列位:老模板固定布局(0=编号,1=名称,2=规格,3=件数,4=饮用日期),无客户编号列
|
||||
*/
|
||||
private void applyFallbackColumns() {
|
||||
productCodeCol = 0;
|
||||
productNameCol = 1;
|
||||
customerCodeCol = -1;
|
||||
specCol = 2;
|
||||
pieceCountCol = 3;
|
||||
drinkDateCol = 4;
|
||||
columnResolved = true;
|
||||
LOGGER.warn("第{}行前未识别到列头行,启用老模板固定列位兜底解析", currentRowNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析产品明细数据(一行对应一个产品)
|
||||
* 新模板列顺序:0=产品编号,1=产品名称,2=规格(箱含量),3=件数,4=饮用日期
|
||||
* 各列按列头行动态定位的索引取值,未识别的列取null/默认值
|
||||
*/
|
||||
private StockInProductDetail parseProductDetail(Map<String, Object> data) {
|
||||
StockInProductDetail detail = new StockInProductDetail();
|
||||
|
||||
// 产品编号(第1列,索引0)- 支持文本型数字转换
|
||||
detail.setProductCode(convertToString(getValueSafely(data, 0)));
|
||||
// 产品名称(第2列,索引1)
|
||||
detail.setProductName(convertToString(getValueSafely(data, 1)));
|
||||
// 箱含量/规格(第3列,索引2)
|
||||
detail.setBoxCapacity(convertToInteger(getValueSafely(data, 2)));
|
||||
// 件数(第4列,索引3)
|
||||
detail.setPieceCount(convertToInteger(getValueSafely(data, 3)));
|
||||
// 饮用日期(第5列,索引4)
|
||||
detail.setDrinkDate(convertToDate(getValueSafely(data, 4)));
|
||||
// 产品编号(可不填名称,导入时按编号自动匹配库中物料名称)
|
||||
detail.setProductCode(convertToString(getValueSafely(data, productCodeCol)));
|
||||
// 产品名称(列头未识别或单元格未填时为空串,由服务端按编号自动匹配覆盖)
|
||||
detail.setProductName(convertToString(getValueSafely(data, productNameCol)));
|
||||
// 客户编号(新模板新增列,可不填;填写后由服务端校验客户存在性并自动匹配客户名称)
|
||||
detail.setCustomerCode(convertToString(getValueSafely(data, customerCodeCol)));
|
||||
// 箱含量/规格
|
||||
detail.setBoxCapacity(convertToInteger(getValueSafely(data, specCol)));
|
||||
// 件数
|
||||
detail.setPieceCount(convertToInteger(getValueSafely(data, pieceCountCol)));
|
||||
// 饮用日期
|
||||
detail.setDrinkDate(convertToDate(getValueSafely(data, drinkDateCol)));
|
||||
|
||||
LOGGER.debug("第{}行产品明细解析完成,产品编号:{},产品名称:{}",
|
||||
currentRowNum, detail.getProductCode(), detail.getProductName());
|
||||
LOGGER.debug("第{}行产品明细解析完成,产品编号:{},产品名称:{},客户编号:{}",
|
||||
currentRowNum, detail.getProductCode(), detail.getProductName(), detail.getCustomerCode());
|
||||
return detail;
|
||||
}
|
||||
|
||||
@@ -152,9 +278,6 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
* 校验产品行是否有效(排除空行和合计行)
|
||||
*/
|
||||
private boolean isValidProductRow(Map<String, Object> data) {
|
||||
// 有效条件:产品编号不为空 + 不是合计行
|
||||
Object productCodeObj = getValueSafely(data, 0);
|
||||
|
||||
// 排除合计行(整行包含"Total"字样)
|
||||
if (containsAnyValue(data, "Total")) {
|
||||
LOGGER.debug("第{}行是合计行,跳过解析", currentRowNum);
|
||||
@@ -162,6 +285,7 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
}
|
||||
|
||||
// 排除空行(产品编号为空)
|
||||
Object productCodeObj = getValueSafely(data, productCodeCol);
|
||||
return productCodeObj != null && !productCodeObj.toString().trim().isEmpty();
|
||||
}
|
||||
|
||||
@@ -178,13 +302,24 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
|
||||
// ---------------------- 工具方法(类型转换+安全取值) ----------------------
|
||||
/**
|
||||
* 按列索引安全获取值(支持列索引越界处理)
|
||||
* 按列索引安全获取值
|
||||
* 优先按Map的key直接查找(适配EasyExcel以列号为key的情况,避免空单元格导致的位置偏移),
|
||||
* 找不到再按位置顺序查找(保持与原逻辑兼容)
|
||||
*/
|
||||
private Object getValueSafely(Map<String, Object> data, int columnIndex) {
|
||||
if (data == null || data.isEmpty()) {
|
||||
if (columnIndex < 0 || data == null || data.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String strIndex = String.valueOf(columnIndex);
|
||||
// 1. 直接按key查找(key为String或Integer,跳过非数字key)
|
||||
for (Object keyObj : data.keySet()) {
|
||||
if (keyObj != null && strIndex.equals(keyObj.toString())) {
|
||||
return data.get(keyObj);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 兜底:按顺序位置查找
|
||||
int index = 0;
|
||||
for (Object value : data.values()) {
|
||||
if (index == columnIndex) {
|
||||
@@ -193,7 +328,7 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
index++;
|
||||
}
|
||||
|
||||
LOGGER.warn("第{}行列索引{}超出范围(当前行共{}列),返回null",
|
||||
LOGGER.warn("第{}行列索引{}超出当前行单元格数量({}列),返回null",
|
||||
currentRowNum, columnIndex, data.size());
|
||||
return null;
|
||||
}
|
||||
@@ -207,9 +342,9 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (String key : data.keySet()) {
|
||||
for (Object key : data.keySet()) {
|
||||
if (index == columnIndex) {
|
||||
return key;
|
||||
return String.valueOf(key);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
@@ -251,6 +386,37 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 列头文字归一化:去空格/冒号/括号等符号、转小写、繁体转简体(覆盖模板常见用字)
|
||||
*/
|
||||
private String normalizeHeaderText(Object value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
String s = value.toString().trim().toLowerCase();
|
||||
s = s.replace(":", "").replace(":", "")
|
||||
.replace(" ", "").replace(" ", "")
|
||||
.replace("(", "").replace(")", "")
|
||||
.replace("(", "").replace(")", "")
|
||||
.replace("/", "").replace("\\", "");
|
||||
s = s.replace('產', '产').replace('編', '编').replace('號', '号').replace('稱', '称')
|
||||
.replace('數', '数').replace('飲', '饮').replace('規', '规').replace('戶', '户')
|
||||
.replace('貨', '货').replace('單', '单').replace('類', '类').replace('倉', '仓');
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断归一化后的列头文字是否命中任一别名
|
||||
*/
|
||||
private boolean matchesAny(String text, String[] aliases) {
|
||||
for (String alias : aliases) {
|
||||
if (text.contains(alias)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用转换:Object转String
|
||||
*/
|
||||
@@ -347,4 +513,4 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
public List<StockInProductDetail> getProductDetailList() {
|
||||
return productDetailList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user