每日菜谱导入优化

This commit is contained in:
zjf committed 2026-08-28 15:44:53 +08:00
1 parent 87bec4ba73
commit 43966c04ec
7 files changed
+307 -87

No files matched your search

@@ -7,8 +7,9 @@ import java.math.BigDecimal;
/**
* 每日菜谱导入/导出 Excel 行(导出含菜谱 ID,导入时可按 ID 更新)
* <p>列顺序:日期、餐段、套餐名、菜品名、分类、单价、套餐价、库存、配料、菜谱ID、填写说明
* <p>列顺序:日期、餐段、套餐名、菜品名、分类、单价、套餐价、库存、配料、菜谱ID、菜品SPU ID、填写说明
* <p>配料:单品写单品 SPU;套餐仅在首行填写,写入套餐 SPU
* <p>菜谱ID / 菜品SPU ID 必须用文本导出,否则 Excel 对超长雪花 ID 会丢精度导致再导入报「不存在」。
*/
@Data
public class CanteenDailyMenuImportExcel {
@@ -47,13 +48,18 @@ public class CanteenDailyMenuImportExcel {
@ExcelProperty(value = "配料", index = 8)
private String ingredients;
/** 每日菜谱记录 ID;导入时有则更新,无则新增 */
/**
* 每日菜谱记录 ID(文本)。导入时有则更新,无则新增。
* 勿改成数字格式,否则 Excel 会丢精度。
*/
@ExcelProperty(value = "菜谱ID", index = 9)
private Long id;
private String id;
/** 关联菜品 SPU ID;导出参考,导入不依赖 */
/**
* 关联菜品/套餐 SPU ID(文本,仅导出参考,导入不依赖此列)。
*/
@ExcelProperty(value = "菜品SPU ID", index = 10)
private Long spuId;
private String spuId;
/** 仅示例说明用,导入逻辑忽略 */
@ExcelProperty(value = "填写说明", index = 11)
@@ -0,0 +1,24 @@
package com.bsd.pjcloud.canteen.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import java.util.List;
/**
* 菜品列表:批量赋值封面图
*/
@Data
@Schema(description = "批量赋值菜品封面图")
public class CanteenSpuBatchPicDTO {
@NotEmpty
@Schema(description = "菜品 SPU ID 列表")
private List<Long> ids;
@NotBlank
@Schema(description = "封面图地址(上传或图库)")
private String picUrl;
}
@@ -178,6 +178,15 @@ public class CanteenDailyMenuController {
catch (IllegalArgumentException ex) {
return R.failed(ex.getMessage());
}
catch (org.springframework.transaction.UnexpectedRollbackException ex) {
// 兜底:避免把 rollback-only 原文甩给前端
Throwable root = ex.getMostSpecificCause() != null ? ex.getMostSpecificCause() : ex;
String tip = root.getMessage();
if (tip != null && tip.contains("rollback-only")) {
tip = "导入失败并已回滚,请检查套餐组成、分类、菜名是否完整后重试";
}
return R.failed(tip);
}
}
/**
@@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.canteen.constant.StockTypeEnum;
import com.bsd.pjcloud.canteen.dto.CanteenDailySpuDTO;
import com.bsd.pjcloud.canteen.dto.CanteenSpuBatchPicDTO;
import com.bsd.pjcloud.canteen.dto.CanteenSpuExportQuery;
import com.bsd.pjcloud.canteen.dto.CanteenSpuImportExcel;
import com.bsd.pjcloud.canteen.dto.CanteenSpuUpdateStockReqDTO;
@@ -209,6 +210,27 @@ public class CanteenSpuController {
}
}
/**
* 批量赋值菜品封面图(导入无图后,从图库或上传一张图批量套用)
*/
@Operation(summary = "批量赋值菜品封面图", description = "批量赋值菜品封面图")
@SysLog("批量赋值菜品封面图")
@PutMapping("/batchPic")
@PreAuthorize("@pms.hasPermission('canteen_canteenSpu_edit')")
public R batchPic(@RequestBody CanteenSpuBatchPicDTO dto) {
if (CollUtil.isEmpty(dto.getIds())) {
return R.failed("请选择菜品");
}
if (StrUtil.isBlank(dto.getPicUrl())) {
return R.failed("请选择或上传封面图");
}
String picUrl = dto.getPicUrl().trim();
boolean ok = canteenSpuService.update(Wrappers.<CanteenSpuEntity>lambdaUpdate()
.set(CanteenSpuEntity::getPicUrl, picUrl)
.in(CanteenSpuEntity::getId, dto.getIds()));
return ok ? R.ok(null, "已更新 " + dto.getIds().size() + " 道菜封面") : R.failed("更新失败");
}
/**
* 修改菜品spu
*
@@ -65,7 +65,7 @@ public class CanteenDailyMenuImportHelper {
public List<CanteenDailyMenuImportExcel> templateRows() {
List<CanteenDailyMenuImportExcel> list = new ArrayList<>();
list.add(row("2026-08-17", "午餐", "A套餐", "糖醋排骨", "荤菜", "青莲仔排", "12", "21", "100",
"分类按名称匹配;套餐价/库存/配料只填首行。已有菜品不选默认分类也可导入"));
"分类按名称匹配;套餐价/库存/配料只填首行。菜谱ID/SPU ID为文本勿改成数字,否则Excel会丢精度"));
list.add(row(null, null, null, "蒜苔炒腊肉", "荤菜", null, "8", null, null, null));
list.add(row(null, null, null, "清炒时蔬", "素菜", null, "3", null, null, null));
list.add(row(null, null, "B套餐", "红烧肉", "荤菜", "五花肉、土豆", "10", "18", "80",
@@ -146,12 +146,20 @@ public class CanteenDailyMenuImportHelper {
String comboLabel = resolveComboLabel(menu);
BigDecimal comboPrice = fenToYuan(menu.getPrice());
List<OnlineSpuComboItemVO> items = menu.getComboItems();
for (int i = 0; i < items.size(); i++) {
OnlineSpuComboItemVO item = items.get(i);
Set<Long> exportedChildIds = new HashSet<>();
int written = 0;
for (OnlineSpuComboItemVO item : items) {
if (item == null) {
continue;
}
Long childId = item.getChildSpuId();
if (childId != null && !exportedChildIds.add(childId)) {
continue;
}
CanteenDailyMenuImportExcel row = new CanteenDailyMenuImportExcel();
if (i == 0) {
row.setId(menu.getId());
row.setSpuId(menu.getSpuId());
if (written == 0) {
row.setId(menu.getId() == null ? null : String.valueOf(menu.getId()));
row.setSpuId(menu.getSpuId() == null ? null : String.valueOf(menu.getSpuId()));
row.setBizDate(dateStr);
row.setMealName(meal);
row.setComboName(comboLabel);
@@ -164,12 +172,13 @@ public class CanteenDailyMenuImportHelper {
row.setDishName(StrUtil.blankToDefault(item.getName(), ""));
row.setDishPriceYuan(fenToYuan(item.getPrice()));
rows.add(row);
written++;
}
}
else {
CanteenDailyMenuImportExcel row = new CanteenDailyMenuImportExcel();
row.setId(menu.getId());
row.setSpuId(menu.getSpuId());
row.setId(menu.getId() == null ? null : String.valueOf(menu.getId()));
row.setSpuId(menu.getSpuId() == null ? null : String.valueOf(menu.getSpuId()));
row.setBizDate(dateStr);
row.setMealName(meal);
row.setComboName("无");
@@ -225,6 +234,36 @@ public class CanteenDailyMenuImportHelper {
};
}
/** dayHour 形如 2026-09-04|2 */
private static String describeDayHour(String dayHour) {
if (StrUtil.isBlank(dayHour) || !dayHour.contains("|")) {
return dayHour;
}
String[] p = dayHour.split("\\|", 2);
Integer hour = null;
try {
hour = Integer.parseInt(p[1]);
}
catch (Exception ignored) {
// ignore
}
return p[0] + " " + formatMeal(hour);
}
/** 普通校验错误:换行编号,便于前端弹窗阅读 */
private static String formatImportErrors(List<String> errors) {
int show = Math.min(errors.size(), 20);
StringBuilder sb = new StringBuilder();
sb.append("导入校验未通过(").append(errors.size()).append(" 项):\n");
for (int i = 0; i < show; i++) {
sb.append(i + 1).append(". ").append(errors.get(i)).append('\n');
}
if (errors.size() > show) {
sb.append("…另有 ").append(errors.size() - show).append(" 项未列出");
}
return sb.toString().trim();
}
private static BigDecimal fenToYuan(Integer fen) {
if (fen == null) {
return null;
@@ -278,6 +317,10 @@ public class CanteenDailyMenuImportHelper {
// groupKey = date|hourType|comboOrEmpty|menuIdOrNew
Map<String, List<CanteenDailyMenuImportExcel>> groups = new LinkedHashMap<>();
/** 菜谱ID → 出现过的 日期|餐段(用于汇总跨天串 ID) */
Map<Long, LinkedHashSet<String>> menuIdDayHours = new LinkedHashMap<>();
/** 菜谱ID → 示例套餐/菜名(取首次) */
Map<Long, String> menuIdLabel = new LinkedHashMap<>();
Set<LocalDate> involvedDates = new HashSet<>();
int line = 1;
for (CanteenDailyMenuImportExcel row : dataRows) {
@@ -300,10 +343,14 @@ public class CanteenDailyMenuImportHelper {
involvedDates.add(date);
String combo = StrUtil.trim(row.getComboName());
String comboKey = StrUtil.blankToDefault(combo, "");
Long menuId = row.getId();
Long menuId = parseExcelLong(row.getId());
// 必须带上日期+餐段:仅按菜谱ID 分组时,同名套餐跨天若串了同一 ID,会把两天组成菜并成一组报「重复」
String groupKey;
if (menuId != null) {
groupKey = "id:" + menuId;
String dayHour = date + "|" + hourType;
menuIdDayHours.computeIfAbsent(menuId, k -> new LinkedHashSet<>()).add(dayHour);
menuIdLabel.putIfAbsent(menuId, StrUtil.blankToDefault(comboKey, dish));
groupKey = "id:" + menuId + "|" + date + "|" + hourType;
}
else if (StrUtil.isNotBlank(comboKey) && !isSingleComboMarker(comboKey)) {
groupKey = "new:" + date + "|" + hourType + "|" + comboKey;
@@ -313,8 +360,30 @@ public class CanteenDailyMenuImportHelper {
}
groups.computeIfAbsent(groupKey, k -> new ArrayList<>()).add(row);
}
List<String> crossDayIdErrors = new ArrayList<>();
for (Map.Entry<Long, LinkedHashSet<String>> en : menuIdDayHours.entrySet()) {
if (en.getValue().size() <= 1) {
continue;
}
List<String> days = en.getValue().stream().map(CanteenDailyMenuImportHelper::describeDayHour).toList();
crossDayIdErrors.add(String.format("ID %s — %s(%s)", en.getKey(), String.join(" / ", days),
StrUtil.blankToDefault(menuIdLabel.get(en.getKey()), "-")));
}
if (CollUtil.isNotEmpty(crossDayIdErrors)) {
StringBuilder sb = new StringBuilder();
sb.append("发现 ").append(crossDayIdErrors.size()).append(" 处菜谱ID跨天/跨餐段重复(同一ID只能对应一天一餐):\n");
int show = Math.min(crossDayIdErrors.size(), 15);
for (int i = 0; i < show; i++) {
sb.append(i + 1).append(". ").append(crossDayIdErrors.get(i)).append('\n');
}
if (crossDayIdErrors.size() > show) {
sb.append("…另有 ").append(crossDayIdErrors.size() - show).append(" 处未列出\n");
}
sb.append("请删掉错误那天的「菜谱ID」列,或重新导出后再改。");
return R.failed(sb.toString().trim());
}
if (CollUtil.isNotEmpty(errors)) {
return R.failed(String.join(";", errors.subList(0, Math.min(errors.size(), 20))));
return R.failed(formatImportErrors(errors));
}
ImportCache cache = buildImportCache(merchantId, involvedDates);
@@ -327,41 +396,65 @@ public class CanteenDailyMenuImportHelper {
for (Map.Entry<String, List<CanteenDailyMenuImportExcel>> e : groups.entrySet()) {
List<CanteenDailyMenuImportExcel> items = e.getValue();
Long menuId = items.stream().map(CanteenDailyMenuImportExcel::getId).filter(Objects::nonNull).findFirst()
.orElse(null);
CanteenDailyMenuImportExcel firstRow = items.get(0);
LocalDate groupDate = parseDate(firstRow.getBizDate());
Integer groupHour = parseMeal(firstRow.getMealName());
String comboHint = StrUtil.blankToDefault(StrUtil.trim(firstRow.getComboName()),
StrUtil.trim(firstRow.getDishName()));
Long menuId = items.stream().map(CanteenDailyMenuImportExcel::getId).map(CanteenDailyMenuImportHelper::parseExcelLong)
.filter(Objects::nonNull).findFirst().orElse(null);
if (menuId != null) {
CanteenDailyMenuEntity existMenu = canteenDailyMenuService.getById(menuId);
if (existMenu == null) {
throw new IllegalArgumentException(String.format(
"%s %s「%s」菜谱ID %s 不存在,请检查是否复制错了或被 Excel 改坏;若是新排菜请清空该行菜谱ID",
groupDate, formatMeal(groupHour), comboHint, menuId));
}
if (!merchantId.equals(existMenu.getMerchantId())) {
throw new IllegalArgumentException(String.format(
"%s %s「%s」菜谱ID %s 不属于当前商户,请勿串用其他食堂的导出表",
groupDate, formatMeal(groupHour), comboHint, menuId));
}
if (groupDate != null && existMenu.getStartTime() != null
&& !groupDate.equals(existMenu.getStartTime())) {
throw new IllegalArgumentException(String.format(
"%s %s「%s」菜谱ID %s 在系统中属于 %s %s,不能用在当前日期;请改正 ID 或清空后按新增导入",
groupDate, formatMeal(groupHour), comboHint, menuId,
existMenu.getStartTime(), formatMeal(existMenu.getHourType())));
}
if (groupHour != null && existMenu.getHourType() != null
&& !groupHour.equals(existMenu.getHourType())) {
throw new IllegalArgumentException(String.format(
"%s %s「%s」菜谱ID %s 在系统中属于 %s,不能用在当前餐段;请改正 ID 或清空后按新增导入",
groupDate, formatMeal(groupHour), comboHint, menuId,
formatMeal(existMenu.getHourType())));
}
String err = upsertGroupById(menuId, items, merchantId, deptId, categoryId, categoryMap, cache);
if (err != null) {
errors.add(err);
}
else {
updated++;
// 事务内勿 return R.failed:内层已写库/抛错会标 rollback-only,提交会报 UnexpectedRollback
throw new IllegalArgumentException(err);
}
updated++;
continue;
}
CanteenDailyMenuImportExcel first = items.get(0);
LocalDate date = parseDate(first.getBizDate());
Integer hourType = parseMeal(first.getMealName());
CanteenDailyMenuImportExcel first = firstRow;
LocalDate date = groupDate;
Integer hourType = groupHour;
String comboName = StrUtil.trim(first.getComboName());
if (StrUtil.isBlank(comboName) || isSingleComboMarker(comboName)) {
for (CanteenDailyMenuImportExcel item : items) {
try {
FindOrCreate r = findOrCreateDish(merchantId, deptId,
item.getCategoryName(), categoryId, categoryMap,
item.getDishName(), item.getIngredients(), resolveDishPriceFen(item), item.getStock(),
cache);
if (r.created) {
createdDish++;
}
if (schedule(merchantId, deptId, date, hourType, r.spu.getId(), replaceDay, clearedDayHour,
cache, pendingMenus)) {
scheduled++;
}
FindOrCreate r = findOrCreateDish(merchantId, deptId,
item.getCategoryName(), categoryId, categoryMap,
item.getDishName(), item.getIngredients(), resolveDishPriceFen(item), item.getStock(),
cache);
if (r.created) {
createdDish++;
}
catch (IllegalArgumentException ex) {
errors.add(ex.getMessage());
if (schedule(merchantId, deptId, date, hourType, r.spu.getId(), replaceDay, clearedDayHour,
cache, pendingMenus)) {
scheduled++;
}
}
}
@@ -369,7 +462,7 @@ public class CanteenDailyMenuImportHelper {
List<CanteenSpuEntity> children = new ArrayList<>();
Integer comboPrice = null;
Integer comboStock = first.getStock();
boolean childFailed = false;
Set<String> seenDishKeys = new LinkedHashSet<>();
for (int i = 0; i < items.size(); i++) {
CanteenDailyMenuImportExcel item = items.get(i);
if (i == 0) {
@@ -378,44 +471,39 @@ public class CanteenDailyMenuImportHelper {
comboPrice = fen;
}
}
try {
FindOrCreate r = findOrCreateDish(merchantId, deptId,
item.getCategoryName(), categoryId, categoryMap,
item.getDishName(), null, resolveDishPriceFen(item), null, cache);
if (r.created) {
createdDish++;
}
children.add(r.spu);
String dishKey = cleanDishName(item.getDishName());
if (StrUtil.isBlank(dishKey) || !seenDishKeys.add(dishKey)) {
// 同套餐块内同名组成跳过(常见于跨日串组或导出重复)
continue;
}
catch (IllegalArgumentException ex) {
errors.add(ex.getMessage());
childFailed = true;
break;
FindOrCreate r = findOrCreateDish(merchantId, deptId,
item.getCategoryName(), categoryId, categoryMap,
item.getDishName(), null, resolveDishPriceFen(item), null, cache);
if (r.created) {
createdDish++;
}
children.add(r.spu);
}
if (childFailed) {
continue;
children = dedupeComboChildren(children);
if (children.isEmpty()) {
throw new IllegalArgumentException(String.format("%s %s 套餐「%s」没有有效组成菜,请检查 Excel",
date, formatMeal(hourType), comboName));
}
if (comboPrice == null) {
comboPrice = 0;
}
String mainName = children.get(0).getName();
String fullComboName = comboName + "-" + mainName;
try {
FindOrCreate combo = findOrCreateCombo(merchantId, deptId,
first.getCategoryName(), categoryId, categoryMap,
fullComboName, comboName, comboPrice, comboStock, children,
resolveComboIngredients(first), cache);
if (combo.created) {
createdCombo++;
}
if (schedule(merchantId, deptId, date, hourType, combo.spu.getId(), replaceDay, clearedDayHour,
cache, pendingMenus)) {
scheduled++;
}
FindOrCreate combo = findOrCreateCombo(merchantId, deptId,
first.getCategoryName(), categoryId, categoryMap,
fullComboName, comboName, comboPrice, comboStock, children,
resolveComboIngredients(first), cache);
if (combo.created) {
createdCombo++;
}
catch (IllegalArgumentException ex) {
errors.add(ex.getMessage());
if (schedule(merchantId, deptId, date, hourType, combo.spu.getId(), replaceDay, clearedDayHour,
cache, pendingMenus)) {
scheduled++;
}
}
if (pendingMenus.size() >= MENU_BATCH_SIZE) {
@@ -423,12 +511,37 @@ public class CanteenDailyMenuImportHelper {
}
}
flushPendingMenus(pendingMenus);
if (CollUtil.isNotEmpty(errors)) {
return R.failed(String.join(";", errors.subList(0, Math.min(errors.size(), 20))));
}
return R.ok(null, String.format("导入成功:更新 %d,新建单品 %d,新建套餐 %d,新增排菜 %d",
updated, createdDish, createdCombo, scheduled));
String msg = String.format("导入成功:更新 %d,新建单品 %d,新建套餐 %d,新增排菜 %d",
updated, createdDish, createdCombo, scheduled);
return R.ok(null, msg);
}
/**
* 解析 Excel 中的雪花 ID。必须用字符串/BigDecimal,禁止经 Double,否则超过 15 位会失真。
*/
private static Long parseExcelLong(String raw) {
if (StrUtil.isBlank(raw)) {
return null;
}
String s = raw.trim();
if (s.endsWith(".0")) {
s = s.substring(0, s.length() - 2);
}
try {
if (s.contains("E") || s.contains("e")) {
return new BigDecimal(s).toBigInteger().longValueExact();
}
return Long.parseLong(s);
}
catch (Exception ex) {
try {
return new BigDecimal(s).toBigInteger().longValueExact();
}
catch (Exception ignored) {
return null;
}
}
}
/**
@@ -514,7 +627,7 @@ public class CanteenDailyMenuImportHelper {
Long defaultCategoryId, Map<String, Long> categoryMap, ImportCache cache) {
CanteenDailyMenuEntity menu = canteenDailyMenuService.getById(menuId);
if (menu == null) {
return "菜谱ID " + menuId + " 不存在";
return "菜谱ID " + menuId + " 不存在(若由Excel导出再导入,多为ID被当成数字丢了末位精度,请重新导出且保持菜谱ID列为文本)";
}
if (!merchantId.equals(menu.getMerchantId())) {
return "菜谱ID " + menuId + " 不属于当前商户";
@@ -565,6 +678,7 @@ public class CanteenDailyMenuImportHelper {
List<CanteenSpuEntity> children = new ArrayList<>();
Integer comboPrice = null;
Integer comboStock = items.get(0).getStock();
Set<String> seenDishKeys = new LinkedHashSet<>();
for (int i = 0; i < items.size(); i++) {
CanteenDailyMenuImportExcel item = items.get(i);
if (i == 0) {
@@ -574,11 +688,19 @@ public class CanteenDailyMenuImportHelper {
if (StrUtil.isBlank(dish)) {
return "菜谱ID " + menu.getId() + ":套餐组成菜品名不能为空";
}
String dishKey = cleanDishName(dish);
if (!seenDishKeys.add(dishKey)) {
continue;
}
FindOrCreate r = findOrCreateDish(merchantId, deptId,
item.getCategoryName(), defaultCategoryId, categoryMap, dish,
null, resolveDishPriceFen(item), null, cache);
children.add(r.spu);
}
children = dedupeComboChildren(children);
if (children.isEmpty()) {
return "菜谱ID " + menu.getId() + ":套餐没有有效组成菜";
}
if (comboPrice == null) {
CanteenSpuEntity existCombo = cache.comboByName.values().stream()
.filter(s -> Objects.equals(s.getId(), menu.getSpuId()))
@@ -760,6 +882,24 @@ public class CanteenDailyMenuImportHelper {
return date + "|" + hourType + "|" + spuId;
}
/** Excel 同一套餐块内菜名/SPU 去重,保留首次出现 */
private static List<CanteenSpuEntity> dedupeComboChildren(List<CanteenSpuEntity> children) {
if (CollUtil.isEmpty(children)) {
return children;
}
Set<Long> ids = new HashSet<>();
List<CanteenSpuEntity> out = new ArrayList<>(children.size());
for (CanteenSpuEntity child : children) {
if (child == null || child.getId() == null) {
continue;
}
if (ids.add(child.getId())) {
out.add(child);
}
}
return out;
}
private static void applyComboIngredients(CanteenSpuEntity combo, String ingredients) {
String importIngredients = normalizeImportIngredients(ingredients);
if (StrUtil.isBlank(importIngredients)) {
@@ -883,7 +1023,7 @@ public class CanteenDailyMenuImportHelper {
}
private static boolean isBlankRow(CanteenDailyMenuImportExcel row) {
return row.getId() == null
return StrUtil.isBlank(row.getId())
&& StrUtil.isBlank(row.getBizDate()) && StrUtil.isBlank(row.getDishName())
&& StrUtil.isBlank(row.getMealName()) && StrUtil.isBlank(row.getComboName());
}
@@ -891,30 +1031,41 @@ public class CanteenDailyMenuImportHelper {
/**
* 合并单元格模板读入后,非首行日期/餐段/套餐名/分类为空,需向下填充。
* 套餐名填「无」「单品」「单点」或「单品-xxx / 单点-xxx」表示单品,不参与套餐继承。
* <p>换日/换餐段时必须清空菜谱ID继承,否则「同名套餐」跨天会被合成一组,组成菜看起来像重复。
*/
private static void normalizeMergedCells(List<CanteenDailyMenuImportExcel> rows) {
String lastDate = null;
String lastMeal = null;
String lastCombo = null;
String lastCategory = null;
Long lastMenuId = null;
Long lastSpuId = null;
String lastMenuId = null;
String lastSpuId = null;
for (CanteenDailyMenuImportExcel row : rows) {
if (isBlankRow(row)) {
continue;
}
if (StrUtil.isBlank(row.getBizDate())) {
String incomingDate = StrUtil.trim(row.getBizDate());
if (StrUtil.isBlank(incomingDate)) {
row.setBizDate(lastDate);
}
else {
lastDate = StrUtil.trim(row.getBizDate());
if (lastDate != null && !incomingDate.equals(lastDate)) {
lastMenuId = null;
lastSpuId = null;
}
lastDate = incomingDate;
row.setBizDate(lastDate);
}
if (StrUtil.isBlank(row.getMealName())) {
String incomingMeal = StrUtil.trim(row.getMealName());
if (StrUtil.isBlank(incomingMeal)) {
row.setMealName(lastMeal);
}
else {
lastMeal = StrUtil.trim(row.getMealName());
if (lastMeal != null && !incomingMeal.equals(lastMeal)) {
lastMenuId = null;
lastSpuId = null;
}
lastMeal = incomingMeal;
row.setMealName(lastMeal);
}
if (StrUtil.isBlank(row.getCategoryName())) {
@@ -944,10 +1095,12 @@ public class CanteenDailyMenuImportHelper {
lastCombo = combo;
row.setComboName(combo);
}
if (row.getId() != null) {
lastMenuId = row.getId();
if (row.getSpuId() != null) {
lastSpuId = row.getSpuId();
if (StrUtil.isNotBlank(row.getId())) {
lastMenuId = StrUtil.trim(row.getId());
row.setId(lastMenuId);
if (StrUtil.isNotBlank(row.getSpuId())) {
lastSpuId = StrUtil.trim(row.getSpuId());
row.setSpuId(lastSpuId);
}
}
else if (lastMenuId != null && StrUtil.isNotBlank(lastCombo)) {
@@ -202,7 +202,10 @@ public class CanteenOnlineSpuServiceImpl extends ServiceImpl<CanteenOnlineSpuMap
continue;
}
if (!childIds.add(item.getChildSpuId())) {
throw new IllegalArgumentException("套餐组成菜品不能重复");
CanteenOnlineSpuEntity dup = baseMapper.selectById(item.getChildSpuId());
String dish = dup != null && StrUtil.isNotBlank(dup.getName()) ? dup.getName()
: String.valueOf(item.getChildSpuId());
throw new IllegalArgumentException("套餐组成菜品不能重复:「" + dish + "」");
}
if (selfId != null && Objects.equals(item.getChildSpuId(), selfId)) {
throw new IllegalArgumentException("套餐不能包含自身");
@@ -549,7 +549,10 @@ public class CanteenSpuServiceImpl extends ServiceImpl<CanteenSpuMapper, Canteen
continue;
}
if (!childIds.add(item.getChildSpuId())) {
throw new IllegalArgumentException("套餐组成菜品不能重复");
CanteenSpuEntity dup = baseMapper.selectById(item.getChildSpuId());
String dish = dup != null && StrUtil.isNotBlank(dup.getName()) ? dup.getName()
: String.valueOf(item.getChildSpuId());
throw new IllegalArgumentException("套餐组成菜品不能重复:「" + dish + "」");
}
if (selfId != null && Objects.equals(item.getChildSpuId(), selfId)) {
throw new IllegalArgumentException("套餐不能包含自身");