diff --git a/mhd-auth/src/main/java/com/mhd/auth/system/controller/SsoAuthorizeController.java b/mhd-auth/src/main/java/com/mhd/auth/system/controller/SsoAuthorizeController.java index 4891fb10e..024cb8c3f 100644 --- a/mhd-auth/src/main/java/com/mhd/auth/system/controller/SsoAuthorizeController.java +++ b/mhd-auth/src/main/java/com/mhd/auth/system/controller/SsoAuthorizeController.java @@ -79,6 +79,7 @@ public class SsoAuthorizeController { // 5. 重定向到前端页面,token通过URL参数传递 // 前端页面需读取URL中的token参数并存入localStorage log.info("SSO单点登录成功, account={}", account); + log.info("SSO单点登录成功, account={}, token={} , redirectUrl={}", account, accessToken, redirectUrl); response.sendRedirect(redirectUrl + "?token=" + URLEncoder.encode(accessToken, StandardCharsets.UTF_8.name())); } catch (Exception e) { diff --git a/mhd-auth/src/main/java/com/mhd/auth/system/service/SsoAuthorizeService.java b/mhd-auth/src/main/java/com/mhd/auth/system/service/SsoAuthorizeService.java index e3e6040d6..ee2613ecd 100644 --- a/mhd-auth/src/main/java/com/mhd/auth/system/service/SsoAuthorizeService.java +++ b/mhd-auth/src/main/java/com/mhd/auth/system/service/SsoAuthorizeService.java @@ -50,7 +50,7 @@ public class SsoAuthorizeService { @Value("${sso.public-key}") private String publicKeyStr; - @Value("https://diglog.namkwong.com.mo/main_app/sso") + @Value("${sso.redirect-url}") private String redirectUrl; @Resource diff --git a/mhd-auth/src/main/java/com/mhd/auth/system/service/SysLoginService.java b/mhd-auth/src/main/java/com/mhd/auth/system/service/SysLoginService.java index dbf6b5e80..09112dcc3 100644 --- a/mhd-auth/src/main/java/com/mhd/auth/system/service/SysLoginService.java +++ b/mhd-auth/src/main/java/com/mhd/auth/system/service/SysLoginService.java @@ -550,11 +550,11 @@ public class SysLoginService { throw new ServiceException("请输入手机号和短信验证码"); } - //调用工具类校验,校验手机号格式错误 - if (!RegexUtil.isPhoneLegal(userPhone)) { - recordLogininfor(userPhone, Constants.LOGIN_FAIL, UserError.USER_PHONE_REG_ERROR.errmsg(), 1); - throw new DigitalLogisticsException(UserError.USER_PHONE_REG_ERROR); - } +// //调用工具类校验,校验手机号格式错误 +// if (!RegexUtil.isPhoneLegal(userPhone)) { +// recordLogininfor(userPhone, Constants.LOGIN_FAIL, UserError.USER_PHONE_REG_ERROR.errmsg(), 1); +// throw new DigitalLogisticsException(UserError.USER_PHONE_REG_ERROR); +// } //根据域名获取一级组织ID AjaxResult topOrganizationIdR = organizationServiceFeign.getOrganizationByPath(path, SecurityConstants.INNER); diff --git a/mhd-auth/src/main/resources/bootstrap.yml b/mhd-auth/src/main/resources/bootstrap.yml index 0f7b839c1..828fff329 100644 --- a/mhd-auth/src/main/resources/bootstrap.yml +++ b/mhd-auth/src/main/resources/bootstrap.yml @@ -15,7 +15,7 @@ spring: # server-addr: 127.0.0.1:8848 # 线上测试环境配置 容器名+端口号 # server-addr: 10.33.0.129:6010 - server-addr: 10.102.192.30:6848 + server-addr: 10.102.192.31:6848 username: nacos password: manhuoda@2023 #线上正式环境 @@ -24,7 +24,7 @@ spring: # password: manhuoda@2023 config: # server-addr: 127.0.0.1:8848 - server-addr: 10.102.192.30:6848 + server-addr: 10.102.192.31:6848 # 测试环境配置 容器名+端口号 # server-addr: 10.33.0.129:6010 # 线上测试环境配置 容器名+端口号 @@ -52,4 +52,4 @@ sso: AwIDAQAB -----END PUBLIC KEY----- # SSO登录成功后跳转的前端页面地址 - redirect-url: "/" + redirect-url: "https://diglog.namkwong.com.mo/main_app/sso" diff --git a/mhd-modules/mhd-system/src/main/java/com/mhd/basic/domain/storageSection/service/StorageSectionDomainService.java b/mhd-modules/mhd-system/src/main/java/com/mhd/basic/domain/storageSection/service/StorageSectionDomainService.java index db2ba5435..5fbe57933 100644 --- a/mhd-modules/mhd-system/src/main/java/com/mhd/basic/domain/storageSection/service/StorageSectionDomainService.java +++ b/mhd-modules/mhd-system/src/main/java/com/mhd/basic/domain/storageSection/service/StorageSectionDomainService.java @@ -91,31 +91,15 @@ public class StorageSectionDomainService { public Boolean update(StorageSectionDO storageSectionDO) { LoginUser loginUser = SecurityUtils.getLoginUser(); Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId(); - AssociationWarehouseCacheDO associationWarehouseCacheDO = SystemServiceCacheUtil.getAssociationWarehouseCache(loginUser.getUserid()); - if (topOrganizationId == null || topOrganizationId == 0 || associationWarehouseCacheDO == null) { - throw new ServiceException("获取当前登陆人组织与登陆仓库失败!"); + if (topOrganizationId == null || topOrganizationId == 0) { + throw new ServiceException("获取当前登陆人组织失败!"); } - checkStorageSectionDuplicate(topOrganizationId, associationWarehouseCacheDO.getWarehouseId(), + // 直接用记录自身的 warehouseId 做唯一校验,不再依赖缓存仓库 + checkStorageSectionDuplicate(topOrganizationId, storageSectionDO.getWarehouseId(), storageSectionDO.getStorageCode(), storageSectionDO.getStorageName(), storageSectionDO.getStorageSectionId()); //设置仓库 setWarehouseInfo(storageSectionDO); - String storageCode = storageSectionDO.getStorageCode(); - String storageName = storageSectionDO.getStorageName(); - Long warehouseId = storageSectionDO.getWarehouseId(); - List codelist = storageSectionService.list(new QueryWrapper().lambda() - .eq(StorageSection::getStorageCode, storageCode) - .eq(StorageSection::getWarehouseId, warehouseId) - .ne(StorageSection::getStorageSectionId, storageSectionDO.getStorageSectionId()) - .eq(StorageSection::getDelFlag, 1)); - List namelist = storageSectionService.list(new QueryWrapper().lambda() - .eq(StorageSection::getStorageName, storageName) - .eq(StorageSection::getWarehouseId, warehouseId) - .ne(StorageSection::getStorageSectionId, storageSectionDO.getStorageSectionId()) - .eq(StorageSection::getDelFlag, 1)); - if ((codelist != null && codelist.size() > 0) || (namelist != null && namelist.size() > 0)) { - throw new ServiceException("库区编码或名称已存在"); - } return storageSectionService.update(storageSectionDO); } diff --git a/mhd-modules/mhd-system/src/main/resources/bootstrap.yml b/mhd-modules/mhd-system/src/main/resources/bootstrap.yml index 79f1a3df7..aa660b126 100644 --- a/mhd-modules/mhd-system/src/main/resources/bootstrap.yml +++ b/mhd-modules/mhd-system/src/main/resources/bootstrap.yml @@ -16,7 +16,7 @@ spring: # 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.30:6848 username: nacos password: manhuoda@2023 #线上正式环境 @@ -25,7 +25,7 @@ spring: # 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.30:6848 username: nacos password: manhuoda@2023 #线上正式环境 diff --git a/mhd-user-center/src/main/java/com/mhd/user/application/service/AgreementDetailsApplicationService.java b/mhd-user-center/src/main/java/com/mhd/user/application/service/AgreementDetailsApplicationService.java index 4b9703459..99d56c911 100644 --- a/mhd-user-center/src/main/java/com/mhd/user/application/service/AgreementDetailsApplicationService.java +++ b/mhd-user-center/src/main/java/com/mhd/user/application/service/AgreementDetailsApplicationService.java @@ -1,12 +1,22 @@ package com.mhd.user.application.service; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.mhd.common.security.utils.SecurityUtils; import com.mhd.user.domain.userAggregate.entity.AgreementDetails; +import com.mhd.user.domain.userAggregate.entity.UserShipperEntity; import com.mhd.user.domain.userAggregate.repository.mapper.AgreementDetailsMapper; +import com.mhd.user.domain.userAggregate.repository.mapper.UserShipperMapper; +import com.mhd.user.domain.userAggregate.repository.po.AgreementDetailsPo; +import com.mhd.system.api.model.LoginUser; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -19,18 +29,87 @@ public class AgreementDetailsApplicationService { @Resource private AgreementDetailsMapper agreementDetailsMapper; + @Resource + private UserShipperMapper userShipperMapper; + /** - * 根据货主ID查询所有协议明细 + * 根据货主ID查询当前登录用户组织下的协议明细 + * 从user_shipper表的orgCode字段解析JSON,匹配当前登录用户组织code, + * 返回该组织下的agreedaydatas列表 * * @param shipperId 货主ID * @return 协议明细列表 */ - public List listByShipperId(Long shipperId) { - return agreementDetailsMapper.selectList( - new LambdaQueryWrapper() - .eq(AgreementDetails::getShipperId, shipperId) - .orderByDesc(AgreementDetails::getCreateTime) - ); + public List listByShipperId(Long shipperId) { + // 1. 获取当前登录用户组织ID + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (ObjectUtil.isNull(loginUser) || ObjectUtil.isNull(loginUser.getUserPo())) { + log.warn("未获取到登录用户信息"); + return Collections.emptyList(); + } + Long organizationId = loginUser.getUserPo().getOrganizationId(); + if (ObjectUtil.isNull(organizationId)) { + log.warn("当前登录用户无组织信息"); + return Collections.emptyList(); + } + + // 2. 根据shipperId查询货主信息,获取orgCode字段 + UserShipperEntity shipperEntity = userShipperMapper.selectById(shipperId); + if (ObjectUtil.isNull(shipperEntity)) { + log.warn("未找到货主信息,shipperId={}", shipperId); + return Collections.emptyList(); + } + String orgCodeJson = shipperEntity.getOrgCode(); + if (ObjectUtil.isNull(orgCodeJson) || orgCodeJson.isEmpty()) { + log.warn("货主orgCode为空,shipperId={}", shipperId); + return Collections.emptyList(); + } + + // 3. 解析orgCode JSON数组,匹配当前登录用户组织code + JSONArray orgCodeArray = JSONArray.parseArray(orgCodeJson); + if (orgCodeArray == null || orgCodeArray.isEmpty()) { + return Collections.emptyList(); + } + + JSONObject matchedOrg = null; + for (int i = 0; i < orgCodeArray.size(); i++) { + JSONObject orgItem = orgCodeArray.getJSONObject(i); + Long code = orgItem.getLong("code"); + if (organizationId.equals(code)) { + matchedOrg = orgItem; + break; + } + } + + if (matchedOrg == null) { + log.info("未匹配到当前组织的协议信息,shipperId={},organizationId={}", shipperId, organizationId); + return Collections.emptyList(); + } + + // 4. 获取匹配组织的agreedaydatas + JSONArray agreedaydatas = matchedOrg.getJSONArray("agreedaydatas"); + if (agreedaydatas == null || agreedaydatas.isEmpty()) { + log.info("当前组织下无协议天数数据,shipperId={},organizationId={}", shipperId, organizationId); + return Collections.emptyList(); + } + + // 5. 转换为AgreementDetailsPo列表 + List result = new ArrayList<>(); + for (int i = 0; i < agreedaydatas.size(); i++) { + JSONObject item = agreedaydatas.getJSONObject(i); + AgreementDetailsPo po = new AgreementDetailsPo(); + po.setCode(item.getString("code")); + po.setOrgcode(item.getString("orgcode")); + po.setPtypename(item.getString("ptypename")); + po.setPtype(item.getString("ptype")); + po.setPcode(item.getString("pcode")); + po.setPname(item.getString("pname")); + po.setPday(item.getString("pday")); + result.add(po); + } + + log.info("查询协议明细成功,shipperId={},organizationId={},共{}条", shipperId, organizationId, result.size()); + return result; } /** diff --git a/mhd-user-center/src/main/java/com/mhd/user/domain/userAggregate/repository/po/AgreementDetailsPo.java b/mhd-user-center/src/main/java/com/mhd/user/domain/userAggregate/repository/po/AgreementDetailsPo.java new file mode 100644 index 000000000..a01334043 --- /dev/null +++ b/mhd-user-center/src/main/java/com/mhd/user/domain/userAggregate/repository/po/AgreementDetailsPo.java @@ -0,0 +1,32 @@ +package com.mhd.user.domain.userAggregate.repository.po; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 协议天数PO + */ +@Data +public class AgreementDetailsPo { + + @ApiModelProperty("客户编码") + private String code; + + @ApiModelProperty("协议组织编码") + private String orgcode; + + @ApiModelProperty("协议类型名称") + private String ptypename; + + @ApiModelProperty("协议类型:1-付款协议天数,2-收款协议天数") + private String ptype; + + @ApiModelProperty("财务系统协议编码") + private String pcode; + + @ApiModelProperty("协议天数名称") + private String pname; + + @ApiModelProperty("协议天数") + private String pday; +} diff --git a/mhd-user-center/src/main/resources/bootstrap.yml b/mhd-user-center/src/main/resources/bootstrap.yml index 6bf4ff00a..dd13bfba5 100644 --- a/mhd-user-center/src/main/resources/bootstrap.yml +++ b/mhd-user-center/src/main/resources/bootstrap.yml @@ -21,14 +21,14 @@ spring: # server-addr: 127.0.0.1:8848 # 线上测试环境配置 容器名+端口号 # server-addr: 10.33.0.129:6010 - server-addr: 10.102.192.30:6848 + server-addr: 10.102.192.31:6848 username: nacos password: manhuoda@2023 config: # server-addr: 127.0.0.1:8848 # 线上测试环境配置 容器名+端口号 # server-addr: 10.33.0.129:6010 - server-addr: 10.102.192.30:6848 + server-addr: 10.102.192.31:6848 username: nacos password: manhuoda@2023 group: DEFAULT_GROUP # 默认分组就是DEFAULT_GROUP,如果使用默认分组可以不配置 diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reservationStockInOrder/repository/persistence/ReservationStockInOrderImpl.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reservationStockInOrder/repository/persistence/ReservationStockInOrderImpl.java index af18fa80b..93070d2f4 100644 --- a/mhd_oms/src/main/java/com/mhd/oms/domain/reservationStockInOrder/repository/persistence/ReservationStockInOrderImpl.java +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reservationStockInOrder/repository/persistence/ReservationStockInOrderImpl.java @@ -271,9 +271,25 @@ public class ReservationStockInOrderImpl extends ServiceImpl接口层调用此方法,将文件保存到临时目录后立即返回"异步处理中", + * 实际导入由 {@link #doImportData(File, String, LoginUser)} 异步执行, + * 避免大数据量时接口超时。

+ */ + public AjaxResult importDataAsync(MultipartFile file) { + if (file == null || file.isEmpty()) { + throw new ServiceException("上传文件不能为空"); + } + String fileName = file.getOriginalFilename(); + if (org.apache.commons.lang3.StringUtils.isBlank(fileName) + || (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx"))) { + throw new ServiceException("请上传xls或xlsx格式文件"); + } + + // 流式写入临时文件,避免大文件 getBytes() 导致内存溢出和接口超时 + String tmpDir = System.getProperty("java.io.tmpdir") + File.separator + "material_import"; + File dir = new File(tmpDir); + if (!dir.exists()) { + dir.mkdirs(); + } + String tmpFileName = System.currentTimeMillis() + "_" + fileName; + File tmpFile = new File(dir, tmpFileName); + try { + file.transferTo(tmpFile); + } catch (Exception e) { + throw new ServiceException("文件保存失败:" + e.getMessage()); + } + + LoginUser loginUser = SecurityUtils.getLoginUser(); + // 通过独立的异步 Service 执行导入(避免类内部 self-invocation 导致 @Async 失效) + materialBaseInfoAsyncService.doImportData(tmpFile, fileName, loginUser); + + return AjaxResult.success("导入任务已提交,正在后台异步处理,请稍后查看导入结果日志"); + } + + /** + * 导入物料主数据(仅导入主表字段)- 原同步方法保留,供其他场景调用 */ @Transactional(rollbackFor = Exception.class) public AjaxResult importData(MultipartFile file) { @@ -496,7 +542,6 @@ public class MaterialBaseInfoApplicationService { row.setHsCode(row.getHsCodeImport().trim()); } normalizeImportBarCodeWhenDuplicateOfHs(row); - // 客户模板通常只有「货主名称」无「货主id」:须先按名称解析货主,再回填组织;勿提前写入登录组织以免组织校验拦截货主匹配 fillIdsFromExistingMasterData(row); fillOrganizationFromShipperIfNeeded(row); if (!isValidOrgId(row.getOrganizationId()) && isValidOrgId(loginOrgId)) { @@ -567,25 +612,20 @@ public class MaterialBaseInfoApplicationService { materialBaseInfoDO.setMaterialCommon(null); materialBaseInfoDO.setMaterialWarehouseControl(null); materialBaseInfoDO.setMaterialBarCodeList(buildImportBarCodeList(row)); - // 商品规则由 buildImportMaterialGoodsRule + insert() 统一写入,勿再单独 insert,避免 MATERIAL_GOODS_RULE 重复 insert(materialBaseInfoDO); Long materialBaseInfoId = materialBaseInfoDO.getMaterialBaseInfoId(); - //公共信息 MaterialCommonDO materialCommonDO = materialBaseInfoDO.getMaterialCommon(); if (materialCommonDO != null) { materialCommonDO.setMaterialBaseInfoId(materialBaseInfoId); materialCommonDomainService.insert(materialCommonDO); } - //仓库控制信息 MaterialWarehouseControlDO materialWarehouseControlDO = materialBaseInfoDO.getMaterialWarehouseControl(); if (materialWarehouseControlDO != null) { materialWarehouseControlDO.setMaterialBaseInfoId(materialBaseInfoId); materialWarehouseControlDomainService.insert(materialWarehouseControlDO); } - //库存预警 materialInventoryWarningDomainService.batchInsertOrUpdate(materialBaseInfoDO); - //条码信息 materialBarCodeDomainService.batchInsertOrUpdate(materialBaseInfoDO); successCount++; } @@ -596,10 +636,11 @@ public class MaterialBaseInfoApplicationService { return AjaxResult.success("导入成功,共" + successCount + "条"); } + /** * 使用已存在主数据按名称/编码反查并回填ID */ - private void fillIdsFromExistingMasterData(MaterialBaseInfoDTO row) { + public void fillIdsFromExistingMasterData(MaterialBaseInfoDTO row) { if (StringUtils.isBlank(row.getPackName()) && StringUtils.isNotBlank(row.getPackImport())) { row.setPackName(row.getPackImport().trim()); } @@ -818,10 +859,14 @@ public class MaterialBaseInfoApplicationService { Object userCode = shipper.get("userCode"); Object userMemberCode = shipper.get("userMemberCode"); Object entName = shipper.get("shipperEnterpriseName"); - if (userName == null) { + // userName 可能为空字符串而非 null,需同时判断空字符串 + if (userName == null || String.valueOf(userName).trim().isEmpty()) { + userName = shipper.get("userNameShipper"); + } + if (userName == null || String.valueOf(userName).trim().isEmpty()) { userName = shipper.get("name"); } - if (userName == null) { + if (userName == null || String.valueOf(userName).trim().isEmpty()) { userName = shipper.get("shipperName"); } String normalizedName = StringUtils.isNotBlank(shipperName) ? shipperName : ""; @@ -919,7 +964,7 @@ public class MaterialBaseInfoApplicationService { /** * Excel 已填货主 ID、但未填组织时,从货主用户补全组织信息。 */ - private void fillOrganizationFromShipperIfNeeded(MaterialBaseInfoDTO row) { + public void fillOrganizationFromShipperIfNeeded(MaterialBaseInfoDTO row) { if (isValidOrgId(row.getOrganizationId()) || row.getShipperId() == null) { return; } @@ -978,7 +1023,7 @@ public class MaterialBaseInfoApplicationService { } } - private void fillOrganizationName(MaterialBaseInfoDTO row, LoginUser loginUser) { + public void fillOrganizationName(MaterialBaseInfoDTO row, LoginUser loginUser) { if (StringUtils.isNotBlank(row.getOrganizationName())) { return; } @@ -998,7 +1043,7 @@ public class MaterialBaseInfoApplicationService { } } - private void fillClassifyTypeAndUnit(MaterialBaseInfoDTO row) { + public void fillClassifyTypeAndUnit(MaterialBaseInfoDTO row) { normalizeMaterialClassifyImportFields(row); // 按组织从 system 主数据反查分类编码,与名称列表对齐 fillMaterialClassifyCodesFromSystem(row); @@ -1179,7 +1224,7 @@ public class MaterialBaseInfoApplicationService { * 导入约定:「HS码/HS编码」与「物料条形码」为不同列。模板表头错位或两列误填相同 HS 时, * barCode 会与 hsCode 相同;此时保留 hsCode,清空主表/子表条码,避免 HS 写入 bar_code。 */ - private void normalizeImportBarCodeWhenDuplicateOfHs(MaterialBaseInfoDTO row) { + public void normalizeImportBarCodeWhenDuplicateOfHs(MaterialBaseInfoDTO row) { if (row == null) { return; } @@ -1194,7 +1239,7 @@ public class MaterialBaseInfoApplicationService { } } - private List buildImportBarCodeList(MaterialBaseInfoDTO row) { + public List buildImportBarCodeList(MaterialBaseInfoDTO row) { if (StringUtils.isBlank(row.getBarCode())) { return null; } @@ -1234,7 +1279,7 @@ public class MaterialBaseInfoApplicationService { return idObj == null ? null : Long.valueOf(String.valueOf(idObj)); } - private MaterialGoodsRuleDO buildImportMaterialGoodsRule(MaterialBaseInfoDTO row) { + public MaterialGoodsRuleDO buildImportMaterialGoodsRule(MaterialBaseInfoDTO row) { String batchName = StringUtils.isNotBlank(row.getBatchRuleNameImport()) ? row.getBatchRuleNameImport().trim() : (StringUtils.isNotBlank(row.getBatchName()) @@ -1309,7 +1354,7 @@ public class MaterialBaseInfoApplicationService { .trim(); } - private List> extractTableDataRows(Map resultMap) { + public List> extractTableDataRows(Map resultMap) { if (resultMap == null) { return new ArrayList<>(); } diff --git a/mhd_wms/src/main/java/com/mhd/wms/application/service/materialBaseInfo/MaterialBaseInfoAsyncService.java b/mhd_wms/src/main/java/com/mhd/wms/application/service/materialBaseInfo/MaterialBaseInfoAsyncService.java new file mode 100644 index 000000000..5a3554478 --- /dev/null +++ b/mhd_wms/src/main/java/com/mhd/wms/application/service/materialBaseInfo/MaterialBaseInfoAsyncService.java @@ -0,0 +1,274 @@ +package com.mhd.wms.application.service.materialBaseInfo; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mhd.common.core.domain.po.UserPo; +import com.mhd.common.core.web.domain.AjaxResult; +import com.mhd.system.api.SystemServiceFeign; +import com.mhd.system.api.UserServiceFeign; +import com.mhd.system.api.model.LoginUser; +import com.mhd.wms.domain.materialBarCode.service.MaterialBarCodeDomainService; +import com.mhd.wms.domain.materialBaseInfo.entity.MaterialBaseInfo; +import com.mhd.wms.domain.materialBaseInfo.repository.facade.IMaterialBaseInfoService; +import com.mhd.wms.domain.materialBaseInfo.repository.mapper.MaterialBaseInfoMapper; +import com.mhd.wms.domain.materialBaseInfo.repository.todo.MaterialBaseInfoDO; +import com.mhd.wms.domain.materialBaseInfo.service.MaterialBaseInfoDomainService; +import com.mhd.wms.domain.materialGoodsRule.service.MaterialGoodsRuleDomainService; +import com.mhd.wms.domain.materialCommon.service.MaterialCommonDomainService; +import com.mhd.wms.domain.materialCommon.repository.todo.MaterialCommonDO; +import com.mhd.wms.domain.materialInventoryWarning.service.MaterialInventoryWarningDomainService; +import com.mhd.wms.domain.materialWarehouseControl.service.MaterialWarehouseControlDomainService; +import com.mhd.wms.domain.materialWarehouseControl.repository.todo.MaterialWarehouseControlDO; +import com.mhd.wms.interfaces.dto.materialBaseInfo.MaterialBaseInfoDTO; +import com.mhd.wms.util.MaterialBaseInfoImportExcelUtil; +import com.mhd.wms.util.ZhTextNormalizeUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 物料主数据异步导入服务 + */ +@Slf4j +@Service +public class MaterialBaseInfoAsyncService { + + @Autowired + private IMaterialBaseInfoService materialBaseInfoService; + @Autowired + private MaterialBaseInfoMapper materialBaseInfoMapper; + @Autowired + private SystemServiceFeign systemServiceFeign; + @Autowired + private UserServiceFeign userServiceFeign; + @Autowired + private MaterialCommonDomainService materialCommonDomainService; + @Autowired + private MaterialWarehouseControlDomainService materialWarehouseControlDomainService; + @Autowired + private MaterialInventoryWarningDomainService materialInventoryWarningDomainService; + @Autowired + private MaterialBarCodeDomainService materialBarCodeDomainService; + @Autowired + private MaterialBaseInfoDomainService materialBaseInfoDomainService; + @Autowired + private com.mhd.wms.domain.materialGoodsRule.service.MaterialGoodsRuleDomainService materialGoodsRuleDomainService; + @org.springframework.context.annotation.Lazy + @Autowired + private MaterialBaseInfoApplicationService materialBaseInfoApplicationService; + + @Async + public void doImportData(File tmpFile, String fileName, LoginUser loginUser) { + long startTime = System.currentTimeMillis(); + log.info("=== 物料主数据异步导入开始,文件:{},大小:{}KB ===", fileName, tmpFile.length() / 1024); + try { + List rows; + try (InputStream is = new FileInputStream(tmpFile)) { + rows = MaterialBaseInfoImportExcelUtil.importRows(is); + } catch (Exception e) { + log.error("物料主数据异步导入失败 - 文件解析异常:{}", e.getMessage(), e); + return; + } + if (CollectionUtils.isEmpty(rows)) { + log.warn("物料主数据异步导入结束 - 导入数据为空"); + return; + } + + Long loginOrgId = null; + Long loginTopOrgId = null; + if (loginUser != null && loginUser.getUserPo() != null) { + loginOrgId = loginUser.getUserPo().getOrganizationId(); + loginTopOrgId = loginUser.getUserPo().getTopOrganizationId(); + } + + StringBuilder errors = new StringBuilder(); + Set duplicateInFile = new HashSet<>(); + int successCount = 0; + int totalCount = rows.size(); + Map> packCache = new HashMap<>(); + + for (int i = 0; i < rows.size(); i++) { + MaterialBaseInfoDTO row = rows.get(i); + int rowNum = i + 2; + if (row == null) { + continue; + } + try { + if (StringUtils.isBlank(row.getMaterialCode())) { + errors.append("第").append(rowNum).append("行:物料编码不能为空;"); + continue; + } + if (StringUtils.isBlank(row.getMaterialName())) { + errors.append("第").append(rowNum).append("行:物料名称不能为空;"); + continue; + } + if (row.getNullify() == null) { + row.setNullify(2); + } + if (row.getCommon() == null) { + row.setCommon(2); + } + if (StringUtils.isBlank(row.getHsCode()) && StringUtils.isNotBlank(row.getHsCodeImport())) { + row.setHsCode(row.getHsCodeImport().trim()); + } + materialBaseInfoApplicationService.normalizeImportBarCodeWhenDuplicateOfHs(row); + materialBaseInfoApplicationService.fillIdsFromExistingMasterData(row); + materialBaseInfoApplicationService.fillOrganizationFromShipperIfNeeded(row); + if (!isValidOrgId(row.getOrganizationId()) && isValidOrgId(loginOrgId)) { + row.setOrganizationId(loginOrgId); + } + if (row.getTopOrganizationId() == null && isValidOrgId(loginTopOrgId)) { + row.setTopOrganizationId(loginTopOrgId); + } + materialBaseInfoApplicationService.fillOrganizationName(row, loginUser); + + String shipperCode = StringUtils.defaultString(row.getShipperCode(), ""); + String duplicateKey = row.getOrganizationId() + "|" + shipperCode + "|" + row.getMaterialCode(); + if (!duplicateInFile.add(duplicateKey)) { + errors.append("第").append(rowNum).append("行:文件内存在重复物料;"); + continue; + } + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .eq(MaterialBaseInfo::getDelFlag, 1) + .eq(MaterialBaseInfo::getOrganizationId, row.getOrganizationId()) + .eq(MaterialBaseInfo::getMaterialCode, row.getMaterialCode()); + if (StringUtils.isBlank(row.getShipperCode())) { + wrapper.and(w -> w.isNull(MaterialBaseInfo::getShipperCode).or().eq(MaterialBaseInfo::getShipperCode, "")); + } else { + wrapper.eq(MaterialBaseInfo::getShipperCode, row.getShipperCode()); + } + if (materialBaseInfoService.count(wrapper) > 0) { + errors.append("第").append(rowNum).append("行:物料已存在;"); + continue; + } + materialBaseInfoApplicationService.fillClassifyTypeAndUnit(row); + + // 包装规格查询(带缓存) + String packCacheKey = row.getOrganizationId() + "|" + StringUtils.defaultString(row.getPackName()); + Map cachedPack = packCache.get(packCacheKey); + if (cachedPack == null) { + Object result = systemServiceFeign.getPackList(null, row.getPackName(), row.getOrganizationId()); + if (result == null) { + packCache.put(packCacheKey, Collections.emptyMap()); + errors.append("第").append(rowNum).append("行:包装规格查询无返回;"); + continue; + } + Map resultMap = JSON.parseObject(JSONObject.toJSONString(result), new TypeReference>() {}); + List> rowsPack = extractTableDataRows(resultMap); + if (CollectionUtils.isEmpty(rowsPack)) { + packCache.put(packCacheKey, Collections.emptyMap()); + errors.append("第").append(rowNum).append("行:包装规格不存在;"); + continue; + } + cachedPack = rowsPack.get(0); + packCache.put(packCacheKey, cachedPack); + } + if (cachedPack.isEmpty()) { + errors.append("第").append(rowNum).append("行:包装规格不存在(缓存);"); + continue; + } + String packCode = cachedPack.get("packCode") == null ? "" : String.valueOf(cachedPack.get("packCode")); + row.setPackCode(packCode); + String packId = cachedPack.get("packId") == null ? "" : String.valueOf(cachedPack.get("packId")); + if (StringUtils.isBlank(packId) || !StringUtils.isNumeric(packId)) { + errors.append("第").append(rowNum).append("行:包装规格数据异常;"); + continue; + } + row.setPackId(Long.valueOf(packId)); + String unitName = cachedPack.get("unitName") == null ? "" : String.valueOf(cachedPack.get("unitName")); + row.setUnitCode("EA"); + row.setUnitName(unitName); + + MaterialBaseInfoDO materialBaseInfoDO = new MaterialBaseInfoDO(); + BeanUtils.copyProperties(row, materialBaseInfoDO); + materialBaseInfoDO.setMaterialGoodsRule(materialBaseInfoApplicationService.buildImportMaterialGoodsRule(row)); + materialBaseInfoDO.setMaterialCommon(null); + materialBaseInfoDO.setMaterialWarehouseControl(null); + materialBaseInfoDO.setMaterialBarCodeList(materialBaseInfoApplicationService.buildImportBarCodeList(row)); + // 补全创建人信息(异步线程没有 SecurityContext,用 loginUser) + if (loginUser != null) { + materialBaseInfoDO.setCreateBy(loginUser.getUserid()); + materialBaseInfoDO.setCreateByName(loginUser.getUsername()); + } + materialBaseInfoDO.setCreateTime(new Date()); + // 调用原 ApplicationService 的 insert,走完整的插入逻辑(含商品规则、公共信息、NC编码等) + materialBaseInfoApplicationService.insert(materialBaseInfoDO); + successCount++; + } catch (Exception e) { + log.error("物料主数据异步导入 - 第{}行处理异常", rowNum, e); + errors.append("第").append(rowNum).append("行:处理异常-").append(e.getMessage()).append(";"); + } + } + + long costTime = System.currentTimeMillis() - startTime; + if (errors.length() > 0) { + log.warn("=== 物料主数据异步导入完成,文件:{},总数:{},成功:{},失败:{},耗时:{}ms,失败原因:{} ===", + fileName, totalCount, successCount, totalCount - successCount, costTime, errors); + } else { + log.info("=== 物料主数据异步导入完成,文件:{},总数:{},成功:{},耗时:{}ms ===", + fileName, totalCount, successCount, costTime); + } + } finally { + try { + if (tmpFile.exists()) { + tmpFile.delete(); + } + } catch (Exception e) { + log.warn("物料主数据异步导入 - 临时文件清理失败:{}", e.getMessage()); + } + } + } + + // ====== 以下工具方法 ====== + + private List> extractTableDataRows(Map resultMap) { + if (resultMap == null) { + return Collections.emptyList(); + } + Object data = resultMap.get("data"); + if (data == null) { + data = resultMap.get("rows"); + } + if (data instanceof List) { + @SuppressWarnings("unchecked") + List> list = (List>) data; + return list; + } + if (data instanceof Map) { + @SuppressWarnings("unchecked") + Map dataMap = (Map) data; + Object innerData = dataMap.get("data"); + if (innerData instanceof List) { + @SuppressWarnings("unchecked") + List> list = (List>) innerData; + return list; + } + Object rows = dataMap.get("rows"); + if (rows instanceof List) { + @SuppressWarnings("unchecked") + List> list = (List>) rows; + return list; + } + } + return Collections.emptyList(); + } + + private boolean isValidOrgId(Long id) { + return id != null && id > 0; + } +} diff --git a/mhd_wms/src/main/java/com/mhd/wms/application/service/stockOutOrder/StockOutOrderApplicationService.java b/mhd_wms/src/main/java/com/mhd/wms/application/service/stockOutOrder/StockOutOrderApplicationService.java index 9b91ca0c4..287e1d242 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/application/service/stockOutOrder/StockOutOrderApplicationService.java +++ b/mhd_wms/src/main/java/com/mhd/wms/application/service/stockOutOrder/StockOutOrderApplicationService.java @@ -282,9 +282,6 @@ public class StockOutOrderApplicationService { stockOutOrderPO.setMaterialQuantity(cnt); } - BigDecimal checkSum = (mag != null && mag.getCheckQuantitySum() != null) - ? mag.getCheckQuantitySum() : BigDecimal.ZERO; - stockOutOrderPO.setCheckQuantity(checkSum); stockOutOrderPO.setReceiptOrderAccountList( accountByOrderId.getOrDefault(stockOutOrderPO.getOutOrderId(), Collections.emptyList())); } diff --git a/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/persistence/MaterialInventoryImpl.java b/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/persistence/MaterialInventoryImpl.java index eab84edbf..8b25a0600 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/persistence/MaterialInventoryImpl.java +++ b/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/persistence/MaterialInventoryImpl.java @@ -819,7 +819,12 @@ public class MaterialInventoryImpl extends ServiceImpl数据量大时同步导入会导致接口超时,改为异步模式: + * 接口立即返回"已提交异步处理",后台线程执行实际导入, + * 导入结果通过日志输出。

*/ - @ApiOperation("导入物料主数据") + @ApiOperation("导入物料主数据(异步)") @PostMapping("/importData") public AjaxResult importData(@RequestParam("file") MultipartFile file) { - return materialBaseInfoApplicationService.importData(file); + return materialBaseInfoApplicationService.importDataAsync(file); } @ApiOperation("物料管理更新推送信息") diff --git a/mhd_wms/src/main/java/com/mhd/wms/util/MaterialBaseInfoImportExcelUtil.java b/mhd_wms/src/main/java/com/mhd/wms/util/MaterialBaseInfoImportExcelUtil.java index 678a72e6d..206e35fb2 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/util/MaterialBaseInfoImportExcelUtil.java +++ b/mhd_wms/src/main/java/com/mhd/wms/util/MaterialBaseInfoImportExcelUtil.java @@ -36,6 +36,11 @@ public final class MaterialBaseInfoImportExcelUtil { HEADER_ALIASES.put("淨重(kg)", "净重 单位:千克"); HEADER_ALIASES.put("体积(m³)", "体积 单位:立方米"); HEADER_ALIASES.put("體積(m³)", "体积 单位:立方米"); + HEADER_ALIASES.put("长", "物料大小-长 单位:米"); + HEADER_ALIASES.put("宽", "物料大小-宽 单位:米"); + HEADER_ALIASES.put("高", "物料大小-高 单位:米"); + HEADER_ALIASES.put("公司名称", "货主名称"); + HEADER_ALIASES.put("公司名稱", "货主名称"); } public static List importRows(InputStream is) throws Exception {