feat:库存退款
This commit is contained in:
1 parent
7fb7feea3f
commit
4e1c65acfa
4 files changed
+219
-69
No files matched your search
+41
-41
@@ -1,41 +1,41 @@
|
||||
package com.bsd.pjcloud.canteen.dto.report;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 包厢台账(汇总)行数据。
|
||||
* <p>
|
||||
* 导入已改为 {@code CanteenReportBoxExcelBinder} 按规范化表头名称映射,
|
||||
* 不再依赖 EasyExcel {@code @ExcelProperty}(多值 value 会被当成多级表头而非别名)。
|
||||
*/
|
||||
@Data
|
||||
public class CanteenReportBoxExcel {
|
||||
|
||||
private String monthText;
|
||||
|
||||
private String bizDate;
|
||||
|
||||
private String costCenter;
|
||||
|
||||
private String costCenterCode;
|
||||
|
||||
private String empNo;
|
||||
|
||||
private String empName;
|
||||
|
||||
private BigDecimal foodAmount;
|
||||
|
||||
private BigDecimal drinkAmount;
|
||||
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
private String legalEntity;
|
||||
|
||||
private String remark1;
|
||||
|
||||
private String remark2;
|
||||
|
||||
private String park;
|
||||
}
|
||||
package com.bsd.pjcloud.canteen.dto.report;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 包厢台账(汇总)行数据。
|
||||
* <p>
|
||||
* 导入已改为 {@code CanteenReportBoxExcelBinder} 按规范化表头名称映射,
|
||||
* 不再依赖 EasyExcel {@code @ExcelProperty}(多值 value 会被当成多级表头而非别名)。
|
||||
*/
|
||||
@Data
|
||||
public class CanteenReportBoxExcel {
|
||||
|
||||
private String monthText;
|
||||
|
||||
private String bizDate;
|
||||
|
||||
private String costCenter;
|
||||
|
||||
private String costCenterCode;
|
||||
|
||||
private String empNo;
|
||||
|
||||
private String empName;
|
||||
|
||||
private BigDecimal foodAmount;
|
||||
|
||||
private BigDecimal drinkAmount;
|
||||
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
private String legalEntity;
|
||||
|
||||
private String remark1;
|
||||
|
||||
private String remark2;
|
||||
|
||||
private String park;
|
||||
}
|
||||
+1
-1
@@ -45,7 +45,7 @@ class CanteenSpuServiceImplTest {
|
||||
valueOperations = mock(ValueOperations.class);
|
||||
when(redisTemplate.opsForValue()).thenReturn(valueOperations);
|
||||
CanteenSpuServiceImpl target = new CanteenSpuServiceImpl(redisTemplate, null, null, null, null, null,
|
||||
mock(CanteenMerchantScopeHelper.class));
|
||||
mock(CanteenMerchantScopeHelper.class), mock(CanteenDailyMenuStockSupport.class));
|
||||
ReflectionTestUtils.setField(target, "baseMapper", spuMapper);
|
||||
service = target;
|
||||
TransactionSynchronizationManager.initSynchronization();
|
||||
|
||||
+36
-21
@@ -2222,10 +2222,7 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
}
|
||||
for (Map.Entry<Long, Integer> entry : required.entrySet()) {
|
||||
DailyMenuAppVO row = menuBySpu.get(entry.getKey());
|
||||
// 线上剩余:优先 remainOnlineStock;兼容旧字段 stock/onlineStock(历史把剩余写在配额字段)
|
||||
int onlineRemain = row.getRemainOnlineStock() != null ? row.getRemainOnlineStock()
|
||||
: (row.getStock() != null ? row.getStock()
|
||||
: (row.getOnlineStock() == null ? 0 : row.getOnlineStock()));
|
||||
int onlineRemain = dailyOnlineRemain(row);
|
||||
if (onlineRemain < entry.getValue()) {
|
||||
throw new I18nBizException(ErrorCodes.HARD_PRODUCT_STOCK_LOW, row.getName());
|
||||
}
|
||||
@@ -4647,6 +4644,9 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
|
||||
HardParentCarEntity parentCar = hardCarService.getParentCarById(order.getParentCarId());
|
||||
boolean dailyMenu = isDailyMenuParentCar(parentCar);
|
||||
// 每日菜谱分池路由:线上预定订单下单扣 ONLINE 池(updateDailyMenuReserveInventory),退款/重扣必须还同一池;
|
||||
// 机器收银等其他类型下单走 OFFLINE 池。不看订单类型会把预约单的线上销量错补到线下池。
|
||||
boolean onlineChannel = HardOrderTypeEnum.RESERVE.getType().equals(order.getOrderType());
|
||||
LocalDate mealDate = order.getApptUseDate();
|
||||
Integer hourType = parseHourType(order.getHourType());
|
||||
Map<Long, Integer> inventorySnapshot = new LinkedHashMap<>();
|
||||
@@ -4655,8 +4655,10 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
if (mealDate == null || hourType == null) {
|
||||
throw new I18nBizException(ErrorCodes.HARD_CANTEEN_SPU_BAD);
|
||||
}
|
||||
R<List<DailyMenuAppVO>> menuResult = remoteDailyMenuService.getCanteenList(
|
||||
mealDate.toString(), hourType, null, order.getMerchantId());
|
||||
// 库存恢复可能在自动退款定时任务等无用户凭证的上下文执行,必须走 @NoToken 内部菜谱接口;
|
||||
// getCanteenList 是 AD 终端登录态接口,任务线程调用会被 424 拒绝。
|
||||
R<List<DailyMenuAppVO>> menuResult = remoteDailyMenuService.listForOrderValidate(
|
||||
mealDate.toString(), hourType, order.getMerchantId());
|
||||
if (menuResult == null || !menuResult.isOk() || menuResult.getData() == null) {
|
||||
throw new I18nBizException(menuResult == null ? ErrorCodes.HARD_CANTEEN_STOCK_NO_RESP
|
||||
: ErrorCodes.HARD_CANTEEN_STOCK_QUERY_FAIL);
|
||||
@@ -4670,13 +4672,11 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
if (row == null) {
|
||||
throw new I18nBizException(ErrorCodes.HARD_CANTEEN_SPU_BAD);
|
||||
}
|
||||
int offlineRemain = row.getRemainOfflineStock() != null ? row.getRemainOfflineStock()
|
||||
: (row.getStock() != null ? row.getStock()
|
||||
: (row.getOfflineStock() != null ? row.getOfflineStock() : 0));
|
||||
if (entry.getValue() < 0 && offlineRemain < Math.abs(entry.getValue())) {
|
||||
int channelRemain = onlineChannel ? dailyOnlineRemain(row) : dailyOfflineRemain(row);
|
||||
if (entry.getValue() < 0 && channelRemain < Math.abs(entry.getValue())) {
|
||||
throw new I18nBizException(ErrorCodes.HARD_PRODUCT_STOCK_LOW, row.getName());
|
||||
}
|
||||
inventorySnapshot.put(entry.getKey(), offlineRemain);
|
||||
inventorySnapshot.put(entry.getKey(), channelRemain);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -4718,10 +4718,11 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
? "HARD_DAILY_MENU_ORDER:" + order.getId()
|
||||
: "HARD_DAILY_MENU_AFTER_SALE:" + afterSaleId;
|
||||
if (dailyMenu) {
|
||||
registerCanteenDailyOfflineInventoryRollback(stockChanges, inventorySnapshot, resolvedInventoryBizId,
|
||||
order.getMerchantId(), mealDate, hourType);
|
||||
applyCanteenDailyOfflineInventoryChange(stockChanges, inventorySnapshot, resolvedInventoryBizId, "APPLY",
|
||||
order.getMerchantId(), mealDate, hourType);
|
||||
String stockChannel = onlineChannel ? "ONLINE" : "OFFLINE";
|
||||
registerCanteenDailyChannelInventoryRollback(stockChannel, stockChanges, inventorySnapshot,
|
||||
resolvedInventoryBizId, order.getMerchantId(), mealDate, hourType);
|
||||
applyCanteenDailyChannelInventoryChange(stockChannel, stockChanges, inventorySnapshot,
|
||||
resolvedInventoryBizId, "APPLY", order.getMerchantId(), mealDate, hourType);
|
||||
}
|
||||
else {
|
||||
registerCanteenSpuInventoryRollback(stockChanges, inventorySnapshot, resolvedInventoryBizId);
|
||||
@@ -4799,14 +4800,14 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
}
|
||||
}
|
||||
|
||||
/** 安卓机/线下每日菜谱:仍调原 updateSkuStockIdempotent,补 OFFLINE 通道参数。 */
|
||||
private void applyCanteenDailyOfflineInventoryChange(Map<Long, Integer> stockChanges,
|
||||
/** 每日菜谱分池幂等变更:ONLINE 改写线上池销量,OFFLINE 改写线下池销量,两池互不影响。 */
|
||||
private void applyCanteenDailyChannelInventoryChange(String stockChannel, Map<Long, Integer> stockChanges,
|
||||
Map<Long, Integer> inventorySnapshot, String inventoryBizId, String operation, Long merchantId,
|
||||
LocalDate mealDate, Integer hourType) {
|
||||
CanteenSpuUpdateStockReqDTO dto = new CanteenSpuUpdateStockReqDTO();
|
||||
dto.setBizId(inventoryBizId);
|
||||
dto.setOperation(operation);
|
||||
dto.setStockChannel("OFFLINE");
|
||||
dto.setStockChannel(stockChannel);
|
||||
dto.setMerchantId(merchantId);
|
||||
dto.setItems(stockChanges.entrySet().stream().map(entry -> {
|
||||
CanteenSpuUpdateStockReqDTO.Item item = new CanteenSpuUpdateStockReqDTO.Item();
|
||||
@@ -4829,7 +4830,7 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
}
|
||||
}
|
||||
|
||||
private void registerCanteenDailyOfflineInventoryRollback(Map<Long, Integer> stockChanges,
|
||||
private void registerCanteenDailyChannelInventoryRollback(String stockChannel, Map<Long, Integer> stockChanges,
|
||||
Map<Long, Integer> inventorySnapshot, String inventoryBizId, Long merchantId, LocalDate mealDate,
|
||||
Integer hourType) {
|
||||
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
@@ -4844,16 +4845,30 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
|
||||
return;
|
||||
}
|
||||
try {
|
||||
applyCanteenDailyOfflineInventoryChange(rollbackChanges, rollbackSnapshot, inventoryBizId,
|
||||
applyCanteenDailyChannelInventoryChange(stockChannel, rollbackChanges, rollbackSnapshot, inventoryBizId,
|
||||
"ROLLBACK", merchantId, mealDate, hourType);
|
||||
}
|
||||
catch (Exception compensationException) {
|
||||
log.error("|{}|每日菜谱线下库存回滚失败,inventoryBizId:|{}|", TAG, inventoryBizId, compensationException);
|
||||
log.error("|{}|每日菜谱{}库存回滚失败,inventoryBizId:|{}|", TAG, stockChannel, inventoryBizId, compensationException);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 线上剩余:优先 remainOnlineStock;兼容旧字段 stock/onlineStock(历史把剩余写在配额字段)。 */
|
||||
private static int dailyOnlineRemain(DailyMenuAppVO row) {
|
||||
return row.getRemainOnlineStock() != null ? row.getRemainOnlineStock()
|
||||
: (row.getStock() != null ? row.getStock()
|
||||
: (row.getOnlineStock() == null ? 0 : row.getOnlineStock()));
|
||||
}
|
||||
|
||||
/** 线下剩余:优先 remainOfflineStock;兼容旧字段 stock/offlineStock。 */
|
||||
private static int dailyOfflineRemain(DailyMenuAppVO row) {
|
||||
return row.getRemainOfflineStock() != null ? row.getRemainOfflineStock()
|
||||
: (row.getStock() != null ? row.getStock()
|
||||
: (row.getOfflineStock() != null ? row.getOfflineStock() : 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void fastenerInventory(HardOrderEntity order, YesNoEnum isReduce) {
|
||||
|
||||
+141
-6
@@ -1,9 +1,12 @@
|
||||
package com.bsd.pjcloud.hard.service.impl.order;
|
||||
|
||||
import com.bsd.pjcloud.canteen.api.feign.RemoteCanteenSpuService;
|
||||
import com.bsd.pjcloud.canteen.api.feign.RemoteDailyMenuService;
|
||||
import com.bsd.pjcloud.canteen.dto.CanteenSpuUpdateStockReqDTO;
|
||||
import com.bsd.pjcloud.canteen.entity.CanteenSpuEntity;
|
||||
import com.bsd.pjcloud.canteen.vo.app.DailyMenuAppVO;
|
||||
import com.bsd.pjcloud.common.core.constant.enums.YesNoEnum;
|
||||
import com.bsd.pjcloud.common.core.exception.ErrorCodes;
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.hard.api.constant.HardProductSourceEnum;
|
||||
import com.bsd.pjcloud.hard.api.constant.HardTypeEnum;
|
||||
@@ -19,6 +22,7 @@ import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
@@ -32,7 +36,11 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* 机器收银食堂订单库存分流测试,确保 BELOW 订单使用 {@code canteen_spu} 原子库存接口。
|
||||
* 食堂订单库存分流测试:机器收银 BELOW 订单使用 {@code canteen_spu} 原子库存接口,
|
||||
* 每日菜谱订单按订单类型路由分池(RESERVE 走 ONLINE,其他类型走 OFFLINE)。
|
||||
* <p>
|
||||
* 直接调用五参 {@code fastenerInventory} 重载:四参重载经 getSelf() 需要 Spring 上下文,
|
||||
* 纯单测环境无法提供;异常文案由全局 i18n 处理器解析,此处只断言错误码。
|
||||
*/
|
||||
class HardOrderServiceImplInventoryTest {
|
||||
|
||||
@@ -42,6 +50,9 @@ class HardOrderServiceImplInventoryTest {
|
||||
/** 食堂基础菜品远程服务,负责查询权威库存并执行幂等扣减。 */
|
||||
private final RemoteCanteenSpuService remoteCanteenSpuService = mock(RemoteCanteenSpuService.class);
|
||||
|
||||
/** 每日菜谱远程服务,负责按日期餐段查询分池剩余。 */
|
||||
private final RemoteDailyMenuService remoteDailyMenuService = mock(RemoteDailyMenuService.class);
|
||||
|
||||
/** 被测订单服务,仅执行真实库存方法,其他依赖不参与本组测试。 */
|
||||
private final HardOrderServiceImpl service = mock(HardOrderServiceImpl.class, CALLS_REAL_METHODS);
|
||||
|
||||
@@ -52,6 +63,7 @@ class HardOrderServiceImplInventoryTest {
|
||||
void setUp() {
|
||||
ReflectionTestUtils.setField(service, "hardCarService", hardCarService);
|
||||
ReflectionTestUtils.setField(service, "remoteCanteenSpuService", remoteCanteenSpuService);
|
||||
ReflectionTestUtils.setField(service, "remoteDailyMenuService", remoteDailyMenuService);
|
||||
TransactionSynchronizationManager.initSynchronization();
|
||||
}
|
||||
|
||||
@@ -75,9 +87,9 @@ class HardOrderServiceImplInventoryTest {
|
||||
when(remoteCanteenSpuService.getInnerSpuListByIds(List.of(11L))).thenReturn(R.ok(List.of(product)));
|
||||
|
||||
RuntimeException exception = assertThrows(RuntimeException.class,
|
||||
() -> service.fastenerInventory(cashierOrder(), null, YesNoEnum.YES, null));
|
||||
() -> service.fastenerInventory(cashierOrder(), null, YesNoEnum.YES, null, null));
|
||||
|
||||
assertEquals("测试菜品库存不足", exception.getMessage());
|
||||
assertEquals(ErrorCodes.HARD_PRODUCT_STOCK_LOW, exception.getMessage());
|
||||
verify(remoteCanteenSpuService, never()).updateSkuStockIdempotent(any());
|
||||
}
|
||||
|
||||
@@ -91,7 +103,7 @@ class HardOrderServiceImplInventoryTest {
|
||||
when(remoteCanteenSpuService.getInnerSpuListByIds(List.of(11L))).thenReturn(R.ok(List.of(product)));
|
||||
when(remoteCanteenSpuService.updateSkuStockIdempotent(any())).thenReturn(R.ok());
|
||||
|
||||
assertDoesNotThrow(() -> service.fastenerInventory(cashierOrder(), null, YesNoEnum.YES, null));
|
||||
assertDoesNotThrow(() -> service.fastenerInventory(cashierOrder(), null, YesNoEnum.YES, null, null));
|
||||
|
||||
ArgumentCaptor<CanteenSpuUpdateStockReqDTO> captor = ArgumentCaptor.forClass(CanteenSpuUpdateStockReqDTO.class);
|
||||
verify(remoteCanteenSpuService).updateSkuStockIdempotent(captor.capture());
|
||||
@@ -129,7 +141,7 @@ class HardOrderServiceImplInventoryTest {
|
||||
RuntimeException exception = assertThrows(RuntimeException.class, () -> ReflectionTestUtils.invokeMethod(
|
||||
service, "validateCanteenSpuOrderInventory", cashierOrder()));
|
||||
|
||||
assertEquals("测试菜品库存不足", exception.getMessage());
|
||||
assertEquals(ErrorCodes.HARD_PRODUCT_STOCK_LOW, exception.getMessage());
|
||||
verify(remoteCanteenSpuService, never()).updateSkuStockIdempotent(any());
|
||||
}
|
||||
|
||||
@@ -143,7 +155,7 @@ class HardOrderServiceImplInventoryTest {
|
||||
when(remoteCanteenSpuService.getInnerSpuListByIds(List.of(11L))).thenReturn(R.ok(List.of(product)));
|
||||
when(remoteCanteenSpuService.updateSkuStockIdempotent(any())).thenReturn(R.ok());
|
||||
|
||||
assertDoesNotThrow(() -> service.fastenerInventory(cashierOrder(), null, YesNoEnum.NO, 500L));
|
||||
assertDoesNotThrow(() -> service.fastenerInventory(cashierOrder(), null, YesNoEnum.NO, 500L, null));
|
||||
|
||||
ArgumentCaptor<CanteenSpuUpdateStockReqDTO> captor = ArgumentCaptor.forClass(CanteenSpuUpdateStockReqDTO.class);
|
||||
verify(remoteCanteenSpuService).updateSkuStockIdempotent(captor.capture());
|
||||
@@ -174,6 +186,74 @@ class HardOrderServiceImplInventoryTest {
|
||||
assertEquals(-2, request.getItems().get(0).getIncrCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证线上预定每日菜谱订单退款恢复 ONLINE 池:下单扣的是 online_sold,退款必须还同一池,
|
||||
* 快照取线上剩余并携带日期餐段。
|
||||
*/
|
||||
@Test
|
||||
void reserveRefundRestoresDailyMenuOnlineStock() {
|
||||
prepareDailyMenuCart(2);
|
||||
when(remoteDailyMenuService.listForOrderValidate(any(), any(), any()))
|
||||
.thenReturn(R.ok(List.of(dailyMenuRow(8, 4))));
|
||||
when(remoteCanteenSpuService.updateSkuStockIdempotent(any())).thenReturn(R.ok());
|
||||
|
||||
assertDoesNotThrow(() -> service.fastenerInventory(reserveDailyMenuOrder(), null, YesNoEnum.NO, 500L, null));
|
||||
|
||||
ArgumentCaptor<CanteenSpuUpdateStockReqDTO> captor = ArgumentCaptor.forClass(CanteenSpuUpdateStockReqDTO.class);
|
||||
verify(remoteCanteenSpuService).updateSkuStockIdempotent(captor.capture());
|
||||
CanteenSpuUpdateStockReqDTO request = captor.getValue();
|
||||
assertEquals("APPLY", request.getOperation());
|
||||
assertEquals("ONLINE", request.getStockChannel());
|
||||
assertEquals("HARD_DAILY_MENU_AFTER_SALE:500", request.getBizId());
|
||||
assertEquals(300L, request.getMerchantId());
|
||||
CanteenSpuUpdateStockReqDTO.Item item = request.getItems().get(0);
|
||||
assertEquals(11L, item.getId());
|
||||
assertEquals(2, item.getIncrCount());
|
||||
assertEquals(8, item.getBeforeStock());
|
||||
assertEquals(LocalDate.of(2026, 9, 13), item.getMealDate());
|
||||
assertEquals(2, item.getHourType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证机器收银等其他类型的每日菜谱订单退款仍恢复 OFFLINE 池,快照取线下剩余。
|
||||
*/
|
||||
@Test
|
||||
void cashierDailyMenuRefundRestoresOfflineStock() {
|
||||
prepareDailyMenuCart(2);
|
||||
when(remoteDailyMenuService.listForOrderValidate(any(), any(), any()))
|
||||
.thenReturn(R.ok(List.of(dailyMenuRow(8, 4))));
|
||||
when(remoteCanteenSpuService.updateSkuStockIdempotent(any())).thenReturn(R.ok());
|
||||
|
||||
HardOrderEntity order = reserveDailyMenuOrder();
|
||||
order.setOrderType(HardOrderTypeEnum.BELOW.getType());
|
||||
assertDoesNotThrow(() -> service.fastenerInventory(order, null, YesNoEnum.NO, 500L, null));
|
||||
|
||||
ArgumentCaptor<CanteenSpuUpdateStockReqDTO> captor = ArgumentCaptor.forClass(CanteenSpuUpdateStockReqDTO.class);
|
||||
verify(remoteCanteenSpuService).updateSkuStockIdempotent(captor.capture());
|
||||
CanteenSpuUpdateStockReqDTO request = captor.getValue();
|
||||
assertEquals("OFFLINE", request.getStockChannel());
|
||||
assertEquals("HARD_DAILY_MENU_AFTER_SALE:500", request.getBizId());
|
||||
assertEquals(2, request.getItems().get(0).getIncrCount());
|
||||
assertEquals(4, request.getItems().get(0).getBeforeStock());
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证线上预定重审重扣按线上剩余校验:线上不足即拒绝,即使线下剩余充足。
|
||||
*/
|
||||
@Test
|
||||
void reserveReoccupyRejectsInsufficientOnlineStock() {
|
||||
prepareDailyMenuCart(2);
|
||||
when(remoteDailyMenuService.listForOrderValidate(any(), any(), any()))
|
||||
.thenReturn(R.ok(List.of(dailyMenuRow(1, 4))));
|
||||
|
||||
RuntimeException exception = assertThrows(RuntimeException.class,
|
||||
() -> service.fastenerInventory(reserveDailyMenuOrder(), null, YesNoEnum.YES, 500L,
|
||||
"HARD_AFTER_SALE_REVIEW_REOCCUPY:500:1"));
|
||||
|
||||
assertEquals(ErrorCodes.HARD_PRODUCT_STOCK_LOW, exception.getMessage());
|
||||
verify(remoteCanteenSpuService, never()).updateSkuStockIdempotent(any());
|
||||
}
|
||||
|
||||
/**
|
||||
* 准备机器收银购物车来源和商品数量。
|
||||
*
|
||||
@@ -193,6 +273,44 @@ class HardOrderServiceImplInventoryTest {
|
||||
when(hardCarService.getByParentCarId(200L)).thenReturn(List.of(car));
|
||||
}
|
||||
|
||||
/**
|
||||
* 准备每日菜谱购物车来源(cartScene 与商品来源标记与服务端保存逻辑一致)和商品数量。
|
||||
*
|
||||
* @param productCount 本次购买份数
|
||||
*/
|
||||
private void prepareDailyMenuCart(int productCount) {
|
||||
HardParentCarEntity.TakeOutSetting setting = new HardParentCarEntity.TakeOutSetting();
|
||||
setting.setCartScene("DAILY_MENU");
|
||||
HardParentCarEntity parentCar = HardParentCarEntity.builder()
|
||||
.id(200L)
|
||||
.productSource(HardProductSourceEnum.CANTEEN_DAILY_MENU.getType())
|
||||
.takeOutSettingJson(setting)
|
||||
.build();
|
||||
HardCarEntity car = HardCarEntity.builder()
|
||||
.parentCarId(200L)
|
||||
.productId(11L)
|
||||
.productCount(productCount)
|
||||
.build();
|
||||
when(hardCarService.getParentCarById(200L)).thenReturn(parentCar);
|
||||
when(hardCarService.getByParentCarId(200L)).thenReturn(List.of(car));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造指定线上/线下剩余的每日菜谱行。
|
||||
*
|
||||
* @param onlineRemain 线上池剩余
|
||||
* @param offlineRemain 线下池剩余
|
||||
* @return 用于分池校验的菜谱行
|
||||
*/
|
||||
private DailyMenuAppVO dailyMenuRow(int onlineRemain, int offlineRemain) {
|
||||
DailyMenuAppVO row = new DailyMenuAppVO();
|
||||
row.setSpuId(11L);
|
||||
row.setName("测试菜品");
|
||||
row.setRemainOnlineStock(onlineRemain);
|
||||
row.setRemainOfflineStock(offlineRemain);
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造指定权威库存的当前商户基础菜品。
|
||||
*
|
||||
@@ -223,4 +341,21 @@ class HardOrderServiceImplInventoryTest {
|
||||
.merchantId(300L)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造线上预定提交的每日菜谱订单。
|
||||
*
|
||||
* @return 关联每日菜谱购物车、固定日期餐段和商户的 RESERVE 订单
|
||||
*/
|
||||
private HardOrderEntity reserveDailyMenuOrder() {
|
||||
return HardOrderEntity.builder()
|
||||
.id(100L)
|
||||
.sysType(HardTypeEnum.CANTEEN.getType())
|
||||
.orderType(HardOrderTypeEnum.RESERVE.getType())
|
||||
.parentCarId(200L)
|
||||
.merchantId(300L)
|
||||
.apptUseDate(LocalDate.of(2026, 9, 13))
|
||||
.hourType("2")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user