每日订餐问题更新
This commit is contained in:
1 parent
4c9bdf073a
commit
cbf28e8d7e
10 files changed
+113
-19
No files matched your search
+1
-1
@@ -116,7 +116,7 @@ public class DailyMenuAppVO {
|
||||
private Integer remainOfflineStock;
|
||||
|
||||
/**
|
||||
* 商品销量,仅用于详情页展示。
|
||||
* 当日移动端已订份数(取 online_sold),供 App 详情「已订」展示;不是 canteen_spu 历史累计销量。
|
||||
*/
|
||||
private Integer salesCount;
|
||||
|
||||
|
||||
+5
-3
@@ -300,17 +300,19 @@ public class CanteenDailyMenuController {
|
||||
* 按硬件订单净销量回填当日线上/线下 sold(配额不动)。
|
||||
* 用于历史 sold 未累计、或怀疑销量漂移时的运维纠偏。
|
||||
*/
|
||||
@Operation(summary = "按订单重算当日分池销量", description = "覆盖 online_sold/offline_sold,不改配额")
|
||||
@Operation(summary = "按订单重算分池销量", description = "覆盖 online_sold/offline_sold,不改配额;传 dailyMenuId 时只重算该行")
|
||||
@SysLog("按订单重算每日菜谱销量")
|
||||
@PutMapping("/syncChannelSold")
|
||||
@PreAuthorize("@pms.hasPermission('canteen_canteenDailyMenu_edit')")
|
||||
public R<Integer> syncChannelSold(@RequestParam("merchantId") Long merchantId,
|
||||
@RequestParam("mealDate") String mealDate) {
|
||||
@RequestParam("mealDate") String mealDate,
|
||||
@RequestParam(value = "dailyMenuId", required = false) Long dailyMenuId) {
|
||||
if (merchantId == null || StrUtil.isBlank(mealDate)) {
|
||||
return R.failed("商户和用餐日不能为空");
|
||||
}
|
||||
try {
|
||||
return canteenDailyMenuService.syncChannelSoldFromOrders(merchantId, LocalDate.parse(mealDate.trim()));
|
||||
return canteenDailyMenuService.syncChannelSoldFromOrders(merchantId, LocalDate.parse(mealDate.trim()),
|
||||
dailyMenuId);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return R.failed("用餐日格式应为 yyyy-MM-dd");
|
||||
|
||||
+9
@@ -469,6 +469,15 @@ public class CanteenSpuController {
|
||||
return R.ok(canteenSpuService.loadComboItemVos(spuIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 反查「含该组成单品」的套餐及每份含本菜数量,供每日菜谱相关订单并入套餐单。
|
||||
*/
|
||||
@GetMapping("/comboParentsByChild")
|
||||
@PreAuthorize("@pms.hasPermission('canteen_canteenDailyMenu_view')")
|
||||
public R<Map<Long, Integer>> comboParentsByChild(@RequestParam("childSpuId") Long childSpuId) {
|
||||
return R.ok(canteenSpuService.mapComboQtyByChildSpuId(childSpuId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 为无用户令牌的 Hardware 异步打印任务提供套餐组成查询,复用管理端相同的数据装配逻辑。
|
||||
*
|
||||
|
||||
+7
@@ -116,4 +116,11 @@ public interface CanteenDailyMenuService extends IService<CanteenDailyMenuEntity
|
||||
* @return 实际改写行数
|
||||
*/
|
||||
R<Integer> syncChannelSoldFromOrders(Long merchantId, LocalDate mealDate);
|
||||
|
||||
/**
|
||||
* 同 {@link #syncChannelSoldFromOrders(Long, LocalDate)};{@code dailyMenuId} 非空时只重算该菜谱行。
|
||||
*
|
||||
* @param dailyMenuId 每日菜谱行 ID,空则重算当日全部
|
||||
*/
|
||||
R<Integer> syncChannelSoldFromOrders(Long merchantId, LocalDate mealDate, Long dailyMenuId);
|
||||
}
|
||||
+8
@@ -129,6 +129,14 @@ public interface CanteenSpuService extends IService<CanteenSpuEntity> {
|
||||
*/
|
||||
Map<Long, List<OnlineSpuComboItemVO>> loadComboItemVos(List<Long> comboSpuIds);
|
||||
|
||||
/**
|
||||
* 查出「组成中含该单品」的套餐及其每份套餐含本菜份数,供相关订单并入套餐单并折算本菜份数。
|
||||
*
|
||||
* @param childSpuId 组成单品 SPU ID
|
||||
* @return key=套餐 SPU ID,value=每份套餐含本菜数量;无则空 Map
|
||||
*/
|
||||
Map<Long, Integer> mapComboQtyByChildSpuId(Long childSpuId);
|
||||
|
||||
/**
|
||||
* 按购物车商品 ID 批量加载打印凭证名称(套餐名优先、商品名兜底)。
|
||||
*
|
||||
|
||||
+37
-9
@@ -506,16 +506,39 @@ public class CanteenDailyMenuServiceImpl extends ServiceImpl<CanteenDailyMenuMap
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<Integer> syncChannelSoldFromOrders(Long merchantId, LocalDate mealDate) {
|
||||
return syncChannelSoldFromOrders(merchantId, mealDate, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<Integer> syncChannelSoldFromOrders(Long merchantId, LocalDate mealDate, Long dailyMenuId) {
|
||||
if (merchantId == null || mealDate == null) {
|
||||
return R.failed("商户和用餐日不能为空");
|
||||
}
|
||||
List<CanteenDailyMenuEntity> menus = list(Wrappers.<CanteenDailyMenuEntity>lambdaQuery()
|
||||
.eq(CanteenDailyMenuEntity::getMerchantId, merchantId)
|
||||
.eq(CanteenDailyMenuEntity::getStartTime, mealDate));
|
||||
List<CanteenDailyMenuEntity> menus;
|
||||
if (dailyMenuId != null) {
|
||||
CanteenDailyMenuEntity one = getById(dailyMenuId);
|
||||
if (one == null) {
|
||||
return R.failed("菜谱行不存在");
|
||||
}
|
||||
if (!Objects.equals(one.getMerchantId(), merchantId)) {
|
||||
return R.failed("菜谱行不属于当前商户");
|
||||
}
|
||||
if (one.getStartTime() == null || !mealDate.equals(one.getStartTime())) {
|
||||
return R.failed("菜谱行用餐日与所选日期不一致");
|
||||
}
|
||||
menus = List.of(one);
|
||||
}
|
||||
else {
|
||||
menus = list(Wrappers.<CanteenDailyMenuEntity>lambdaQuery()
|
||||
.eq(CanteenDailyMenuEntity::getMerchantId, merchantId)
|
||||
.eq(CanteenDailyMenuEntity::getStartTime, mealDate));
|
||||
}
|
||||
if (CollectionUtils.isEmpty(menus)) {
|
||||
return R.ok(0, "当日无菜谱记录");
|
||||
}
|
||||
|
||||
// 单行重算也要拉当日全量净销量:组成单品需叠加「含本菜」套餐单的摊销量
|
||||
DailyMenuNetSoldQueryDTO query = new DailyMenuNetSoldQueryDTO();
|
||||
query.setMerchantId(merchantId);
|
||||
query.setMealDate(mealDate);
|
||||
@@ -543,12 +566,16 @@ public class CanteenDailyMenuServiceImpl extends ServiceImpl<CanteenDailyMenuMap
|
||||
}
|
||||
}
|
||||
|
||||
// 套餐订单销量摊到同日同餐段已排菜的组成单品(与实时扣减联动一致)
|
||||
Set<Long> menuSpuIds = menus.stream().map(CanteenDailyMenuEntity::getSpuId).filter(Objects::nonNull)
|
||||
// 套餐联动范围:单行时仍按「当日全部菜谱键」判断组成是否已排菜,与全量重算口径一致
|
||||
List<CanteenDailyMenuEntity> dayMenus = dailyMenuId == null ? menus
|
||||
: list(Wrappers.<CanteenDailyMenuEntity>lambdaQuery()
|
||||
.eq(CanteenDailyMenuEntity::getMerchantId, merchantId)
|
||||
.eq(CanteenDailyMenuEntity::getStartTime, mealDate));
|
||||
Set<Long> menuSpuIds = dayMenus.stream().map(CanteenDailyMenuEntity::getSpuId).filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
Map<Long, List<com.bsd.pjcloud.canteen.vo.app.OnlineSpuComboItemVO>> comboMap =
|
||||
canteenSpuService.loadComboItemVos(new ArrayList<>(menuSpuIds));
|
||||
Set<String> menuKeys = menus.stream()
|
||||
Set<String> menuKeys = dayMenus.stream()
|
||||
.filter(m -> m.getHourType() != null && m.getSpuId() != null)
|
||||
.map(m -> m.getHourType() + "|" + m.getSpuId())
|
||||
.collect(Collectors.toSet());
|
||||
@@ -612,9 +639,10 @@ public class CanteenDailyMenuServiceImpl extends ServiceImpl<CanteenDailyMenuMap
|
||||
if (CollUtil.isNotEmpty(toUpdate)) {
|
||||
updateBatchById(toUpdate);
|
||||
}
|
||||
log.info("每日菜谱按订单重算销量 merchantId={} mealDate={} menuRows={} updated={}",
|
||||
merchantId, mealDate, menus.size(), toUpdate.size());
|
||||
return R.ok(toUpdate.size(), "已按订单重算 " + toUpdate.size() + " 条销量(共 " + menus.size() + " 条菜谱)");
|
||||
log.info("每日菜谱按订单重算销量 merchantId={} mealDate={} dailyMenuId={} menuRows={} updated={}",
|
||||
merchantId, mealDate, dailyMenuId, menus.size(), toUpdate.size());
|
||||
String scope = dailyMenuId == null ? ("共 " + menus.size() + " 条菜谱") : "本菜 1 条";
|
||||
return R.ok(toUpdate.size(), "已按订单重算 " + toUpdate.size() + " 条销量(" + scope + ")");
|
||||
}
|
||||
|
||||
private static String weekdayLabel(DayOfWeek dayOfWeek) {
|
||||
|
||||
+23
@@ -895,6 +895,29 @@ public class CanteenSpuServiceImpl extends ServiceImpl<CanteenSpuMapper, Canteen
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, Integer> mapComboQtyByChildSpuId(Long childSpuId) {
|
||||
if (childSpuId == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
List<CanteenSpuComboItemEntity> lines = comboItemMapper.selectList(
|
||||
Wrappers.<CanteenSpuComboItemEntity>lambdaQuery()
|
||||
.select(CanteenSpuComboItemEntity::getComboSpuId, CanteenSpuComboItemEntity::getQuantity)
|
||||
.eq(CanteenSpuComboItemEntity::getChildSpuId, childSpuId));
|
||||
if (CollUtil.isEmpty(lines)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<Long, Integer> result = new LinkedHashMap<>();
|
||||
for (CanteenSpuComboItemEntity line : lines) {
|
||||
if (line == null || line.getComboSpuId() == null) {
|
||||
continue;
|
||||
}
|
||||
int qty = line.getQuantity() == null || line.getQuantity() < 1 ? 1 : line.getQuantity();
|
||||
result.merge(line.getComboSpuId(), qty, Integer::sum);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SpuPrintNameVo> loadSpuPrintNames(List<Long> productIds) {
|
||||
if (CollUtil.isEmpty(productIds)) {
|
||||
|
||||
+5
-4
@@ -99,7 +99,8 @@
|
||||
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,
|
||||
<!-- 每日菜品「已订/已售」展示当日移动端已订,勿用 canteen_spu 历史累计销量 -->
|
||||
IFNULL(t.online_sold, 0) AS sales_count,
|
||||
sp.sort,
|
||||
sp.is_show,
|
||||
c.id AS category_id,
|
||||
@@ -154,7 +155,7 @@
|
||||
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,
|
||||
IFNULL(t.online_sold, 0) AS sales_count,
|
||||
sp.sort,
|
||||
sp.is_show,
|
||||
c.id AS category_id,
|
||||
@@ -197,7 +198,7 @@
|
||||
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,
|
||||
IFNULL(t.online_sold, 0) AS sales_count,
|
||||
sp.sort,
|
||||
sp.is_show,
|
||||
c.id AS category_id,
|
||||
@@ -283,7 +284,7 @@
|
||||
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,
|
||||
IFNULL(m.online_sold, 0) AS sales_count,
|
||||
s.pic_url,
|
||||
s.slider_pic_urls,
|
||||
s.introduction,
|
||||
|
||||
+6
@@ -73,6 +73,12 @@ public class HardOrderDto extends HardOrderEntity implements BaseIsIncludeTreeIn
|
||||
*/
|
||||
private Long productId;
|
||||
|
||||
/**
|
||||
* 购物车商品 SPU ID 集合;用于含套餐父商品的「相关订单」查询(本菜 + 含本菜的套餐)。
|
||||
* 与 {@link #productId} 同时有值时取并集语义(XML 用 IN)。
|
||||
*/
|
||||
private List<Long> productIds;
|
||||
|
||||
private LocalDateTime[] refundTimeList;
|
||||
|
||||
/** 预计使用日期范围。 */
|
||||
|
||||
+12
-2
@@ -239,12 +239,22 @@
|
||||
<if test="dto.apptUseDateTimeList != null and dto.apptUseDateTimeList.length == 2">
|
||||
and a.appt_use_date between #{dto.apptUseDateTimeList[0]} and #{dto.apptUseDateTimeList[1]}
|
||||
</if>
|
||||
<if test="dto.productId != null">
|
||||
<if test="dto.productId != null or (dto.productIds != null and dto.productIds.size() != 0)">
|
||||
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}
|
||||
<choose>
|
||||
<when test="dto.productIds != null and dto.productIds.size() != 0">
|
||||
and c.product_id in
|
||||
<foreach collection="dto.productIds" item="v" open="(" close=")" separator=",">
|
||||
#{v}
|
||||
</foreach>
|
||||
</when>
|
||||
<otherwise>
|
||||
and c.product_id = #{dto.productId}
|
||||
</otherwise>
|
||||
</choose>
|
||||
)
|
||||
</if>
|
||||
<if test="dto.apptUseDateStart != null">
|
||||
|
||||
Reference in new issue
Block a user