|
|
@@ -0,0 +1,619 @@
|
|
|
+package shop.alien.storeplatform.service.impl;
|
|
|
+
|
|
|
+import cn.hutool.core.collection.CollectionUtil;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.poi.ss.usermodel.*;
|
|
|
+import org.apache.poi.xssf.usermodel.*;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
+import shop.alien.entity.result.R;
|
|
|
+import shop.alien.entity.store.StoreImg;
|
|
|
+import shop.alien.entity.store.excelVo.util.ExcelImage;
|
|
|
+import shop.alien.mapper.StoreImgMapper;
|
|
|
+import shop.alien.storeplatform.entity.StorePlatformStoreMenu;
|
|
|
+import shop.alien.storeplatform.feign.AlienStoreFeign;
|
|
|
+import shop.alien.storeplatform.mapper.StorePlatformStoreMenuMapper;
|
|
|
+import shop.alien.storeplatform.service.StorePlatformBarMenuService;
|
|
|
+import shop.alien.storeplatform.util.FileUploadUtil;
|
|
|
+import shop.alien.storeplatform.vo.StorePlatformStoreMenuImportVo;
|
|
|
+import shop.alien.util.ali.AliOSSUtil;
|
|
|
+
|
|
|
+import java.io.ByteArrayInputStream;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.lang.reflect.Field;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 商户平台-酒吧菜单服务实现类
|
|
|
+ *
|
|
|
+ * @author system
|
|
|
+ * @since 2025-01-XX
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+@RequiredArgsConstructor
|
|
|
+public class StorePlatformBarMenuServiceImpl extends ServiceImpl<StorePlatformStoreMenuMapper, StorePlatformStoreMenu> implements StorePlatformBarMenuService {
|
|
|
+
|
|
|
+ private final StorePlatformStoreMenuMapper storeMenuMapper;
|
|
|
+
|
|
|
+ private final StoreImgMapper storeImgMapper;
|
|
|
+
|
|
|
+ private final AliOSSUtil aliOSSUtil;
|
|
|
+
|
|
|
+ private final AlienStoreFeign alienStoreFeign;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public StorePlatformStoreMenu getById(Integer id) {
|
|
|
+ log.info("StorePlatformBarMenuServiceImpl.getById id={}", id);
|
|
|
+ if (id == null) {
|
|
|
+ throw new RuntimeException("菜单ID不能为空");
|
|
|
+ }
|
|
|
+ return super.getById(id);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<StorePlatformStoreMenu> getListByStoreId(Integer storeId) {
|
|
|
+ log.info("StorePlatformBarMenuServiceImpl.getListByStoreId storeId={}", storeId);
|
|
|
+ if (storeId == null) {
|
|
|
+ throw new RuntimeException("门店ID不能为空");
|
|
|
+ }
|
|
|
+ LambdaQueryWrapper<StorePlatformStoreMenu> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.eq(StorePlatformStoreMenu::getStoreId, storeId)
|
|
|
+ .eq(StorePlatformStoreMenu::getDeleteFlag, 0)
|
|
|
+ .orderByAsc(StorePlatformStoreMenu::getSort);
|
|
|
+ List<StorePlatformStoreMenu> list = this.list(queryWrapper);
|
|
|
+ return list.stream()
|
|
|
+ .sorted(Comparator.comparing(StorePlatformStoreMenu::getSort))
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<StorePlatformStoreMenu> getListByStoreIdAndType(Integer storeId, String dishMenuType) {
|
|
|
+ log.info("StorePlatformBarMenuServiceImpl.getListByStoreIdAndType storeId={}, dishMenuType={}", storeId, dishMenuType);
|
|
|
+ if (storeId == null) {
|
|
|
+ throw new RuntimeException("门店ID不能为空");
|
|
|
+ }
|
|
|
+ LambdaQueryWrapper<StorePlatformStoreMenu> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.eq(StorePlatformStoreMenu::getStoreId, storeId)
|
|
|
+ .eq(StorePlatformStoreMenu::getDeleteFlag, 0);
|
|
|
+ if (StringUtils.isNotEmpty(dishMenuType)) {
|
|
|
+ queryWrapper.eq(StorePlatformStoreMenu::getDishMenuType, dishMenuType);
|
|
|
+ }
|
|
|
+ queryWrapper.orderByAsc(StorePlatformStoreMenu::getSort);
|
|
|
+ List<StorePlatformStoreMenu> list = this.list(queryWrapper);
|
|
|
+ return list.stream()
|
|
|
+ .sorted(Comparator.comparing(StorePlatformStoreMenu::getSort))
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public R<String> saveMenu(StorePlatformStoreMenu storeMenu) {
|
|
|
+ log.info("StorePlatformBarMenuServiceImpl.saveMenu storeMenu={}", storeMenu);
|
|
|
+ if (storeMenu == null) {
|
|
|
+ return R.fail("菜单信息不能为空");
|
|
|
+ }
|
|
|
+ if (storeMenu.getStoreId() == null) {
|
|
|
+ return R.fail("门店ID不能为空");
|
|
|
+ }
|
|
|
+ if (StringUtils.isEmpty(storeMenu.getDishName())) {
|
|
|
+ return R.fail("菜单名称不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置排序
|
|
|
+ LambdaQueryWrapper<StorePlatformStoreMenu> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.eq(StorePlatformStoreMenu::getStoreId, storeMenu.getStoreId())
|
|
|
+ .eq(StorePlatformStoreMenu::getDeleteFlag, 0);
|
|
|
+ List<StorePlatformStoreMenu> menuList = this.list(queryWrapper);
|
|
|
+ if (CollectionUtil.isNotEmpty(menuList)) {
|
|
|
+ int maxSort = menuList.stream()
|
|
|
+ .map(StorePlatformStoreMenu::getSort)
|
|
|
+ .max(Integer::compareTo)
|
|
|
+ .orElse(0);
|
|
|
+ storeMenu.setSort(maxSort + 1);
|
|
|
+ } else {
|
|
|
+ storeMenu.setSort(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ boolean flag = this.save(storeMenu);
|
|
|
+ if (!flag) {
|
|
|
+ return R.fail("新增菜单失败");
|
|
|
+ }
|
|
|
+ return R.success("新增菜单成功");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public R<String> updateMenu(StorePlatformStoreMenu storeMenu) {
|
|
|
+ log.info("StorePlatformBarMenuServiceImpl.updateMenu storeMenu={}", storeMenu);
|
|
|
+ if (storeMenu == null || storeMenu.getId() == null) {
|
|
|
+ return R.fail("菜单信息或菜单ID不能为空");
|
|
|
+ }
|
|
|
+ if (StringUtils.isEmpty(storeMenu.getDishName())) {
|
|
|
+ return R.fail("菜单名称不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ boolean flag = this.updateById(storeMenu);
|
|
|
+ if (!flag) {
|
|
|
+ return R.fail("修改菜单失败");
|
|
|
+ }
|
|
|
+ return R.success("修改菜单成功");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public R<String> saveOrUpdateMenu(StorePlatformStoreMenu storeMenu) {
|
|
|
+ log.info("StorePlatformBarMenuServiceImpl.saveOrUpdateMenu storeMenu={}", storeMenu);
|
|
|
+ if (storeMenu == null) {
|
|
|
+ return R.fail("菜单信息不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (storeMenu.getId() != null) {
|
|
|
+ // 修改
|
|
|
+ return updateMenu(storeMenu);
|
|
|
+ } else {
|
|
|
+ // 新增
|
|
|
+ return saveMenu(storeMenu);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public R<String> deleteMenu(Integer id) {
|
|
|
+ log.info("StorePlatformBarMenuServiceImpl.deleteMenu id={}", id);
|
|
|
+ if (id == null) {
|
|
|
+ return R.fail("菜单ID不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ boolean flag = this.removeById(id);
|
|
|
+ if (!flag) {
|
|
|
+ return R.fail("删除菜单失败");
|
|
|
+ }
|
|
|
+ return R.success("删除菜单成功");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public R<String> deleteMenuBatch(List<Integer> ids) {
|
|
|
+ log.info("StorePlatformBarMenuServiceImpl.deleteMenuBatch ids={}", ids);
|
|
|
+ if (CollectionUtil.isEmpty(ids)) {
|
|
|
+ return R.fail("菜单ID列表不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ boolean flag = this.removeByIds(ids);
|
|
|
+ if (!flag) {
|
|
|
+ return R.fail("批量删除菜单失败");
|
|
|
+ }
|
|
|
+ return R.success("批量删除菜单成功");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Excel导入酒吧菜单
|
|
|
+ *
|
|
|
+ * @param file Excel文件
|
|
|
+ * @param storeId 门店id
|
|
|
+ * @return 导入结果
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public R<String> importMenuFromExcel(MultipartFile file, Integer storeId) {
|
|
|
+ log.info("StorePlatformBarMenuServiceImpl.importMenuFromExcel storeId={}", storeId);
|
|
|
+
|
|
|
+ if (file == null || file.isEmpty()) {
|
|
|
+ return R.fail("上传文件为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ String fileName = file.getOriginalFilename();
|
|
|
+ if (fileName == null || (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls"))) {
|
|
|
+ return R.fail("文件格式不正确,请上传Excel文件");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (storeId == null) {
|
|
|
+ return R.fail("门店ID不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ List<String> errorMessages = new ArrayList<>();
|
|
|
+ int successCount = 0;
|
|
|
+ int totalCount = 0;
|
|
|
+
|
|
|
+ try (InputStream inputStream = file.getInputStream();
|
|
|
+ XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) {
|
|
|
+ Sheet sheet = workbook.getSheetAt(0);
|
|
|
+
|
|
|
+ // 获取表头(假设表头在第5行,索引为5)
|
|
|
+ Row headerRow = sheet.getRow(5);
|
|
|
+ if (headerRow == null) {
|
|
|
+ return R.fail("Excel文件格式不正确,缺少表头");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建字段映射(表头名称 -> 列索引)
|
|
|
+ Map<String, Integer> headerMap = new HashMap<>();
|
|
|
+ Field[] fields = StorePlatformStoreMenuImportVo.class.getDeclaredFields();
|
|
|
+ for (int i = 0; i < headerRow.getLastCellNum(); i++) {
|
|
|
+ Cell cell = headerRow.getCell(i);
|
|
|
+ if (cell != null) {
|
|
|
+ String headerName = getCellValueAsString(cell);
|
|
|
+ if (StringUtils.isNotEmpty(headerName)) {
|
|
|
+ headerMap.put(headerName.trim(), i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取图片映射(行索引 -> 图片字节数组)
|
|
|
+ Map<Integer, byte[]> imageMap = extractImagesFromSheet(sheet);
|
|
|
+
|
|
|
+ // 读取数据行(从第6行开始,索引为6)
|
|
|
+ for (int rowIndex = 6; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
|
|
|
+ Row row = sheet.getRow(rowIndex);
|
|
|
+ if (row == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否为空行
|
|
|
+ boolean isEmptyRow = true;
|
|
|
+ for (int i = 0; i < row.getLastCellNum(); i++) {
|
|
|
+ Cell cell = row.getCell(i);
|
|
|
+ if (cell != null && cell.getCellType() != CellType.BLANK) {
|
|
|
+ String cellValue = getCellValueAsString(cell);
|
|
|
+ if (StringUtils.isNotEmpty(cellValue)) {
|
|
|
+ isEmptyRow = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (isEmptyRow) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ totalCount++;
|
|
|
+ StorePlatformStoreMenuImportVo excelVo = new StorePlatformStoreMenuImportVo();
|
|
|
+
|
|
|
+ // 读取每个字段
|
|
|
+ for (Field field : fields) {
|
|
|
+ field.setAccessible(true);
|
|
|
+ String fieldName = field.getName();
|
|
|
+ Integer colIndex = null;
|
|
|
+
|
|
|
+ // 根据字段名查找对应的表头
|
|
|
+ for (Map.Entry<String, Integer> entry : headerMap.entrySet()) {
|
|
|
+ String headerName = entry.getKey();
|
|
|
+ if (isFieldMatch(fieldName, headerName)) {
|
|
|
+ colIndex = entry.getValue();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (colIndex == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ Cell cell = row.getCell(colIndex);
|
|
|
+ if (cell == null && !field.isAnnotationPresent(ExcelImage.class)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (field.isAnnotationPresent(ExcelImage.class)) {
|
|
|
+ // 处理图片字段
|
|
|
+ byte[] imageBytes = imageMap.get(rowIndex);
|
|
|
+ if (imageBytes != null && imageBytes.length > 0) {
|
|
|
+ String imageName = "barMenu_" + storeId + "_" + System.currentTimeMillis() + "_" + rowIndex + ".jpg";
|
|
|
+ MultipartFile multipartFile = new ByteArrayMultipartFile(imageBytes, imageName);
|
|
|
+ JSONObject jsonObject = alienStoreFeign.uploadFile(multipartFile);
|
|
|
+ if (200 == jsonObject.getIntValue("code")) {
|
|
|
+ field.set(excelVo, jsonObject.getJSONArray("data").get(0));
|
|
|
+ } else {
|
|
|
+ field.set(excelVo, "");
|
|
|
+ }
|
|
|
+// // 上传图片到OSS
|
|
|
+// String imageUrl = uploadImageToOSS(imageBytes, storeId, rowIndex);
|
|
|
+// field.set(excelVo, imageUrl);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 处理普通字段
|
|
|
+ String cellValue = getCellValueAsString(cell);
|
|
|
+ if (StringUtils.isNotEmpty(cellValue)) {
|
|
|
+ setFieldValue(excelVo, field, cellValue.trim());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("读取字段{}失败:{}", fieldName, e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理导入数据
|
|
|
+ try {
|
|
|
+ validateAndSaveMenu(excelVo, storeId, rowIndex + 1);
|
|
|
+ successCount++;
|
|
|
+ } catch (Exception e) {
|
|
|
+ errorMessages.add(String.format("第%d行:%s", rowIndex + 1, e.getMessage()));
|
|
|
+ log.error("导入第{}行数据失败", rowIndex + 1, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("导入Excel失败", e);
|
|
|
+ return R.fail("导入失败:" + e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建返回消息
|
|
|
+ StringBuilder message = new StringBuilder();
|
|
|
+ message.append(String.format("导入完成:成功%d条,失败%d条", successCount, totalCount - successCount));
|
|
|
+ if (!errorMessages.isEmpty()) {
|
|
|
+ message.append("\n失败详情:\n");
|
|
|
+ for (int i = 0; i < Math.min(errorMessages.size(), 10); i++) {
|
|
|
+ message.append(errorMessages.get(i)).append("\n");
|
|
|
+ }
|
|
|
+ if (errorMessages.size() > 10) {
|
|
|
+ message.append("...还有").append(errorMessages.size() - 10).append("条错误信息");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return R.success(message.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从Sheet中提取图片
|
|
|
+ */
|
|
|
+ private Map<Integer, byte[]> extractImagesFromSheet(Sheet sheet) {
|
|
|
+ Map<Integer, byte[]> imageMap = new HashMap<>();
|
|
|
+ if (sheet instanceof XSSFSheet) {
|
|
|
+ XSSFSheet xssfSheet = (XSSFSheet) sheet;
|
|
|
+ XSSFDrawing drawing = xssfSheet.getDrawingPatriarch();
|
|
|
+ if (drawing != null) {
|
|
|
+ List<XSSFShape> shapes = drawing.getShapes();
|
|
|
+ for (XSSFShape shape : shapes) {
|
|
|
+ if (shape instanceof XSSFPicture) {
|
|
|
+ XSSFPicture picture = (XSSFPicture) shape;
|
|
|
+ XSSFClientAnchor anchor = (XSSFClientAnchor) picture.getAnchor();
|
|
|
+ int rowIndex = anchor.getRow1();
|
|
|
+ try {
|
|
|
+ // 直接使用 getData() 方法获取图片字节数组
|
|
|
+ byte[] imageBytes = picture.getPictureData().getData();
|
|
|
+ imageMap.put(rowIndex, imageBytes);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("提取第{}行图片失败:{}", rowIndex, e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return imageMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 上传图片到OSS
|
|
|
+ */
|
|
|
+ private String uploadImageToOSS(byte[] imageBytes, Integer storeId, int rowIndex) {
|
|
|
+ try {
|
|
|
+ // 生成文件名
|
|
|
+ String fileName = "bar_menu_" + storeId + "_" + System.currentTimeMillis() + "_" + rowIndex + ".jpg";
|
|
|
+ String prefix = "image/";
|
|
|
+
|
|
|
+ // 创建临时MultipartFile
|
|
|
+ MultipartFile multipartFile = new ByteArrayMultipartFile(imageBytes, fileName);
|
|
|
+
|
|
|
+ // 上传到OSS
|
|
|
+ return aliOSSUtil.uploadFile(multipartFile, prefix + fileName);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("上传图片失败", e);
|
|
|
+ throw new RuntimeException("上传图片失败:" + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验并保存菜单
|
|
|
+ */
|
|
|
+ private void validateAndSaveMenu(StorePlatformStoreMenuImportVo excelVo, Integer storeId, int rowNum) {
|
|
|
+ // 校验必填字段
|
|
|
+ if (StringUtils.isEmpty(excelVo.getDishName())) {
|
|
|
+ throw new RuntimeException("名称不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (excelVo.getDishPrice() == null || excelVo.getDishPrice().compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
+ throw new RuntimeException("价格必须大于0");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建StorePlatformStoreMenu对象
|
|
|
+ StorePlatformStoreMenu storeMenu = new StorePlatformStoreMenu();
|
|
|
+ storeMenu.setStoreId(storeId);
|
|
|
+ storeMenu.setDishMenuType(excelVo.getDishMenuType() != null ? excelVo.getDishMenuType() : 0);
|
|
|
+ storeMenu.setDishName(excelVo.getDishName());
|
|
|
+ storeMenu.setDishPrice(excelVo.getDishPrice());
|
|
|
+ storeMenu.setCostPrice(excelVo.getCostPrice());
|
|
|
+ storeMenu.setCategory(excelVo.getCategory());
|
|
|
+ storeMenu.setAlcoholVolume(excelVo.getAlcoholVolume());
|
|
|
+ storeMenu.setFlavor(excelVo.getFlavor());
|
|
|
+ storeMenu.setDescription(excelVo.getDescription());
|
|
|
+ storeMenu.setDishType(excelVo.getDishType() != null ? excelVo.getDishType() : 0);
|
|
|
+
|
|
|
+ // 处理图片
|
|
|
+ if (StringUtils.isNotEmpty(excelVo.getImg())) {
|
|
|
+ StoreImg storeImg = new StoreImg();
|
|
|
+ storeImg.setStoreId(storeId);
|
|
|
+ storeImg.setImgType(7); // 菜单图片类型
|
|
|
+ storeImg.setImgUrl(excelVo.getImg());
|
|
|
+ storeImg.setImgDescription(excelVo.getDishName());
|
|
|
+ storeImgMapper.insert(storeImg);
|
|
|
+ storeMenu.setImgId(storeImg.getId());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置排序
|
|
|
+ LambdaQueryWrapper<StorePlatformStoreMenu> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.eq(StorePlatformStoreMenu::getStoreId, storeId)
|
|
|
+ .eq(StorePlatformStoreMenu::getDeleteFlag, 0);
|
|
|
+ List<StorePlatformStoreMenu> menuList = this.list(queryWrapper);
|
|
|
+ if (CollectionUtil.isNotEmpty(menuList)) {
|
|
|
+ int maxSort = menuList.stream()
|
|
|
+ .map(StorePlatformStoreMenu::getSort)
|
|
|
+ .max(Integer::compareTo)
|
|
|
+ .orElse(0);
|
|
|
+ storeMenu.setSort(maxSort + 1);
|
|
|
+ } else {
|
|
|
+ storeMenu.setSort(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存菜单
|
|
|
+ boolean flag = this.save(storeMenu);
|
|
|
+ if (!flag) {
|
|
|
+ throw new RuntimeException("保存菜单失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断字段名是否匹配表头
|
|
|
+ */
|
|
|
+ private boolean isFieldMatch(String fieldName, String headerName) {
|
|
|
+ // 字段映射表
|
|
|
+ Map<String, String> fieldMapping = new HashMap<>();
|
|
|
+ fieldMapping.put("dishMenuType", "类型(酒水/餐食)");
|
|
|
+ fieldMapping.put("dishName", "名称");
|
|
|
+ fieldMapping.put("dishPrice", "价格(¥)");
|
|
|
+ fieldMapping.put("costPrice", "成本价(¥)");
|
|
|
+ fieldMapping.put("category", "品类");
|
|
|
+ fieldMapping.put("alcoholVolume", "酒精度(%vol)");
|
|
|
+ fieldMapping.put("flavor", "风味");
|
|
|
+ fieldMapping.put("img", "图片");
|
|
|
+ fieldMapping.put("description", "描述");
|
|
|
+ fieldMapping.put("dishType", "是否设为推荐(是/否)");
|
|
|
+
|
|
|
+ String expectedHeader = fieldMapping.get(fieldName);
|
|
|
+ return expectedHeader != null && expectedHeader.equals(headerName);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置字段值
|
|
|
+ */
|
|
|
+ private void setFieldValue(StorePlatformStoreMenuImportVo excelVo, Field field, String cellValue) throws Exception {
|
|
|
+ Class<?> fieldType = field.getType();
|
|
|
+ String fieldName = field.getName();
|
|
|
+
|
|
|
+ if (fieldType == BigDecimal.class) {
|
|
|
+ try {
|
|
|
+ BigDecimal value = new BigDecimal(cellValue);
|
|
|
+ if (value.scale() > 2) {
|
|
|
+ throw new RuntimeException("价格或者成本价格式错误, 请输入数字, 小数点保留两位以内");
|
|
|
+ }
|
|
|
+ field.set(excelVo, value);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ throw new RuntimeException("价格或者成本价格式错误, 请输入数字, 小数点保留两位以内");
|
|
|
+ }
|
|
|
+ } else if (fieldType == Integer.class) {
|
|
|
+ // 处理 dishType 字段:将"是"/"否"转换为1/0
|
|
|
+ if ("dishType".equals(fieldName)) {
|
|
|
+ String trimmedValue = cellValue.trim();
|
|
|
+ if ("是".equals(trimmedValue)) {
|
|
|
+ field.set(excelVo, 1);
|
|
|
+ } else if ("否".equals(trimmedValue)) {
|
|
|
+ field.set(excelVo, 0);
|
|
|
+ } else {
|
|
|
+ throw new RuntimeException("是否推荐字段格式错误,请输入'是'或'否'");
|
|
|
+ }
|
|
|
+ } else if ("dishMenuType".equals(fieldName)) {
|
|
|
+ String trimmedValue = cellValue.trim();
|
|
|
+ if ("餐食".equals(trimmedValue)) {
|
|
|
+ field.set(excelVo, 1);
|
|
|
+ } else if ("酒水".equals(trimmedValue)) {
|
|
|
+ field.set(excelVo, 2);
|
|
|
+ } else {
|
|
|
+ throw new RuntimeException("类型字段格式错误,请输入'酒水'或'餐食'");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ try {
|
|
|
+ field.set(excelVo, Integer.parseInt(cellValue));
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ throw new RuntimeException("数字格式错误:" + cellValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // String类型
|
|
|
+ field.set(excelVo, cellValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取单元格值(字符串格式)
|
|
|
+ */
|
|
|
+ private String getCellValueAsString(Cell cell) {
|
|
|
+ if (cell == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (cell.getCellType()) {
|
|
|
+ case STRING:
|
|
|
+ return cell.getStringCellValue();
|
|
|
+ case NUMERIC:
|
|
|
+ if (DateUtil.isCellDateFormatted(cell)) {
|
|
|
+ return cell.getDateCellValue().toString();
|
|
|
+ } else {
|
|
|
+ // 处理数字,避免科学计数法
|
|
|
+ double numericValue = cell.getNumericCellValue();
|
|
|
+ if (numericValue == (long) numericValue) {
|
|
|
+ return String.valueOf((long) numericValue);
|
|
|
+ } else {
|
|
|
+ return String.valueOf(numericValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ case BOOLEAN:
|
|
|
+ return String.valueOf(cell.getBooleanCellValue());
|
|
|
+ case FORMULA:
|
|
|
+ return cell.getCellFormula();
|
|
|
+ default:
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 临时MultipartFile实现类,用于上传字节数组
|
|
|
+ */
|
|
|
+ private static class ByteArrayMultipartFile implements MultipartFile {
|
|
|
+ private final byte[] content;
|
|
|
+ private final String fileName;
|
|
|
+
|
|
|
+ public ByteArrayMultipartFile(byte[] content, String fileName) {
|
|
|
+ this.content = content;
|
|
|
+ this.fileName = fileName;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getName() {
|
|
|
+ return "file";
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getOriginalFilename() {
|
|
|
+ return fileName;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getContentType() {
|
|
|
+ return "image/jpeg";
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean isEmpty() {
|
|
|
+ return content == null || content.length == 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public long getSize() {
|
|
|
+ return content.length;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public byte[] getBytes() throws IOException {
|
|
|
+ return content;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public InputStream getInputStream() throws IOException {
|
|
|
+ return new ByteArrayInputStream(content);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void transferTo(java.io.File dest) throws IOException, IllegalStateException {
|
|
|
+ java.nio.file.Files.write(dest.toPath(), content);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|