diff --git a/wms-module-system/pom.xml b/wms-module-system/pom.xml
index 64845da..05be82f 100644
--- a/wms-module-system/pom.xml
+++ b/wms-module-system/pom.xml
@@ -19,4 +19,4 @@
wms-common
-
\ No newline at end of file
+
diff --git a/wms-module-system/src/main/java/top/wms/admin/material/mapper/MaterialInfoMapper.java b/wms-module-system/src/main/java/top/wms/admin/material/mapper/MaterialInfoMapper.java
index dd54d25..75ed192 100644
--- a/wms-module-system/src/main/java/top/wms/admin/material/mapper/MaterialInfoMapper.java
+++ b/wms-module-system/src/main/java/top/wms/admin/material/mapper/MaterialInfoMapper.java
@@ -15,5 +15,6 @@ import java.util.List;
@Repository
public interface MaterialInfoMapper extends BaseMapper {
public int updateByName(List list);
+
public int updateByCode(List list);
}
diff --git a/wms-module-system/src/main/java/top/wms/admin/material/model/resp/MaterialInfoImportResp.java b/wms-module-system/src/main/java/top/wms/admin/material/model/resp/MaterialInfoImportResp.java
index 0ac98e8..37a5eca 100644
--- a/wms-module-system/src/main/java/top/wms/admin/material/model/resp/MaterialInfoImportResp.java
+++ b/wms-module-system/src/main/java/top/wms/admin/material/model/resp/MaterialInfoImportResp.java
@@ -1,6 +1,5 @@
package top.wms.admin.material.model.resp;
-
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
diff --git a/wms-module-system/src/main/java/top/wms/admin/material/service/MaterialInfoService.java b/wms-module-system/src/main/java/top/wms/admin/material/service/MaterialInfoService.java
index 1c394cf..6644972 100644
--- a/wms-module-system/src/main/java/top/wms/admin/material/service/MaterialInfoService.java
+++ b/wms-module-system/src/main/java/top/wms/admin/material/service/MaterialInfoService.java
@@ -10,9 +10,6 @@ import top.wms.admin.material.model.req.MaterialInfoReq;
import top.wms.admin.material.model.resp.MaterialImportParseResp;
import top.wms.admin.material.model.resp.MaterialInfoImportResp;
import top.wms.admin.material.model.resp.MaterialInfoResp;
-import top.wms.admin.system.model.req.user.UserImportReq;
-import top.wms.admin.system.model.resp.user.UserImportParseResp;
-import top.wms.admin.system.model.resp.user.UserImportResp;
import java.io.IOException;
@@ -24,33 +21,38 @@ import java.io.IOException;
*/
public interface MaterialInfoService extends BaseService {
- /*
- *
- * 根据编码查询物料信息
- * */
- public MaterialInfoDO getMaterialInfoByCode(String code);
+ /*
+ *
+ * 根据编码查询物料信息
+ * */
+ public MaterialInfoDO getMaterialInfoByCode(String code);
- /**
- * 下载导入模板
- *
- * @param response 响应对象
- * @throws IOException /
- */
- void downloadImportTemplate(HttpServletResponse response) throws IOException;
+ /**
+ * 下载导入模板
+ *
+ * @param response 响应对象
+ * @throws IOException /
+ */
+ void downloadImportTemplate(HttpServletResponse response) throws IOException;
- /**
- * 解析导入数据
- *
- * @param file 导入文件
- * @return 解析结果
- */
- MaterialImportParseResp parseImport(MultipartFile file);
+ /**
+ * 解析导入数据
+ *
+ * @param file 导入文件
+ * @return 解析结果
+ */
+ MaterialImportParseResp parseImport(MultipartFile file);
- /**
- * 导入数据
- *
- * @param req 导入信息
- * @return 导入结果
- */
- MaterialInfoImportResp importMaterial(MaterialInfoImportReq req);
+ /**
+ * 导入数据
+ *
+ * @param req 导入信息
+ * @return 导入结果
+ */
+ MaterialInfoImportResp importMaterial(MaterialInfoImportReq req);
+
+ /*
+ * 照片批量上传处理
+ * */
+ void uploadMaterialPhotos(MultipartFile file);
}
diff --git a/wms-module-system/src/main/java/top/wms/admin/material/service/impl/MaterialInfoServiceImpl.java b/wms-module-system/src/main/java/top/wms/admin/material/service/impl/MaterialInfoServiceImpl.java
index 2af22fb..cfdccdd 100644
--- a/wms-module-system/src/main/java/top/wms/admin/material/service/impl/MaterialInfoServiceImpl.java
+++ b/wms-module-system/src/main/java/top/wms/admin/material/service/impl/MaterialInfoServiceImpl.java
@@ -2,40 +2,35 @@ package top.wms.admin.material.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.lang.UUID;
-import cn.hutool.core.math.MathUtil;
import cn.hutool.core.util.*;
import cn.hutool.extra.validation.ValidationUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
-import com.baomidou.mybatisplus.core.conditions.Wrapper;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import jakarta.servlet.http.HttpServletResponse;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
-import me.ahoo.cosid.IdGenerator;
-import me.ahoo.cosid.provider.DefaultIdGeneratorProvider;
+import lombok.extern.slf4j.Slf4j;
import net.dreamlu.mica.core.result.R;
+import org.dromara.x.file.storage.core.FileInfo;
+import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import top.continew.starter.cache.redisson.util.RedisUtils;
import top.continew.starter.core.exception.BusinessException;
-import top.continew.starter.core.util.SpringUtils;
import top.continew.starter.core.validation.CheckUtils;
import top.continew.starter.extension.crud.service.BaseServiceImpl;
import top.continew.starter.web.util.FileUploadUtils;
import top.wms.admin.common.constant.CacheConstants;
import top.wms.admin.common.context.UserContextHolder;
-import top.wms.admin.common.enums.GenderEnum;
import top.wms.admin.common.util.SecureUtils;
import top.wms.admin.material.mapper.MaterialInfoMapper;
import top.wms.admin.material.model.entity.MaterialInfoDO;
@@ -47,23 +42,19 @@ import top.wms.admin.material.model.resp.MaterialImportParseResp;
import top.wms.admin.material.model.resp.MaterialInfoImportResp;
import top.wms.admin.material.model.resp.MaterialInfoResp;
import top.wms.admin.material.service.MaterialInfoService;
-import top.wms.admin.system.model.entity.DeptDO;
-import top.wms.admin.system.model.entity.RoleDO;
-import top.wms.admin.system.model.entity.UserDO;
-import top.wms.admin.system.model.entity.UserRoleDO;
-import top.wms.admin.system.model.req.user.UserImportReq;
-import top.wms.admin.system.model.req.user.UserImportRowReq;
-import top.wms.admin.system.model.resp.user.UserImportParseResp;
-import top.wms.admin.system.model.resp.user.UserImportResp;
+import top.wms.admin.system.service.FileService;
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.math.BigDecimal;
-import java.sql.Time;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
import static top.wms.admin.system.enums.ImportPolicyEnum.*;
import static top.wms.admin.system.enums.ImportPolicyEnum.SKIP;
@@ -76,13 +67,18 @@ import static top.wms.admin.system.enums.ImportPolicyEnum.SKIP;
*/
@Service
@RequiredArgsConstructor
+@Slf4j
public class MaterialInfoServiceImpl extends BaseServiceImpl implements MaterialInfoService {
+ private static final Set IMAGE_EXTENSIONS = Set.of("jpg", "jpeg", "png", "gif", "bmp");
+
+ private final FileService fileService;
+
@Override
public MaterialInfoDO getMaterialInfoByCode(String code) {
- if(StrUtil.isNotBlank(code)){
+ if (StrUtil.isNotBlank(code)) {
return baseMapper.lambdaQuery().eq(MaterialInfoDO::getEncoding, code).one();
- }else{
+ } else {
return null;
}
}
@@ -90,7 +86,8 @@ public class MaterialInfoServiceImpl extends BaseServiceImpl seenCode = new HashSet<>();
boolean hasDuplicateEncoding = validRowList.stream()
- .map(MaterialImportRowReq::getEncoding)
- .anyMatch(encoding -> encoding != null && !seenCode.add(encoding));
+ .map(MaterialImportRowReq::getEncoding)
+ .anyMatch(encoding -> encoding != null && !seenCode.add(encoding));
CheckUtils.throwIf(hasDuplicateEncoding, "存在重复物料编码,请检测数据");
// 查询重复用户
materialImportResp
- .setDuplicateNameRows(countExistByField(validRowList, MaterialImportRowReq::getMaterialName, MaterialInfoDO::getMaterialName, false));
+ .setDuplicateNameRows(countExistByField(validRowList, MaterialImportRowReq::getMaterialName, MaterialInfoDO::getMaterialName, false));
// 查询重复邮箱
materialImportResp
- .setDuplicateCodeRows(countExistByField(validRowList, MaterialImportRowReq::getEncoding, MaterialInfoDO::getEncoding, false));
+ .setDuplicateCodeRows(countExistByField(validRowList, MaterialImportRowReq::getEncoding, MaterialInfoDO::getEncoding, false));
// 设置导入会话并缓存数据,有效期10分钟
String importKey = UUID.fastUUID().toString(true);
RedisUtils.set(CacheConstants.DATA_IMPORT_KEY + importKey, JSONUtil.toJsonStr(validRowList), Duration
- .ofMinutes(10));
+ .ofMinutes(10));
materialImportResp.setImportKey(importKey);
return materialImportResp;
}
-
@Override
@Transactional(rollbackFor = Exception.class)
public MaterialInfoImportResp importMaterial(MaterialInfoImportReq req) {
@@ -160,22 +156,21 @@ public class MaterialInfoServiceImpl extends BaseServiceImpl existName = listExistByField(importMaterialList, MaterialImportRowReq::getMaterialName, MaterialInfoDO::getMaterialName);
List existCode = listExistByField(importMaterialList, MaterialImportRowReq::getEncoding, MaterialInfoDO::getEncoding);
- CheckUtils
- .throwIf(isExitImportMaterial(req, importMaterialList, existName, existCode), "数据不符合导入策略,已退出导入");
+ CheckUtils.throwIf(isExitImportMaterial(req, importMaterialList, existName, existCode), "数据不符合导入策略,已退出导入");
// 批量操作数据库集合
List insertList = new ArrayList<>();
List updateByNameList = new ArrayList<>();
List updateByCodeList = new ArrayList<>();
// ID生成器
-// IdGenerator idGenerator = DefaultIdGeneratorProvider.INSTANCE.getShare();
+ // IdGenerator idGenerator = DefaultIdGeneratorProvider.INSTANCE.getShare();
for (MaterialImportRowReq row : importMaterialList) {
if (isSkipMaterialImport(req, row, existName, existCode)) {
// 按规则跳过该行
continue;
}
MaterialInfoDO materialDO = BeanUtil.toBeanIgnoreError(row, MaterialInfoDO.class);
- materialDO.setUnitWeight(NumberUtil.isValidNumber(row.getUnitWeight())?row.getUnitWeight(): null);
- materialDO.setMaterialSpec(StrUtil.isNotBlank(row.getMaterialSpec())?row.getMaterialSpec():null);
+ materialDO.setUnitWeight(NumberUtil.isValidNumber(row.getUnitWeight()) ? row.getUnitWeight() : null);
+ materialDO.setMaterialSpec(StrUtil.isNotBlank(row.getMaterialSpec()) ? row.getMaterialSpec() : null);
// 修改 or 新增
if (UPDATE.validate(req.getDuplicateName(), row.getMaterialName(), existName)) {
materialDO.setMaterialName(row.getMaterialName());
@@ -188,7 +183,7 @@ public class MaterialInfoServiceImpl extends BaseServiceImpl materialRowList,
@@ -227,10 +217,9 @@ public class MaterialInfoServiceImpl extends BaseServiceImpllambdaQuery()
- .in(dbField, fieldEncrypt ? SecureUtils.encryptFieldByAes(fieldValues) : fieldValues));
+ .in(dbField, fieldEncrypt ? SecureUtils.encryptFieldByAes(fieldValues) : fieldValues));
}
-
/**
* 过滤无效的导入用户数据(批量导入不严格校验数据)
*
@@ -239,22 +228,23 @@ public class MaterialInfoServiceImpl extends BaseServiceImpl filterImportData(List importRowList) {
// 校验过滤
List list = importRowList.stream()
- .filter(row -> ValidationUtil.validate(row).isEmpty())
- .toList();
+ .filter(row -> ValidationUtil.validate(row).isEmpty())
+ .toList();
// 物料名去重
return list.stream()
- .collect(Collectors.toMap(MaterialImportRowReq::getMaterialName, row -> row, (existing, replacement) -> existing))
- .values()
- .stream()
- .toList();
+ .collect(Collectors.toMap(MaterialImportRowReq::getMaterialName, row -> row, (existing,
+ replacement) -> existing))
+ .values()
+ .stream()
+ .toList();
}
/**
* 按指定数据集获取数据库已存在内容
*
* @param materialRowList 导入的数据源
- * @param rowField 导入数据的字段
- * @param dbField 对比数据库的字段
+ * @param rowField 导入数据的字段
+ * @param dbField 对比数据库的字段
* @return 存在的内容
*/
private List listExistByField(List materialRowList,
@@ -265,54 +255,56 @@ public class MaterialInfoServiceImpl extends BaseServiceImpl materialDOList = baseMapper.selectList(Wrappers.lambdaQuery()
- .in(dbField, fieldValues)
- .select(dbField));
+ .in(dbField, fieldValues)
+ .select(dbField));
return materialDOList.stream().map(dbField).filter(Objects::nonNull).toList();
}
/**
* 判断是否退出导入
*
- * @param req 导入参数
- * @param list 导入数据
- * @param existName 导入数据中已存在的物料名
- * @param existCode 导入数据中已存在的物料编号
+ * @param req 导入参数
+ * @param list 导入数据
+ * @param existName 导入数据中已存在的物料名
+ * @param existCode 导入数据中已存在的物料编号
* @return 是否退出
*/
private boolean isExitImportMaterial(MaterialInfoImportReq req,
- List list,
- List existName,
- List existCode) {
+ List list,
+ List existName,
+ List existCode) {
return list.stream()
- .anyMatch(row -> EXIT.validate(req.getDuplicateName(), row.getMaterialName(), existName) || EXIT.validate(req
- .getDuplicateCode(), row.getEncoding(), existCode));
+ .anyMatch(row -> EXIT.validate(req.getDuplicateName(), row.getMaterialName(), existName) || EXIT
+ .validate(req.getDuplicateCode(), row.getEncoding(), existCode));
}
/**
* 判断是否跳过导入
*
- * @param req 导入参数
- * @param row 导入数据
- * @param existName 导入数据中已存在的物料名称
- * @param existCode 导入数据中已存在的物料编号
+ * @param req 导入参数
+ * @param row 导入数据
+ * @param existName 导入数据中已存在的物料名称
+ * @param existCode 导入数据中已存在的物料编号
* @return 是否跳过
*/
private boolean isSkipMaterialImport(MaterialInfoImportReq req,
- MaterialImportRowReq row,
- List existName,
- List existCode) {
+ MaterialImportRowReq row,
+ List existName,
+ List existCode) {
return SKIP.validate(req.getDuplicateName(), row.getMaterialName(), existName) || SKIP.validate(req
- .getDuplicateCode(), row.getEncoding(), existCode);
+ .getDuplicateCode(), row.getEncoding(), existCode);
}
/**
* 导入用户
*
- * @param insertList 新增用户
- * @param updateByNameList 修改用户
- * @param updateByCodeList 用户角色关联
+ * @param insertList 新增用户
+ * @param updateByNameList 修改用户
+ * @param updateByCodeList 用户角色关联
*/
- private void doImportMaterial(List insertList, List updateByNameList, List updateByCodeList) {
+ private void doImportMaterial(List insertList,
+ List updateByNameList,
+ List updateByCodeList) {
if (CollUtil.isNotEmpty(insertList)) {
baseMapper.insertBatch(insertList);
}
@@ -323,4 +315,102 @@ public class MaterialInfoServiceImpl extends BaseServiceImpl codeUrlMap = new HashMap<>();
+ // 物料照片存储路径(自定义,比如按日期分目录)
+ String photoStoragePath = "/" + DateUtil.today() + "/";
+
+ try (ZipInputStream zipInputStream = new ZipInputStream(zipFile.getInputStream())) {
+ ZipEntry zipEntry;
+ while ((zipEntry = zipInputStream.getNextEntry()) != null) {
+ // 跳过目录、非图片文件
+ if (zipEntry.isDirectory() || !isImageFile(zipEntry.getName())) {
+ zipInputStream.closeEntry();
+ continue;
+ }
+ // 2. 提取物料编码(照片名 = 物料编码,去掉后缀)
+ String fileName = zipEntry.getName();
+ log.info("正在处理的照片:" + fileName);
+ //去除windows或linux环境下 可能存在的多层级目录
+ if (fileName.contains("/")) {
+ fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
+ }
+ if (fileName.contains("\\")) {
+ fileName = fileName.substring(fileName.lastIndexOf("\\") + 1);
+ }
+ String materialCode = fileName.substring(0, fileName.lastIndexOf("."));
+ // 3. 读取ZIP中的图片为BufferedImage
+ BufferedImage image = ImageIO.read(zipInputStream);
+ if (ObjectUtil.isEmpty(image)) {
+ log.warn("无法读取图片: {}", fileName);
+ zipInputStream.closeEntry();
+ continue;
+ }
+ // 4. 将BufferedImage转为字节流
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ String imageExt = getImageExtension(fileName);
+ ImageIO.write(image, imageExt, baos);
+ byte[] imageBytes = baos.toByteArray();
+
+ // 5. 转换为MultipartFile(使用MockMultipartFile)
+ MultipartFile singleImageFile = new MockMultipartFile("file", fileName, "image/" + (imageExt
+ .equals("jpg") ? "jpeg" : imageExt), imageBytes);
+
+ // 6. 调用upload方法上传图片
+ FileInfo fileInfo = fileService.upload(singleImageFile, photoStoragePath, null, true, true);
+
+ // 7. 将物料编码和图片URL存入Map
+ codeUrlMap.put(materialCode, fileInfo.getUrl());
+
+ zipInputStream.closeEntry();
+ }
+ setPhotosByCode(codeUrlMap);
+ } catch (Exception e) {
+ throw new BusinessException("照片批量导入失败:" + e.getMessage());
+ }
+ }
+
+ private boolean isImageFile(String fileName) {
+ if (fileName == null || !fileName.contains("."))
+ return false;
+ String ext = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
+ return IMAGE_EXTENSIONS.contains(ext);
+ }
+
+ private String getImageExtension(String fileName) {
+ String ext = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
+ return "jpeg".equals(ext) ? "jpg" : ext;
+ }
+
+ private void setPhotosByCode(Map codeUrlMap) {
+ CheckUtils.throwIfEmpty(codeUrlMap, "照片为空,请重新上传");
+ List existList = baseMapper.selectList(Wrappers.lambdaQuery()
+ .in(MaterialInfoDO::getEncoding, codeUrlMap.keySet()));
+ if (existList.isEmpty()) {
+ log.warn("未找到任何匹配的物料编码");
+ return;
+ }
+ List updateList = existList.stream().map(exist -> {
+ MaterialInfoDO updateDO = new MaterialInfoDO();
+ updateDO.setId(exist.getId());
+ updateDO.setPhotoUrl(codeUrlMap.get(exist.getEncoding()));
+ return updateDO;
+ }).collect(Collectors.toList());
+ if (!updateList.isEmpty()) {
+ baseMapper.updateBatchById(updateList);
+ log.info("成功更新 {} 个物料的照片", updateList.size());
+ }
+
+ //记录未找到的物料编码(可选,方便排查问题)
+ Set existCodes = existList.stream().map(MaterialInfoDO::getEncoding).collect(Collectors.toSet());
+
+ codeUrlMap.keySet()
+ .stream()
+ .filter(code -> !existCodes.contains(code))
+ .forEach(code -> log.warn("物料编码 [{}] 不存在,照片更新失败", code));
+ }
+
}
diff --git a/wms-module-system/src/main/java/top/wms/admin/system/service/impl/UserServiceImpl.java b/wms-module-system/src/main/java/top/wms/admin/system/service/impl/UserServiceImpl.java
index 35fe50c..fcef782 100644
--- a/wms-module-system/src/main/java/top/wms/admin/system/service/impl/UserServiceImpl.java
+++ b/wms-module-system/src/main/java/top/wms/admin/system/service/impl/UserServiceImpl.java
@@ -133,7 +133,7 @@ public class UserServiceImpl extends BaseServiceImpl {
@GetMapping("/code/{code}")
- public MaterialInfoDO getMaterialInfoByCode(@PathVariable String code) {
- return baseService.getMaterialInfoByCode(code);
+ public MaterialInfoDO getMaterialInfoByCode(@PathVariable String code) {
+ return baseService.getMaterialInfoByCode(code);
}
@Operation(summary = "下载导入模板", description = "下载导入模板")
@@ -70,4 +68,15 @@ public class MaterialInfoController extends BaseController originalFilename == null || !originalFilename.endsWith(".zip"), "仅支持上传ZIP格式文件");
+ baseService.uploadMaterialPhotos(zipFile);
+ return R.ok();
+ }
}
diff --git a/wms-webapi/src/main/java/top/wms/admin/controller/system/UserController.java b/wms-webapi/src/main/java/top/wms/admin/controller/system/UserController.java
index 286b3e5..edd39bd 100644
--- a/wms-webapi/src/main/java/top/wms/admin/controller/system/UserController.java
+++ b/wms-webapi/src/main/java/top/wms/admin/controller/system/UserController.java
@@ -93,7 +93,7 @@ public class UserController extends BaseController