diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/api/feign/RemoteCanteenSpuService.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/api/feign/RemoteCanteenSpuService.java index c168bf846..983f094bc 100644 --- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/api/feign/RemoteCanteenSpuService.java +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/api/feign/RemoteCanteenSpuService.java @@ -46,18 +46,24 @@ public interface RemoteCanteenSpuService { /** - * 获取菜品列表 + * 获取菜品列表(有每日菜谱时 stock=当日线下剩余) * @param categoryId 不填默认查全部 + * @param day 菜谱日期 yyyy-MM-dd,不填默认当天 + * @param hourType 餐段,不填按分类解析 * @return */ @GetMapping("/canteenSpu/ad/getCanteenSpuList") - R getCanteenSpuList(@RequestParam(value = "categoryId",required = false)String categoryId); + R getCanteenSpuList(@RequestParam(value = "categoryId",required = false)String categoryId, + @RequestParam(value = "day", required = false) String day, + @RequestParam(value = "hourType", required = false) Integer hourType); /** - * 获取菜品详情(含详情富文本、套餐组成) + * 获取菜品详情(含详情富文本、套餐组成);有每日菜谱时 stock=线下剩余 */ @GetMapping("/canteenSpu/ad/getCanteenSpuDetails") - R getCanteenSpuDetails(@RequestParam("id") Long id); + R getCanteenSpuDetails(@RequestParam("id") Long id, + @RequestParam(value = "day", required = false) String day, + @RequestParam(value = "hourType", required = false) Integer hourType); /** diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenDailyMenuEntity.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenDailyMenuEntity.java index 833570834..c173cd4c2 100644 --- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenDailyMenuEntity.java +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenDailyMenuEntity.java @@ -50,17 +50,29 @@ public class CanteenDailyMenuEntity extends Model { private Integer hourType; /** - * 线上可预约库存配额(人工设置;剩余=配额−线上净销量) + * 线上可预约库存配额(人工设置;下单不改;剩余=配额−onlineSold) */ @Schema(description = "线上可预约库存配额") private Integer onlineStock; /** - * 线下销售库存配额(人工设置;剩余对外字段名仍用 stock) + * 线下销售库存配额(人工设置;下单不改;剩余=配额−offlineSold) */ @Schema(description = "线下销售库存配额") private Integer offlineStock; + /** + * 线上净销量(下单增加、售后完成减少) + */ + @Schema(description = "线上净销量") + private Integer onlineSold; + + /** + * 线下净销量(下单增加、售后完成减少) + */ + @Schema(description = "线下净销量") + private Integer offlineSold; + /** * 商户id */ diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/ADCanteenSpuListVO.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/ADCanteenSpuListVO.java index 71236c187..ae8c74980 100644 --- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/ADCanteenSpuListVO.java +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/ADCanteenSpuListVO.java @@ -65,9 +65,9 @@ public class ADCanteenSpuListVO { /** - * 库存 + * 库存:无每日菜谱时为 canteen_spu.stock;有菜谱时 AD=线下剩余、App 详情=线上剩余 */ - @Schema(description="库存") + @Schema(description="库存(有每日菜谱时为通道剩余)") private Integer stock; diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/DailyMenuAdminVO.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/DailyMenuAdminVO.java index 71d25c964..4c27dcfd6 100644 --- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/DailyMenuAdminVO.java +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/DailyMenuAdminVO.java @@ -53,6 +53,12 @@ public class DailyMenuAdminVO { */ private Integer stock; + /** 线上净销量(仅服务端填剩余用,可不展示) */ + private Integer onlineSold; + + /** 线下净销量(仅服务端填剩余用,可不展示) */ + private Integer offlineSold; + /** 线上剩余 = onlineStock − 预约净销量 */ private Integer remainOnlineStock; diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/app/DailyMenuAppVO.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/app/DailyMenuAppVO.java index a1256764c..01d268be3 100644 --- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/app/DailyMenuAppVO.java +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/app/DailyMenuAppVO.java @@ -82,12 +82,24 @@ public class DailyMenuAppVO { */ private Integer stock; - /** 线上配额(可选透出) */ + /** 线上配额(人工;下单不改) */ private Integer onlineStock; - /** 线下配额(可选透出) */ + /** 线下配额(人工;下单不改) */ private Integer offlineStock; + /** 线上净销量(服务端计算剩余用) */ + private Integer onlineSold; + + /** 线下净销量(服务端计算剩余用) */ + private Integer offlineSold; + + /** 线上剩余 = onlineStock − onlineSold */ + private Integer remainOnlineStock; + + /** 线下剩余 = offlineStock − offlineSold */ + private Integer remainOfflineStock; + /** * 商品销量,仅用于详情页展示。 */ 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 402a2d399..a34a5510c 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 @@ -26,7 +26,6 @@ import com.bsd.pjcloud.canteen.vo.erp.CanteenSpuSimpleVO; import com.bsd.pjcloud.canteen.vo.erp.CanteenSpuStockVO; import com.bsd.pjcloud.common.core.exception.ErrorCodes; import com.bsd.pjcloud.common.core.util.MsgUtils; -import com.bsd.pjcloud.common.core.util.BeanUtils; import com.bsd.pjcloud.common.core.util.R; import com.bsd.pjcloud.common.log.annotation.SysLog; import com.bsd.pjcloud.canteen.entity.CanteenSpuEntity; @@ -45,7 +44,6 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -339,57 +337,68 @@ public class CanteenSpuController { /** - * 获取菜品列表 + * 获取菜品列表(有每日菜谱时 stock=当日线下剩余) * * @param categoryId 分类编号,不传默认查询全部 - * @return + * @param day 菜谱日期 yyyy-MM-dd,不传默认当天 + * @param hourType 餐段,不传则按分类 categoryType 解析 */ @GetMapping("/ad/getCanteenSpuList") - public R getCanteenSpuList(@RequestParam(value = "categoryId", required = false) String categoryId) { - return canteenSpuService.getCanteenSpuList(categoryId); + public R getCanteenSpuList(@RequestParam(value = "categoryId", required = false) String categoryId, + @RequestParam(value = "day", required = false) String day, + @RequestParam(value = "hourType", required = false) Integer hourType) { + return canteenSpuService.getCanteenSpuList(categoryId, day, hourType); } /** * 查询收银终端使用的每日菜品详情,并补充富文本与套餐组成。 + * stock:有每日菜谱行时为线下剩余。 * * @param id 每日菜品 SPU ID + * @param day 菜谱日期,空则当天 + * @param hourType 餐段,空则按分类解析 * @return 每日菜品详情;商品不存在时返回失败响应 */ @GetMapping("/ad/getCanteenSpuDetails") - public R getCanteenSpuDetails(@RequestParam("id") Long id) { - CanteenSpuEntity detail = canteenSpuService.getDetail(id); - if (detail == null) { + public R getCanteenSpuDetails(@RequestParam("id") Long id, + @RequestParam(value = "day", required = false) String day, + @RequestParam(value = "hourType", required = false) Integer hourType) { + ADCanteenSpuListVO vo = canteenSpuService.buildAdSpuDetails(id, day, hourType, true); + if (vo == null) { return R.failed(MsgUtils.getMessage(ErrorCodes.COMMON_PRODUCT_NOT_FOUND)); } - ADCanteenSpuListVO vo = BeanUtils.toBean(detail, ADCanteenSpuListVO.class); - vo.setIntroduction(detail.getIntroduction()); - vo.setDescription(detail.getDescription()); - int type = detail.getSpuType() == null ? 1 : detail.getSpuType(); - vo.setSpuType(type); - if (type == 2) { - vo.setComboItems(canteenSpuService.loadComboItemVos(Collections.singletonList(id)) - .getOrDefault(id, Collections.emptyList())); - } - else { - vo.setComboItems(Collections.emptyList()); - } return R.ok(vo); } /** * 按每日菜品 SPU ID 查询 App 商品详情。 + * stock:有每日菜谱行时为线上剩余。 * * @param id 套餐组成返回的 childSpuId,关联 canteen_spu 主键 + * @param day 菜谱日期,空则当天 + * @param hourType 餐段,空则按分类解析 * @return App 可展示的每日菜品详情;商品不存在时返回失败响应 */ @ApiCryptoAes @Operation(summary = "查询每日菜品详情", description = "通过每日菜品 SPU ID 查询富文本和套餐组成") @GetMapping("/app/getCanteenSpuDetails") - public R getAppCanteenSpuDetails(@RequestParam("id") Long id) { - return getCanteenSpuDetails(id); + public R getAppCanteenSpuDetails(@RequestParam("id") Long id, + @RequestParam(value = "day", required = false) String day, + @RequestParam(value = "hourType", required = false) Integer hourType) { + ADCanteenSpuListVO vo = canteenSpuService.buildAdSpuDetails(id, day, hourType, false); + if (vo == null) { + return R.failed(MsgUtils.getMessage(ErrorCodes.COMMON_PRODUCT_NOT_FOUND)); + } + return R.ok(vo); } + /** + * 这里有个问题,就是因为每日加了库存,为了不影响原有的安卓消费机调用扣减库存的逻辑 + * 因此这里传参还是效验了spuid,因为之前的库存在菜品列表里,所以为什么不直接拿菜谱id去扣减,用「商户+日期+餐段+SPU」去定位 + * @param updateStockReqDTO + * @return + */ @PostMapping("/ad/updateSkuStock") @SysLog("更新菜品库存") @PreAuthorize("@pms.hasPermission('canteen_canteenSpu_edit')") diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenDailyMenuMapper.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenDailyMenuMapper.java index 24501c78b..3090da9b4 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenDailyMenuMapper.java +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenDailyMenuMapper.java @@ -64,14 +64,14 @@ public interface CanteenDailyMenuMapper extends PjcloudBaseMapper { /** - * 根据菜品分类id获取菜品信息 - * @param categoryId - * @return + * 安卓消费机菜品列表;有每日菜谱行时 stock 为当日线下剩余。 + * + * @param categoryId 分类,空则全部 + * @param day 菜谱日期 yyyy-MM-dd,空则当天 + * @param hourType 餐段,空则按分类 categoryType 解析 */ - R getCanteenSpuList(String categoryId); + R getCanteenSpuList(String categoryId, String day, Integer hourType); + + /** + * 组装安卓/App 菜品详情 VO,并按通道覆盖每日菜谱剩余库存。 + * + * @param id SPU ID + * @param day 菜谱日期,空则当天 + * @param hourType 餐段,空则按分类解析 + * @param offline true=线下剩余(AD),false=线上剩余(App) + */ + ADCanteenSpuListVO buildAdSpuDetails(Long id, String day, Integer hourType, boolean offline); /** diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenDailyMenuImportHelper.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenDailyMenuImportHelper.java index 3aa200d02..b7c75336d 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenDailyMenuImportHelper.java +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenDailyMenuImportHelper.java @@ -1459,7 +1459,7 @@ public class CanteenDailyMenuImportHelper { * 解析/创建单品。 *

有「菜品SPU ID」→ 按 ID 复用并按需更新;无 ID → 一律新建,不再按菜名复用。 *

{@code stock} 仅在显式传入时更新 {@code canteen_spu.stock};每日菜谱导入传 {@code null}, - * 当日可售走 {@code canteen_daily_menu.online_stock/offline_stock}。 + * 当日可售 = 配额(online_stock/offline_stock) − 销量;导入只写配额,不改销量。 */ private FindOrCreate findOrCreateDish(Long merchantId, Long deptId, String categoryName, Long defaultCategoryId, Map categoryMap, String name, String ingredients, Integer priceFen, Integer stock, diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenDailyMenuStockSupport.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenDailyMenuStockSupport.java index 81349bbeb..2e99f0a19 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenDailyMenuStockSupport.java +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenDailyMenuStockSupport.java @@ -1,21 +1,27 @@ package com.bsd.pjcloud.canteen.service.impl; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.bsd.pjcloud.canteen.entity.CanteenDailyMenuEntity; +import com.bsd.pjcloud.canteen.vo.ADCanteenSpuListVO; import com.bsd.pjcloud.canteen.vo.DailyMenuAdminVO; import com.bsd.pjcloud.canteen.vo.app.DailyMenuAppVO; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import java.time.LocalDate; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; /** * 每日菜谱分池库存展示。 *

- * {@code online_stock}/{@code offline_stock} 为可售剩余(下单扣减、退货加回); - * App 的 {@code stock}=线上剩余,安卓 AD 的 {@code stock}=线下剩余(字段名兼容旧客户端)。 + * {@code online_stock}/{@code offline_stock} 为人工配额(下单不改); + * {@code online_sold}/{@code offline_sold} 为净销量;剩余 = 配额 − 销量。 + * App 的 {@code stock}=线上剩余,安卓 AD 的 {@code stock}=线下剩余。 */ @Component @RequiredArgsConstructor @@ -27,17 +33,30 @@ public class CanteenDailyMenuStockSupport { return quota == null || quota < 0 ? DEFAULT_QUOTA : quota; } - /** App:stock = 线上剩余 */ + public static int soldOrDefault(Integer sold) { + return sold == null || sold < 0 ? 0 : sold; + } + + /** 剩余 = max(0, 配额 − 销量) */ + public static int remainOf(Integer quota, Integer sold) { + return Math.max(0, quotaOrDefault(quota) - soldOrDefault(sold)); + } + + public static int channelRemain(CanteenDailyMenuEntity menu, boolean offline) { + if (menu == null) { + return 0; + } + return offline ? remainOf(menu.getOfflineStock(), menu.getOfflineSold()) + : remainOf(menu.getOnlineStock(), menu.getOnlineSold()); + } + + /** App:stock = 线上剩余;onlineStock/offlineStock 仍透出配额 */ public void fillAppOnlineRemain(List rows, Long merchantId, LocalDate mealDate) { if (CollUtil.isEmpty(rows)) { return; } for (DailyMenuAppVO row : rows) { - int online = quotaOrDefault(row.getOnlineStock()); - int offline = quotaOrDefault(row.getOfflineStock()); - row.setOnlineStock(online); - row.setOfflineStock(offline); - row.setStock(online); + applyAppQuotasAndRemains(row, true); } } @@ -47,11 +66,7 @@ public class CanteenDailyMenuStockSupport { return; } for (DailyMenuAppVO row : rows) { - int online = quotaOrDefault(row.getOnlineStock()); - int offline = quotaOrDefault(row.getOfflineStock()); - row.setOnlineStock(online); - row.setOfflineStock(offline); - row.setStock(offline); + applyAppQuotasAndRemains(row, false); } } @@ -65,32 +80,121 @@ public class CanteenDailyMenuStockSupport { for (Map.Entry> e : byDate.entrySet()) { fillAdOfflineRemain(e.getValue(), merchantId, e.getKey()); } - // 无日期行仍规范默认值 for (DailyMenuAppVO row : rows) { if (row.getStartTime() == null) { - int online = quotaOrDefault(row.getOnlineStock()); - int offline = quotaOrDefault(row.getOfflineStock()); - row.setOnlineStock(online); - row.setOfflineStock(offline); - row.setStock(offline); + applyAppQuotasAndRemains(row, false); } } } - /** PC:配额即当前剩余;合计供旧列 remainStock */ + /** PC:配额与剩余分列展示 */ public void fillAdminRemain(List rows, Long merchantId, LocalDate mealDate) { if (CollUtil.isEmpty(rows)) { return; } for (DailyMenuAdminVO row : rows) { - int online = quotaOrDefault(row.getOnlineStock()); - int offline = quotaOrDefault(row.getOfflineStock()); - row.setOnlineStock(online); - row.setOfflineStock(offline); - row.setRemainOnlineStock(online); - row.setRemainOfflineStock(offline); - row.setStock(offline); - row.setRemainStock(online + offline); + int onlineQuota = quotaOrDefault(row.getOnlineStock()); + int offlineQuota = quotaOrDefault(row.getOfflineStock()); + int onlineRemain = remainOf(onlineQuota, row.getOnlineSold()); + int offlineRemain = remainOf(offlineQuota, row.getOfflineSold()); + row.setOnlineStock(onlineQuota); + row.setOfflineStock(offlineQuota); + row.setRemainOnlineStock(onlineRemain); + row.setRemainOfflineStock(offlineRemain); + row.setStock(offlineRemain); + row.setRemainStock(onlineRemain + offlineRemain); } } + + private static void applyAppQuotasAndRemains(DailyMenuAppVO row, boolean stockAsOnline) { + int onlineQuota = quotaOrDefault(row.getOnlineStock()); + int offlineQuota = quotaOrDefault(row.getOfflineStock()); + int onlineRemain = remainOf(onlineQuota, row.getOnlineSold()); + int offlineRemain = remainOf(offlineQuota, row.getOfflineSold()); + row.setOnlineStock(onlineQuota); + row.setOfflineStock(offlineQuota); + row.setRemainOnlineStock(onlineRemain); + row.setRemainOfflineStock(offlineRemain); + row.setStock(stockAsOnline ? onlineRemain : offlineRemain); + } + + /** + * 安卓 SPU 查看接口:若当日菜谱存在对应行,则 {@code stock} 覆盖为线下剩余;无菜谱行则保留 canteen_spu.stock。 + * + * @param rows 已组装的 AD VO(含 merchantId、categoryType) + * @param menus 当日(或指定日)相关每日菜谱行 + * @param hourTypeOverride 请求显式餐段;为空时从 VO.categoryType 解析 + * @param offline true=线下剩余(AD),false=线上剩余(App 详情复用) + */ + public void overlaySpuViewStock(List rows, List menus, + Integer hourTypeOverride, boolean offline) { + if (CollUtil.isEmpty(rows) || CollUtil.isEmpty(menus)) { + return; + } + Map byKey = new HashMap<>(); + for (CanteenDailyMenuEntity menu : menus) { + if (menu == null || menu.getMerchantId() == null || menu.getSpuId() == null || menu.getHourType() == null) { + continue; + } + byKey.put(menuKey(menu.getMerchantId(), menu.getHourType(), menu.getSpuId()), menu); + } + for (ADCanteenSpuListVO row : rows) { + if (row == null || row.getMerchantId() == null || row.getId() == null) { + continue; + } + Integer hourType = hourTypeOverride != null ? hourTypeOverride : parseHourType(row.getCategoryType()); + if (hourType == null) { + // 分类未标餐段时:同一商户+SPU 当日若只有一行菜谱,仍可覆盖 + CanteenDailyMenuEntity only = findUniqueMenu(menus, row.getMerchantId(), row.getId()); + if (only != null) { + row.setStock(channelRemain(only, offline)); + } + continue; + } + CanteenDailyMenuEntity menu = byKey.get(menuKey(row.getMerchantId(), hourType, row.getId())); + if (menu != null) { + row.setStock(channelRemain(menu, offline)); + } + } + } + + private static CanteenDailyMenuEntity findUniqueMenu(List menus, Long merchantId, + Long spuId) { + CanteenDailyMenuEntity found = null; + for (CanteenDailyMenuEntity menu : menus) { + if (menu == null || !Objects.equals(menu.getMerchantId(), merchantId) + || !Objects.equals(menu.getSpuId(), spuId)) { + continue; + } + if (found != null) { + return null; + } + found = menu; + } + return found; + } + + private static String menuKey(Long merchantId, Integer hourType, Long spuId) { + return merchantId + "|" + hourType + "|" + spuId; + } + + /** categoryType 支持 "2" 或 "2,3";取第一个有效餐段。 */ + public static Integer parseHourType(String categoryType) { + if (StrUtil.isBlank(categoryType)) { + return null; + } + for (String part : categoryType.split(",")) { + String token = StrUtil.trim(part); + if (StrUtil.isBlank(token)) { + continue; + } + try { + return Integer.valueOf(token); + } + catch (Exception ignored) { + return null; + } + } + return null; + } } diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenSpuServiceImpl.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenSpuServiceImpl.java index 1fecfad80..6d0ae49ba 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenSpuServiceImpl.java +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenSpuServiceImpl.java @@ -70,6 +70,7 @@ public class CanteenSpuServiceImpl extends ServiceImpl wrapper) { @@ -206,7 +207,7 @@ public class CanteenSpuServiceImpl extends ServiceImpl current) { CanteenSpuEntity spu = baseMapper.selectById(item.getId()); return R.failed(MsgUtils.getMessage(ErrorCodes.HARD_PRODUCT_STOCK_LOW, @@ -245,7 +245,7 @@ public class CanteenSpuServiceImpl extends ServiceImpl canteenSpuEntities = baseMapper.selectListByScope(new LambdaQueryWrapper() .eq(StringUtils.isNotEmpty(categoryId), CanteenSpuEntity::getCategoryId, categoryId), DataScope.of()); if (CollectionUtils.isEmpty(canteenSpuEntities)) { @@ -404,9 +402,73 @@ public class CanteenSpuServiceImpl extends ServiceImpl rows, String day, Integer hourType, + boolean offline) { + if (CollUtil.isEmpty(rows)) { + return; + } + java.time.LocalDate mealDate = parseMealDateOrToday(day); + List merchantIds = rows.stream().map(ADCanteenSpuListVO::getMerchantId).filter(Objects::nonNull) + .distinct().collect(Collectors.toList()); + List spuIds = rows.stream().map(ADCanteenSpuListVO::getId).filter(Objects::nonNull).distinct() + .collect(Collectors.toList()); + if (CollUtil.isEmpty(merchantIds) || CollUtil.isEmpty(spuIds)) { + return; + } + List menus = canteenDailyMenuMapper.selectList(Wrappers.lambdaQuery() + .in(CanteenDailyMenuEntity::getMerchantId, merchantIds) + .eq(CanteenDailyMenuEntity::getStartTime, mealDate) + .in(CanteenDailyMenuEntity::getSpuId, spuIds)); + dailyMenuStockSupport.overlaySpuViewStock(rows, menus, hourType, offline); + } + + private static java.time.LocalDate parseMealDateOrToday(String day) { + if (StrUtil.isBlank(day)) { + return java.time.LocalDate.now(); + } + try { + return java.time.LocalDate.parse(day.trim()); + } + catch (Exception e) { + return java.time.LocalDate.now(); + } + } + @Override public Object pageScope(Page page, CanteenSpuEntity canteenSpu) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenDailyMenuMapper.xml b/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenDailyMenuMapper.xml index 873aa65bf..7a727fc13 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenDailyMenuMapper.xml +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenDailyMenuMapper.xml @@ -9,6 +9,8 @@ + + @@ -36,6 +38,8 @@ + + @@ -85,6 +89,8 @@ IFNULL(t.offline_stock, 600) AS stock, IFNULL(t.online_stock, 600) AS online_stock, IFNULL(t.offline_stock, 600) AS offline_stock, + IFNULL(t.online_sold, 0) AS online_sold, + IFNULL(t.offline_sold, 0) AS offline_sold, sp.sales_count, sp.sort, sp.is_show, @@ -133,6 +139,8 @@ IFNULL(t.offline_stock, 600) AS stock, IFNULL(t.online_stock, 600) AS online_stock, IFNULL(t.offline_stock, 600) AS offline_stock, + IFNULL(t.online_sold, 0) AS online_sold, + IFNULL(t.offline_sold, 0) AS offline_sold, sp.sales_count, sp.sort, sp.is_show, @@ -171,6 +179,8 @@ IFNULL(m.online_stock, 600) as stock, IFNULL(m.online_stock, 600) as online_stock, IFNULL(m.offline_stock, 600) as offline_stock, + IFNULL(m.online_sold, 0) as online_sold, + IFNULL(m.offline_sold, 0) as offline_sold, s.pic_url, s.sort, s.is_show, @@ -208,6 +218,8 @@ IFNULL(m.online_stock, 600) AS stock, IFNULL(m.online_stock, 600) AS online_stock, IFNULL(m.offline_stock, 600) AS offline_stock, + IFNULL(m.online_sold, 0) AS online_sold, + IFNULL(m.offline_sold, 0) AS offline_sold, s.sales_count, s.pic_url, s.slider_pic_urls, @@ -241,6 +253,8 @@ IFNULL(t.offline_stock, 600) AS stock, IFNULL(t.online_stock, 600) AS online_stock, IFNULL(t.offline_stock, 600) AS offline_stock, + IFNULL(t.online_sold, 0) AS online_sold, + IFNULL(t.offline_sold, 0) AS offline_sold, sp.sort, sp.description AS ingredients, c.id AS category_id, @@ -264,31 +278,38 @@ ORDER BY t.hour_type ASC, sp.sort ASC, t.id ASC + UPDATE canteen_daily_menu - SET online_stock = online_stock + #{delta} + SET online_sold = IFNULL(online_sold, 0) - #{delta} WHERE del_flag = '0' AND merchant_id = #{merchantId} AND start_time = #{mealDate} AND hour_type = #{hourType} AND spu_id = #{spuId} - AND online_stock + #{delta} >= 0 + AND IFNULL(online_sold, 0) - #{delta} >= 0 + AND IFNULL(online_sold, 0) - #{delta} <= IFNULL(online_stock, 600) - AND online_stock = #{expected} + AND (IFNULL(online_stock, 600) - IFNULL(online_sold, 0)) = #{expected} UPDATE canteen_daily_menu - SET offline_stock = offline_stock + #{delta} + SET offline_sold = IFNULL(offline_sold, 0) - #{delta} WHERE del_flag = '0' AND merchant_id = #{merchantId} AND start_time = #{mealDate} AND hour_type = #{hourType} AND spu_id = #{spuId} - AND offline_stock + #{delta} >= 0 + AND IFNULL(offline_sold, 0) - #{delta} >= 0 + AND IFNULL(offline_sold, 0) - #{delta} <= IFNULL(offline_stock, 600) - AND offline_stock = #{expected} + AND (IFNULL(offline_stock, 600) - IFNULL(offline_sold, 0)) = #{expected} diff --git a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/order/HardOrderDto.java b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/order/HardOrderDto.java index 612703ddb..da5bab525 100644 --- a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/order/HardOrderDto.java +++ b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/order/HardOrderDto.java @@ -68,6 +68,11 @@ public class HardOrderDto extends HardOrderEntity implements BaseIsIncludeTreeIn */ private String productName; + /** + * 购物车商品 SPU ID;非空时只返回购物车含该商品的订单(每日菜谱「谁订了这道菜」)。 + */ + private Long productId; + private LocalDateTime[] refundTimeList; /** 预计使用日期范围。 */ diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/order/HardOrderServiceImpl.java b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/order/HardOrderServiceImpl.java index 00c251138..d44bfe87a 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/order/HardOrderServiceImpl.java +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/order/HardOrderServiceImpl.java @@ -2222,9 +2222,10 @@ public class HardOrderServiceImpl extends ServiceImpl entry : required.entrySet()) { DailyMenuAppVO row = menuBySpu.get(entry.getKey()); - int onlineRemain = row.getOnlineStock() == null - ? (row.getStock() == null ? 0 : row.getStock()) - : row.getOnlineStock(); + // 线上剩余:优先 remainOnlineStock;兼容旧字段 stock/onlineStock(历史把剩余写在配额字段) + int onlineRemain = row.getRemainOnlineStock() != null ? row.getRemainOnlineStock() + : (row.getStock() != null ? row.getStock() + : (row.getOnlineStock() == null ? 0 : row.getOnlineStock())); if (onlineRemain < entry.getValue()) { throw new I18nBizException(ErrorCodes.HARD_PRODUCT_STOCK_LOW, row.getName()); } @@ -2742,7 +2743,7 @@ public class HardOrderServiceImpl extends ServiceImpl contexts, int dinerCount, @@ -4666,8 +4667,9 @@ public class HardOrderServiceImpl extends ServiceImpl and a.appt_use_date between #{dto.apptUseDateTimeList[0]} and #{dto.apptUseDateTimeList[1]} + + and exists ( + select 1 from hard_car c + where c.parent_car_id = a.parent_car_id + and c.del_flag = '0' + and c.product_id = #{dto.productId} + ) + and a.appt_use_date >= #{dto.apptUseDateStart}