diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenSpuController.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenSpuController.java index cc32fbd00..27a6466e8 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenSpuController.java +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenSpuController.java @@ -241,18 +241,24 @@ public class CanteenSpuController { /** * Excel 导入菜品(单品 + 套餐) * - * @param cover true=覆盖(同名更新),false=仅新增(同名跳过) + * @param importMode cover=覆盖更新;skip=同名跳过;force=同名也新增(分类仍复用)。兼容旧参数 cover */ - @Operation(summary = "Excel导入菜品", description = "cover=true 覆盖导入;cover=false 新增导入;支持套餐") + @Operation(summary = "Excel导入菜品", description = "importMode=cover/skip/force;兼容 cover=true/false") @SysLog("导入菜品spu") @PostMapping("/import") @PreAuthorize("@pms.hasPermission('canteen_canteenSpu_add')") public R importExcel(@RequestParam("file") MultipartFile file, @RequestParam("merchantId") Long merchantId, @RequestParam(value = "categoryId", required = false) Long categoryId, - @RequestParam(value = "cover", defaultValue = "false") boolean cover) throws Exception { + @RequestParam(value = "importMode", required = false) String importMode, + @RequestParam(value = "cover", required = false) Boolean cover) throws Exception { try { - return canteenSpuImportHelper.importExcel(file, merchantId, categoryId, cover); + String mode = importMode; + if (StrUtil.isBlank(mode) && cover != null) { + mode = Boolean.TRUE.equals(cover) ? CanteenSpuImportHelper.MODE_COVER + : CanteenSpuImportHelper.MODE_SKIP; + } + return canteenSpuImportHelper.importExcel(file, merchantId, categoryId, mode); } catch (IllegalArgumentException ex) { return R.failed(ex.getMessage()); diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenSpuImportHelper.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenSpuImportHelper.java index 3df4662e9..b2332f7cc 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenSpuImportHelper.java +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenSpuImportHelper.java @@ -46,7 +46,7 @@ import java.util.Set; * 菜品列表 Excel 导入/导出(单品 + 套餐) * */ @Service @@ -190,9 +190,25 @@ public class CanteenSpuImportHelper { return row; } + /** 覆盖:同名/同ID更新 */ + public static final String MODE_COVER = "cover"; + /** 新增:同名跳过 */ + public static final String MODE_SKIP = "skip"; + /** 不覆盖:同名套餐/单品也强制新增;分类仍复用 */ + public static final String MODE_FORCE = "force"; + @Transactional(rollbackFor = Exception.class) public R importExcel(MultipartFile file, Long merchantId, Long defaultCategoryId, boolean cover) throws IOException { + return importExcel(file, merchantId, defaultCategoryId, cover ? MODE_COVER : MODE_SKIP); + } + + @Transactional(rollbackFor = Exception.class) + public R importExcel(MultipartFile file, Long merchantId, Long defaultCategoryId, String importMode) + throws IOException { + String mode = normalizeImportMode(importMode); + boolean cover = MODE_COVER.equals(mode); + boolean force = MODE_FORCE.equals(mode); if (merchantId == null) { return R.failed("请选择商户"); } @@ -269,7 +285,7 @@ public class CanteenSpuImportHelper { } try { int[] c = upsertCombo(e.getKey(), e.getValue(), merchantId, deptId, defaultCategoryId, categoryByName, - cover, errors, cache, pendingUpdates, standaloneDishIds, standaloneNames); + mode, errors, cache, pendingUpdates, standaloneDishIds, standaloneNames); created += c[0]; updated += c[1]; skipped += c[2]; @@ -281,7 +297,7 @@ public class CanteenSpuImportHelper { } if (CollUtil.isNotEmpty(singleRows)) { try { - int[] c = upsertSingles(singleRows, merchantId, deptId, defaultCategoryId, categoryByName, cover, + int[] c = upsertSingles(singleRows, merchantId, deptId, defaultCategoryId, categoryByName, mode, errors, cache, pendingUpdates); created += c[0]; updated += c[1]; @@ -299,8 +315,11 @@ public class CanteenSpuImportHelper { } String msg = String.format("导入完成:新增 %d,更新 %d,跳过 %d,价格已改 %d", created, updated, skipped, priceChanged); - if (!cover && skipped > 0) { - msg += ";当前为「新增导入」,同名已跳过。改价格请选「覆盖导入」"; + if (force) { + msg += ";当前为「不覆盖导入」,同名套餐/单品均已新增(分类仍复用)"; + } + else if (!cover && skipped > 0) { + msg += ";当前为「新增导入」,同名已跳过。改价格请选「覆盖导入」;同名也要新增请选「不覆盖导入」"; } if (cover && singlePriceMissing > 0 && singlePriceParsed == 0) { msg += ";警告:单品行均未读到「菜品单价(元)」,请确认第4列有数值"; @@ -477,8 +496,10 @@ public class CanteenSpuImportHelper { /** @return [created, updated, skipped, priceChanged] */ private int[] upsertSingles(List items, Long merchantId, Long deptId, - Long defaultCategoryId, Map categoryByName, boolean cover, List errors, + Long defaultCategoryId, Map categoryByName, String mode, List errors, ImportCache cache, List pendingUpdates) { + boolean cover = MODE_COVER.equals(mode); + boolean force = MODE_FORCE.equals(mode); int created = 0; int updated = 0; int skipped = 0; @@ -487,7 +508,7 @@ public class CanteenSpuImportHelper { try { Long categoryId = requireOrCreateCategory(item, defaultCategoryId, categoryByName, merchantId, deptId); - List exists = resolveDishes(merchantId, item, cache); + List exists = force ? List.of() : resolveDishes(merchantId, item, cache); if (CollUtil.isNotEmpty(exists)) { if (!cover) { skipped++; @@ -513,7 +534,9 @@ public class CanteenSpuImportHelper { CanteenSpuEntity spu = newDish(merchantId, deptId, categoryId, item); canteenSpuService.save(spu); createStock(spu); - cache.put(spu); + if (!force) { + cache.put(spu); + } created++; if (resolveDishPriceFen(item) != null) { priceChanged++; @@ -529,9 +552,11 @@ public class CanteenSpuImportHelper { /** @return [created, updated, skipped, priceChanged] */ private int[] upsertCombo(String comboName, List items, Long merchantId, Long deptId, - Long defaultCategoryId, Map categoryByName, boolean cover, List errors, + Long defaultCategoryId, Map categoryByName, String mode, List errors, ImportCache cache, List pendingUpdates, Set standaloneDishIds, Set standaloneNames) { + boolean cover = MODE_COVER.equals(mode); + boolean force = MODE_FORCE.equals(mode); List children = new ArrayList<>(); int created = 0; int updated = 0; @@ -541,7 +566,7 @@ public class CanteenSpuImportHelper { try { Long categoryId = requireOrCreateCategory(item, defaultCategoryId, categoryByName, merchantId, deptId); - List exists = resolveDishes(merchantId, item, cache); + List exists = force ? List.of() : resolveDishes(merchantId, item, cache); if (CollUtil.isNotEmpty(exists)) { CanteenSpuEntity exist = exists.get(0); boolean dishOwnedBySingleRow = standaloneDishIds.contains(exist.getId()) @@ -570,7 +595,9 @@ public class CanteenSpuImportHelper { CanteenSpuEntity spu = newDish(merchantId, deptId, categoryId, item); canteenSpuService.save(spu); createStock(spu); - cache.put(spu); + if (!force) { + cache.put(spu); + } children.add(spu); created++; if (resolveDishPriceFen(item) != null) { @@ -604,7 +631,7 @@ public class CanteenSpuImportHelper { comboItems.add(line); } - CanteenSpuEntity existCombo = resolveCombo(merchantId, comboName, first.getComboId(), cache); + CanteenSpuEntity existCombo = force ? null : resolveCombo(merchantId, comboName, first.getComboId(), cache); if (existCombo != null) { if (!cover) { skipped++; @@ -654,7 +681,9 @@ public class CanteenSpuImportHelper { combo.setComboItems(comboItems); canteenSpuService.saveDeep(combo); createStock(combo); - cache.put(combo); + if (!force) { + cache.put(combo); + } created++; if (comboPriceFen != null) { priceChanged++; @@ -663,6 +692,20 @@ public class CanteenSpuImportHelper { return new int[] { created, updated, skipped, priceChanged }; } + private static String normalizeImportMode(String importMode) { + if (StrUtil.isBlank(importMode)) { + return MODE_SKIP; + } + String m = importMode.trim().toLowerCase(); + if (MODE_COVER.equals(m) || "true".equals(m) || "1".equals(m)) { + return MODE_COVER; + } + if (MODE_FORCE.equals(m) || "always".equals(m) || "duplicate".equals(m)) { + return MODE_FORCE; + } + return MODE_SKIP; + } + /** 单品单价:菜品单价优先;为空时用市场价 */ private static Integer resolveDishPriceFen(CanteenSpuImportExcel item) { Integer dish = toFen(item.getPriceYuan());