From 0c97c9820b9e1fd346f5220784013b283663592f Mon Sep 17 00:00:00 2001
From: zjf <1799650030@qq.com>
Date: Tue, 18 Aug 2026 09:21:46 +0800
Subject: [PATCH] =?UTF-8?q?=E8=8F=9C=E5=93=81=E5=AF=BC=E5=85=A5=E4=B8=8D?=
=?UTF-8?q?=E8=A6=86=E7=9B=96=E9=80=89=E9=A1=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../controller/CanteenSpuController.java | 14 ++--
.../service/impl/CanteenSpuImportHelper.java | 69 +++++++++++++++----
2 files changed, 66 insertions(+), 17 deletions(-)
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 导入/导出(单品 + 套餐)
*
* - 套餐名留空=单品;套餐名相同多行=套餐组成
- * - cover=true:同名更新;cover=false:同名跳过
+ * - cover:同名更新;skip:同名跳过;force:同名也新增(分类仍按名称复用)
*
*/
@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());