feat:消息推送查询

fix:消费机库存显示更改
This commit is contained in:
dzc committed 2026-10-09 13:47:26 +08:00
1 parent bf5db4bbb9
commit 96abe868ef
8 files changed
+136 -14

No files matched your search

@@ -93,7 +93,7 @@ public class DailyMenuAppVO {
/**
* 当前可售库存。
* <p>App/移动端:移动端可预订剩余;AD:总库存 − 移动端已预订。字段名保持 stock 兼容旧客户端。
* <p>App/移动端:移动端可预订剩余;AD:剩余总库存(总库存 − 线上已订 − 线下已售)。字段名保持 stock 兼容旧客户端。
*/
private Integer stock;
@@ -112,7 +112,7 @@ public class DailyMenuAppVO {
/** 移动端可预订剩余 */
private Integer remainOnlineStock;
/** App:剩余总库存;AD:与 stock 同为 总库存−移动端已预订 */
/** 剩余总库存(AD 与 stock 同值;管理端 remainStock 同口径) */
private Integer remainOfflineStock;
/**
@@ -385,7 +385,7 @@ public class CanteenDailyMenuServiceImpl extends ServiceImpl<CanteenDailyMenuMap
public List<DailyMenuAppVO> getCanteenMenuListScope(String day, Integer hourType, Long categoryId, Long merchantId) {
List<DailyMenuAppVO> list = baseMapper.getCanteenMenuList(day, hourType, categoryId, merchantId, DataScope.of());
fillDailyMenuAppCombo(list);
// 安卓消费机:stock = 当日线下剩余
// 安卓消费机:stock = 剩余总库存(与管理端 remainStock、线下扣减边界同口径)
LocalDate mealDate = null;
try {
mealDate = day == null ? null : LocalDate.parse(day);
@@ -30,7 +30,7 @@ import java.util.stream.Collectors;
* <pre>
* 剩余总库存 = max(0, 总库存 − 移动端已预订 − 消费机已销售)
* 移动端可预订剩余 = min(移动端上限 − 已预订, 剩余总库存)
* AD stock = max(0, 总库存 − 移动端已预订)
* AD stock = 剩余总库存(与管理端 remainStock、线下扣减边界同口径)
* </pre>
* PC 管理端只展示总库存、移动端上限、移动端已订、消费机已售、剩余总库存。
* 扣减仍受「剩余总库存」边界约束,防止超卖。
@@ -95,7 +95,8 @@ public class CanteenDailyMenuStockSupport {
}
/**
* AD 返回的 stock:总库存 − 移动端已预订(不含消费机已售;已售由 offline_sold 单独体现)。
* 总库存 − 移动端已预订(不含消费机已售)。
* 仅作库存扣减流程的线下游标基线;AD 列表展示已改用 {@link #sharedRemain}。
*/
public static int adStockOf(Integer totalStock, Integer onlineSold) {
return Math.max(0, totalStockOf(totalStock) - soldOrDefault(onlineSold));
@@ -126,7 +127,7 @@ public class CanteenDailyMenuStockSupport {
applyAppComboLinkedRemain(rows, true);
}
/** 安卓消费机:stock = 总库存 − 移动端已预订 */
/** 安卓消费机:stock = 剩余总库存(管理端 remainStock 同口径) */
public void fillAdOfflineRemain(List<DailyMenuAppVO> rows, Long merchantId, LocalDate mealDate) {
if (CollUtil.isEmpty(rows)) {
return;
@@ -206,15 +207,14 @@ public class CanteenDailyMenuStockSupport {
int offlineSold = soldOrDefault(row.getOfflineSold());
int shared = sharedRemain(total, onlineSold, offlineSold);
int onlineRemain = onlineRemain(total, mobile, onlineSold, offlineSold);
int adStock = adStockOf(total, onlineSold);
row.setOnlineStock(mobile);
row.setOfflineStock(total);
row.setOnlineSold(onlineSold);
row.setOfflineSold(offlineSold);
row.setRemainOnlineStock(onlineRemain);
// AD:remainOfflineStock / stock = 总库存 − 线上已订;App 侧 remainOffline 仍给剩余总库存供参考
row.setRemainOfflineStock(stockAsOnline ? shared : adStock);
row.setStock(stockAsOnline ? onlineRemain : adStock);
// remainOfflineStock = 剩余总库存;stock:App=移动端可订剩余,AD=剩余总库存(与线下扣减边界一致)
row.setRemainOfflineStock(shared);
row.setStock(stockAsOnline ? onlineRemain : shared);
}
/**
@@ -0,0 +1,25 @@
package com.bsd.pjcloud.thirdparty.dto.appmessage;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* PC重新推送失败终态投递记录的请求。
*/
@Data
public class AppPushDeliveryRetryDto implements Serializable {
/** 当前操作的商户ID,用于复核管理员数据范围。 */
@NotNull(message = "商户ID不能为空")
private Long mchId;
/** 待重新推送的投递记录ID列表,仅失败终态记录会被重置。 */
@NotEmpty(message = "请选择要重新推送的投递记录")
@Size(max = 100, message = "单次最多重新推送100条投递记录")
private List<Long> deliveryIds;
}
@@ -23,6 +23,9 @@ public class AppPushDeliveryVo implements Serializable {
/** 目标用户ID。 */
private Long recipientUserId;
/** 接收用户姓名,远程用户服务不可用或用户已删除时为空。 */
private String recipientName;
/** 投递状态。 */
private String status;
@@ -6,6 +6,7 @@ import com.bsd.pjcloud.common.core.constant.enums.MerchantTypeEnum;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.log.annotation.SysLog;
import com.bsd.pjcloud.common.security.util.SecurityUtils;
import com.bsd.pjcloud.thirdparty.dto.appmessage.AppPushDeliveryRetryDto;
import com.bsd.pjcloud.thirdparty.dto.appmessage.AppPushPolicySaveDto;
import com.bsd.pjcloud.thirdparty.dto.appmessage.AppPushTestDto;
import com.bsd.pjcloud.thirdparty.service.appmessage.ThirdAppMessageSettingService;
@@ -121,6 +122,7 @@ public class ThirdAppMessagePcController {
* @param mchId 商户ID
* @param status 可选投递状态
* @param eventCode 可选事件编码
* @param recipientUserId 可选接收用户ID
* @return 商户投递记录分页
*/
@Operation(summary = "分页查询商户业务Push投递记录")
@@ -128,9 +130,10 @@ public class ThirdAppMessagePcController {
@GetMapping("/deliveries")
public R<Page<AppPushDeliveryVo>> pageDeliveries(@ParameterObject Page<?> page, @RequestParam Long mchId,
@RequestParam(required = false) String status,
@RequestParam(required = false) String eventCode) {
@RequestParam(required = false) String eventCode,
@RequestParam(required = false) Long recipientUserId) {
requireMerchantAccess(mchId);
return R.ok(deliveryService.page(page, mchId, status, eventCode));
return R.ok(deliveryService.page(page, mchId, status, eventCode, recipientUserId));
}
/**
@@ -148,6 +151,21 @@ public class ThirdAppMessagePcController {
return R.ok(deliveryService.listAttempts(deliveryId, mchId));
}
/**
* 把失败终态的投递记录重置回待投递,由投递扫描任务在数秒内重新推送。
*
* @param dto 商户与投递记录ID集合
* @return 实际重置的记录数量
*/
@Operation(summary = "重新推送App投递记录")
@SysLog("重新推送App投递记录")
@PreAuthorize("@pms.hasPermission('wallet_mchManage_setting')")
@PostMapping("/deliveries/retry")
public R<Integer> retryDeliveries(@Valid @RequestBody AppPushDeliveryRetryDto dto) {
requireMerchantAccess(dto.getMchId());
return R.ok(deliveryService.retryDeliveries(dto.getMchId(), dto.getDeliveryIds()));
}
/**
* 查询当前部署Push供应商只读状态。
*
@@ -35,9 +35,10 @@ public interface ThirdPushDeliveryService {
* @param mchId 商户ID
* @param status 可选投递状态
* @param eventCode 可选事件编码
* @param recipientUserId 可选接收用户ID
* @return 商户投递记录分页
*/
Page<AppPushDeliveryVo> page(Page<?> page, Long mchId, String status, String eventCode);
Page<AppPushDeliveryVo> page(Page<?> page, Long mchId, String status, String eventCode, Long recipientUserId);
/**
* 查询指定商户投递任务的供应商调用详情。
@@ -48,6 +49,15 @@ public interface ThirdPushDeliveryService {
*/
List<AppPushDeliveryAttemptVo> listAttempts(Long deliveryId, Long mchId);
/**
* 把失败终态的投递记录重置回待投递,由既有扫描任务在数秒内重新推送。
*
* @param mchId 商户ID
* @param deliveryIds 待重新推送的投递记录ID
* @return 实际重置的记录数量,非失败终态或非本商户记录不计入
*/
int retryDeliveries(Long mchId, List<Long> deliveryIds);
/**
* 返回供应商只读健康状态,不包含部署密钥。
*
@@ -1,9 +1,14 @@
package com.bsd.pjcloud.thirdparty.service.impl.appmessage;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.admin.api.entity.SysUser;
import com.bsd.pjcloud.admin.api.feign.RemoteUserService;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.data.tenant.TenantBroker;
import com.bsd.pjcloud.thirdparty.constant.appmessage.AppMessageConstants;
import com.bsd.pjcloud.thirdparty.constant.push.ThirdPushBizTypeEnum;
@@ -83,6 +88,9 @@ public class ThirdPushDeliveryServiceImpl implements ThirdPushDeliveryService {
/** 事务模板,保证任务领取、供应商尝试留痕与最终投递状态在同一数据库事务中提交。 */
private final TransactionTemplate transactionTemplate;
/** UPMS远程用户服务,用于PC列表补齐接收用户姓名。 */
private final RemoteUserService remoteUserService;
/**
* 创建一条唯一Push决策;没有有效设备时把原本待投递任务记录为跳过。
*
@@ -157,23 +165,56 @@ public class ThirdPushDeliveryServiceImpl implements ThirdPushDeliveryService {
* @param mchId 商户ID
* @param status 可选投递状态
* @param eventCode 可选事件编码
* @param recipientUserId 可选接收用户ID
* @return PC投递记录分页
*/
@Override
public Page<AppPushDeliveryVo> page(Page<?> page, Long mchId, String status, String eventCode) {
public Page<AppPushDeliveryVo> page(Page<?> page, Long mchId, String status, String eventCode, Long recipientUserId) {
Page<ThirdPushDeliveryEntity> entityPage = deliveryMapper.selectPage(
new Page<>(page.getCurrent(), page.getSize()),
Wrappers.<ThirdPushDeliveryEntity>lambdaQuery()
.eq(ThirdPushDeliveryEntity::getSourceMchId, mchId)
.eq(StrUtil.isNotBlank(status), ThirdPushDeliveryEntity::getStatus, status)
.eq(StrUtil.isNotBlank(eventCode), ThirdPushDeliveryEntity::getEventCode, eventCode)
.eq(recipientUserId != null, ThirdPushDeliveryEntity::getRecipientUserId, recipientUserId)
.orderByDesc(ThirdPushDeliveryEntity::getCreateTime)
.orderByDesc(ThirdPushDeliveryEntity::getId));
Page<AppPushDeliveryVo> result = new Page<>(entityPage.getCurrent(), entityPage.getSize(), entityPage.getTotal());
result.setRecords(entityPage.getRecords().stream().map(this::toDeliveryVo).toList());
fillRecipientNames(result.getRecords());
return result;
}
/**
* 批量补齐投递记录接收用户姓名,远程服务异常或用户已删除不阻断诊断列表。
*
* @param records 当前页PC投递记录
*/
private void fillRecipientNames(List<AppPushDeliveryVo> records) {
List<Long> userIdList = records.stream().map(AppPushDeliveryVo::getRecipientUserId)
.filter(ObjectUtil::isNotEmpty).distinct().toList();
if (ObjectUtil.isEmpty(userIdList)) {
return;
}
try {
R<List<SysUser>> result = remoteUserService.getInnerUserById(userIdList);
List<SysUser> userList = result == null || !result.isOk() ? null : result.getData();
if (ObjectUtil.isEmpty(userList)) {
return;
}
Map<Long, SysUser> userMap = IterUtil.toMap(userList, SysUser::getUserId);
for (AppPushDeliveryVo record : records) {
SysUser targetUser = userMap.get(record.getRecipientUserId());
if (targetUser == null) {
continue;
}
record.setRecipientName(targetUser.getName());
}
} catch (Exception exception) {
log.warn("投递记录列表补充接收用户姓名失败,errorType:{}", exception.getClass().getSimpleName());
}
}
/**
* 查询指定商户投递任务的供应商调用详情,先校验任务确实属于该商户。
*
@@ -196,6 +237,31 @@ public class ThirdPushDeliveryServiceImpl implements ThirdPushDeliveryService {
.stream().map(this::toAttemptVo).toList();
}
/**
* 把失败终态的投递记录条件重置回待投递,交由既有扫描任务重新领取处理。
*
* @param mchId 商户ID
* @param deliveryIds 待重新推送的投递记录ID
* @return 实际重置的记录数量
*/
@Override
public int retryDeliveries(Long mchId, List<Long> deliveryIds) {
if (ObjectUtil.isEmpty(deliveryIds)) {
return 0;
}
return deliveryMapper.update(null, Wrappers.<ThirdPushDeliveryEntity>lambdaUpdate()
.set(ThirdPushDeliveryEntity::getStatus, AppMessageConstants.DELIVERY_PENDING)
.set(ThirdPushDeliveryEntity::getAttemptCount, 0)
.set(ThirdPushDeliveryEntity::getNextRetryTime, null)
.set(ThirdPushDeliveryEntity::getFinalProviderCode, null)
.set(ThirdPushDeliveryEntity::getLastError, null)
.set(ThirdPushDeliveryEntity::getSuccessTime, null)
.set(ThirdPushDeliveryEntity::getUpdateTime, LocalDateTime.now())
.in(ThirdPushDeliveryEntity::getId, deliveryIds)
.eq(ThirdPushDeliveryEntity::getSourceMchId, mchId)
.eq(ThirdPushDeliveryEntity::getStatus, AppMessageConstants.DELIVERY_FAILED));
}
/**
* 返回全部供应商只读状态。
*