|
|
@@ -0,0 +1,458 @@
|
|
|
+package com.fs.hisStore.service.impl;
|
|
|
+
|
|
|
+import com.fs.hisStore.domain.FsStoreOrderScrm;
|
|
|
+import com.fs.hisStore.domain.FsStoreProductActivity;
|
|
|
+import com.fs.hisStore.domain.FsStoreProductGroupBuy;
|
|
|
+import com.fs.hisStore.domain.FsStoreProductGroupBuyItem;
|
|
|
+import com.fs.hisStore.domain.FsUserScrm;
|
|
|
+import com.fs.hisStore.mapper.FsStoreOrderScrmMapper;
|
|
|
+import com.fs.hisStore.mapper.FsStoreProductActivityMapper;
|
|
|
+import com.fs.hisStore.mapper.FsStoreProductGroupBuyItemMapper;
|
|
|
+import com.fs.hisStore.mapper.FsStoreProductGroupBuyMapper;
|
|
|
+import com.fs.hisStore.service.IFsStoreOrderScrmService;
|
|
|
+import com.fs.hisStore.service.IFsStoreProductGroupBuyService;
|
|
|
+import com.fs.hisStore.service.IFsUserScrmService;
|
|
|
+import com.fs.hisStore.vo.FsStoreGroupBuyListVO;
|
|
|
+import com.fs.hisStore.vo.FsStoreGroupBuyMemberVO;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.context.annotation.Lazy;
|
|
|
+import org.springframework.dao.DuplicateKeyException;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Propagation;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.transaction.support.TransactionSynchronization;
|
|
|
+import org.springframework.transaction.support.TransactionSynchronizationManager;
|
|
|
+
|
|
|
+import java.util.Date;
|
|
|
+import java.util.List;
|
|
|
+import java.util.UUID;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 限时团购业务Service实现
|
|
|
+ *
|
|
|
+ * @author fs
|
|
|
+ * @date 2026-04-29
|
|
|
+ */
|
|
|
+@Service
|
|
|
+public class FsStoreProductGroupBuyServiceImpl implements IFsStoreProductGroupBuyService {
|
|
|
+
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(FsStoreProductGroupBuyServiceImpl.class);
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private FsStoreProductGroupBuyMapper groupBuyMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private FsStoreProductGroupBuyItemMapper groupBuyItemMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private FsStoreProductActivityMapper activityMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private FsStoreOrderScrmMapper fsStoreOrderMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IFsUserScrmService userService;
|
|
|
+
|
|
|
+ /** 打破与 FsStoreOrderScrmServiceImpl 的循环依赖:对方注入本类,这里反向注入要 @Lazy */
|
|
|
+ @Lazy
|
|
|
+ @Autowired
|
|
|
+ private IFsStoreOrderScrmService fsStoreOrderService;
|
|
|
+
|
|
|
+ // ================================================================
|
|
|
+ // 1. 下单阶段:预占名额
|
|
|
+ // ================================================================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用独立事务(REQUIRES_NEW)。订单主事务即便后续因为其他原因回滚,
|
|
|
+ * 这里的名额占用也已提交;但若下单最终失败,调用方应该自行调 {@link #releaseGroupSlot}
|
|
|
+ * 把名额还回去,避免"占座不下单"把团卡死。
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
|
|
+ public Long reserveGroupSlot(Long activityId, Long userId) {
|
|
|
+ if (activityId == null || userId == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ FsStoreProductActivity activity = activityMapper.selectFsStoreProductActivityById(activityId);
|
|
|
+ if (activity == null || activity.getGroupNum() == null || activity.getGroupNum() <= 0) {
|
|
|
+ log.warn("[团购预占] 活动信息不全,activityId={}", activityId);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (activity.getEndTime() != null && activity.getEndTime().before(new Date())) {
|
|
|
+ log.warn("[团购预占] 活动已结束,activityId={}", activityId);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 先尝试加入已有未满团,循环重试应对并发抢
|
|
|
+ for (int i = 0; i < DEFAULT_RESERVE_RETRY; i++) {
|
|
|
+ FsStoreProductGroupBuy candidate = groupBuyMapper.selectJoinableGroup(activityId, userId);
|
|
|
+ if (candidate == null) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ int affected = groupBuyMapper.tryJoinGroup(candidate.getId());
|
|
|
+ if (affected == 1) {
|
|
|
+ return candidate.getId();
|
|
|
+ }
|
|
|
+ // 被别人抢走了,重新找
|
|
|
+ }
|
|
|
+
|
|
|
+ // 没有可加入的团 → 自己开一团
|
|
|
+ FsStoreProductGroupBuy created = createNewGroup(activity, userId);
|
|
|
+ return created.getId();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
|
|
+ public boolean releaseGroupSlot(Long groupBuyId) {
|
|
|
+ if (groupBuyId == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ int affected = groupBuyMapper.releaseJoin(groupBuyId);
|
|
|
+ if (affected == 0) {
|
|
|
+ log.info("[团购释放名额] 无需释放,groupId={}", groupBuyId);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 已付款订单退款路径:同时回滚 join_num 和 paid_num。
|
|
|
+ * 走独立事务,主退款事务即使后续出异常回滚,名额也已经扣进去(跟 releaseGroupSlot 设计一致)。
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
|
|
+ public boolean releasePaidGroupSlot(Long groupBuyId) {
|
|
|
+ if (groupBuyId == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ int affected = groupBuyMapper.releasePaidAndJoin(groupBuyId);
|
|
|
+ if (affected == 0) {
|
|
|
+ // 团已非进行中(成团或已失败),这时不动 paid_num 正是预期行为:
|
|
|
+ // - status=1 成团的订单退款不影响后续成团判定(团已终态)
|
|
|
+ // - status=2 失败团同理,后续孤儿退款走的就是这条路径
|
|
|
+ log.info("[团购退款释放名额] 团已终态无需回滚,groupId={}", groupBuyId);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ================================================================
|
|
|
+ // 2. 支付回调阶段:写详情 + 成团判断
|
|
|
+ // ================================================================
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void handleAfterPay(FsStoreOrderScrm order) {
|
|
|
+ if (order == null || order.getOrderType() == null || order.getOrderType() != 8) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 主路径:下单已预占到团,回调这里只需落详情 + 判定是否成团
|
|
|
+ if (order.getGroupBuyId() != null) {
|
|
|
+ finishPaidOrderInGroup(order);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 兜底:订单没有 group_buy_id(历史单或预占阶段异常),退回到"回调匹配团"
|
|
|
+ log.warn("[团购回调] 订单无 group_buy_id 走兜底匹配,orderId={}", order.getId());
|
|
|
+ fallbackMatchGroupOnPay(order);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 已预占好团的订单:写 item + 原子推进 paid_num,满员才真正成团
|
|
|
+ * <p>幂等保护:如果 insertItemSafely 返回 false(uk_group_user 已存在、回调重入),
|
|
|
+ * 说明本笔订单之前已被处理过一次,paid_num 已加入团,这里绝不能再 +1,不然会虚高。</p>
|
|
|
+ */
|
|
|
+ private void finishPaidOrderInGroup(FsStoreOrderScrm order) {
|
|
|
+ Long groupId = order.getGroupBuyId();
|
|
|
+ FsStoreProductGroupBuy group = groupBuyMapper.selectFsStoreProductGroupBuyById(groupId);
|
|
|
+ if (group == null) {
|
|
|
+ log.error("[团购回调] 团已不存在,orderId={}, groupId={}", order.getId(), groupId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 极端并发兜底:回调到达时团已被定时任务判失败(status=2)——不写 item、不推 ERP,
|
|
|
+ // 订单照常完成支付流程,留给定时任务的孤儿订单扫描兜底退款
|
|
|
+ if (Integer.valueOf(2).equals(group.getStatus())) {
|
|
|
+ log.warn("[团购回调] 订单回调到达时团已判失败,等定时任务兜底退款,orderId={}, groupId={}",
|
|
|
+ order.getId(), groupId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ FsStoreProductActivity activity = activityMapper.selectFsStoreProductActivityById(order.getAssociatedId());
|
|
|
+ boolean freshInsert = insertItemSafely(group, order, activity);
|
|
|
+ if (!freshInsert) {
|
|
|
+ // 回调重入(上游 payConfirm 锁理论上拦的住,这里是额外防御):之前已成功推过 paid_num,
|
|
|
+ // 直接返回不能再 +1,避免 paid_num 虚高误判成团
|
|
|
+ log.info("[团购回调] 重入,item 已存在且 paid_num 已加过,skip,orderId={}", order.getId());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 原子 CAS 推进 paid_num + 满员置 status=1,避免多线程同时付款时丢成团
|
|
|
+ int affected = groupBuyMapper.tryMarkPaidAndComplete(groupId);
|
|
|
+ if (affected == 0) {
|
|
|
+ // 走到这里说明团已经不是进行中(被定时任务或其他路径置成成功/失败)——再查一次
|
|
|
+ FsStoreProductGroupBuy latest = groupBuyMapper.selectFsStoreProductGroupBuyById(groupId);
|
|
|
+ if (latest != null && Integer.valueOf(2).equals(latest.getStatus())) {
|
|
|
+ log.warn("[团购回调] 团推进付款数失败,团已失败,等定时任务兜底,orderId={}", order.getId());
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 推进成功:重新查最新状态,若此单是满员的最后一笔,latest.status=1 才触发成团钩子
|
|
|
+ FsStoreProductGroupBuy latest = groupBuyMapper.selectFsStoreProductGroupBuyById(groupId);
|
|
|
+ if (latest != null && Integer.valueOf(1).equals(latest.getStatus())) {
|
|
|
+ onGroupComplete(latest);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 兜底分支:支付回调到了但订单没绑团,按旧逻辑动态匹配一次
|
|
|
+ */
|
|
|
+ private void fallbackMatchGroupOnPay(FsStoreOrderScrm order) {
|
|
|
+ if (order.getAssociatedId() == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ FsStoreProductActivity activity = activityMapper.selectFsStoreProductActivityById(order.getAssociatedId());
|
|
|
+ if (activity == null || activity.getGroupNum() == null || activity.getGroupNum() <= 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (activity.getEndTime() != null && activity.getEndTime().before(new Date())) {
|
|
|
+ log.warn("[团购回调-兜底] 活动已结束,等待兜底退款,orderId={}", order.getId());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int i = 0; i < DEFAULT_RESERVE_RETRY; i++) {
|
|
|
+ FsStoreProductGroupBuy target = groupBuyMapper.selectJoinableGroup(activity.getId(), order.getUserId());
|
|
|
+ if (target == null) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (groupBuyMapper.tryJoinGroup(target.getId()) == 1) {
|
|
|
+ attachOrderToGroup(order, target.getId());
|
|
|
+ boolean freshInsert = insertItemSafely(target, order, activity);
|
|
|
+ // 兼容路径也走原子推进 paid_num,满员才触发成团;若 item 已存在(重入)跳过 +1 避免虚高
|
|
|
+ if (freshInsert && groupBuyMapper.tryMarkPaidAndComplete(target.getId()) == 1) {
|
|
|
+ FsStoreProductGroupBuy after = groupBuyMapper.selectFsStoreProductGroupBuyById(target.getId());
|
|
|
+ if (after != null && Integer.valueOf(1).equals(after.getStatus())) {
|
|
|
+ onGroupComplete(after);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ FsStoreProductGroupBuy created = createNewGroup(activity, order.getUserId());
|
|
|
+ attachOrderToGroup(order, created.getId());
|
|
|
+ boolean freshInsert = insertItemSafely(created, order, activity);
|
|
|
+ // 新开团的第一笔付款也按正常路径推进;重入时 item 已存在不再 +1 paid_num
|
|
|
+ if (freshInsert && groupBuyMapper.tryMarkPaidAndComplete(created.getId()) == 1) {
|
|
|
+ FsStoreProductGroupBuy after = groupBuyMapper.selectFsStoreProductGroupBuyById(created.getId());
|
|
|
+ if (after != null && Integer.valueOf(1).equals(after.getStatus())) {
|
|
|
+ onGroupComplete(after);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ================================================================
|
|
|
+ // 3. 成团钩子
|
|
|
+ // ================================================================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 成团后:把团里所有待推 ERP 的订单统一推送出去
|
|
|
+ * <p><b>关键</b>:必须在当前事务 <b>提交之后</b> 再推 ERP。</p>
|
|
|
+ * <p>原因:{@code createOmsOrder} 自带 {@code @Transactional},ERP 外部接口超时/异常会把当前事务
|
|
|
+ * 标记为 rollback-only。即便我们 try/catch 吞掉了异常,事务提交时依然会抛
|
|
|
+ * {@code UnexpectedRollbackException},导致 {@code tryMarkPaidAndComplete} 的成团标记(status=1)
|
|
|
+ * 和 paid_num+1、item 插入被连锁回滚——用户钱收了、团却没成团,数据就错乱了。</p>
|
|
|
+ * <p>所以这里用 afterCommit 钩子延后推 ERP;单个订单推失败不影响其他订单;
|
|
|
+ * 漏推的由原有 {@code PushErp} 定时任务兜底,业务最终一致。</p>
|
|
|
+ */
|
|
|
+ private void onGroupComplete(FsStoreProductGroupBuy group) {
|
|
|
+ log.info("[团购成团] groupId={}, groupNo={}, joinNum={}, groupNum={}",
|
|
|
+ group.getId(), group.getGroupNo(), group.getJoinNum(), group.getGroupNum());
|
|
|
+ final Long groupId = group.getId();
|
|
|
+ if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
|
|
+ // 事务提交后再推 ERP,ERP 的任何异常都不会影响本事务落库的成团状态
|
|
|
+ TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
|
|
+ @Override
|
|
|
+ public void afterCommit() {
|
|
|
+ pushErpForGroupOrders(groupId);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ // 兜底:理论上 handleAfterPay 一定带事务,这里走到只可能是调用链被人改过,直接同步推
|
|
|
+ log.warn("[团购成团] 当前无事务同步上下文,降级同步推 ERP,groupId={}", groupId);
|
|
|
+ pushErpForGroupOrders(groupId);
|
|
|
+ }
|
|
|
+ // TODO: 后续接入发货 / 模板消息推送(同样建议放在 afterCommit 里)
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 实际执行团内订单 ERP 推送:单个失败不影响其他;漏推的由原有定时任务 PushErp 兜底。
|
|
|
+ */
|
|
|
+ private void pushErpForGroupOrders(Long groupId) {
|
|
|
+ try {
|
|
|
+ List<Long> orderIds = fsStoreOrderMapper.selectOrderIdsByGroupBuyId(groupId);
|
|
|
+ if (orderIds == null || orderIds.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ for (Long orderId : orderIds) {
|
|
|
+ try {
|
|
|
+ fsStoreOrderService.createOmsOrder(orderId);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[成团推ERP失败] orderId={}, groupId={}", orderId, groupId, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[成团钩子异常] groupId={}", groupId, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ================================================================
|
|
|
+ // 4. 工具方法
|
|
|
+ // ================================================================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 新建一个团,初始 join_num=1(已占位)、paid_num=0(还没付款)、status=0。
|
|
|
+ * 即使 groupNum=1 的极端配置也不在这里提前置成团,统一由支付回调 tryMarkPaidAndComplete 标成
|
|
|
+ */
|
|
|
+ private FsStoreProductGroupBuy createNewGroup(FsStoreProductActivity activity, Long userId) {
|
|
|
+ FsStoreProductGroupBuy g = new FsStoreProductGroupBuy();
|
|
|
+ g.setGroupNo(generateGroupNo());
|
|
|
+ g.setActivityId(activity.getId());
|
|
|
+ g.setProductId(activity.getProductId());
|
|
|
+ g.setGroupNum(activity.getGroupNum());
|
|
|
+ g.setJoinNum(1);
|
|
|
+ g.setPaidNum(0);
|
|
|
+ g.setStatus(0);
|
|
|
+ g.setStartTime(new Date());
|
|
|
+ g.setEndTime(activity.getEndTime());
|
|
|
+ g.setDelFlag(0);
|
|
|
+ g.setCreateBy(userId == null ? null : String.valueOf(userId));
|
|
|
+ g.setCreateTime(new Date());
|
|
|
+
|
|
|
+ groupBuyMapper.insertFsStoreProductGroupBuy(g);
|
|
|
+ return g;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 回写订单 group_buy_id(主路径下下单就写好了,这里只给兜底分支用)
|
|
|
+ */
|
|
|
+ private void attachOrderToGroup(FsStoreOrderScrm order, Long groupId) {
|
|
|
+ FsStoreOrderScrm up = new FsStoreOrderScrm();
|
|
|
+ up.setId(order.getId());
|
|
|
+ up.setGroupBuyId(groupId);
|
|
|
+ fsStoreOrderMapper.updateFsStoreOrder(up);
|
|
|
+ order.setGroupBuyId(groupId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 插入参与详情,依靠唯一键 uk_group_user 保证幂等
|
|
|
+ * @return true = 真新插入(第一次处理);false = 重入(本用户在本团的 item 已存在)
|
|
|
+ */
|
|
|
+ private boolean insertItemSafely(FsStoreProductGroupBuy group,
|
|
|
+ FsStoreOrderScrm order,
|
|
|
+ FsStoreProductActivity activity) {
|
|
|
+ FsStoreProductGroupBuyItem item = new FsStoreProductGroupBuyItem();
|
|
|
+ item.setGroupId(group.getId());
|
|
|
+ item.setProductId(group.getProductId());
|
|
|
+ if (activity != null) {
|
|
|
+ item.setSpecId(activity.getSpecId());
|
|
|
+ item.setActivityId(activity.getId());
|
|
|
+ }
|
|
|
+ item.setUserId(order.getUserId());
|
|
|
+
|
|
|
+ try {
|
|
|
+ FsUserScrm user = userService.selectFsUserById(order.getUserId());
|
|
|
+ if (user != null) {
|
|
|
+ item.setNickName(user.getNickName());
|
|
|
+ item.setAvatar(user.getAvatar());
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("[团购] 查询用户信息失败,跳过昵称头像,userId={}", order.getUserId(), e);
|
|
|
+ }
|
|
|
+
|
|
|
+ item.setPayStatus(1);
|
|
|
+ item.setPayTime(order.getPayTime() == null ? new Date() : order.getPayTime());
|
|
|
+ item.setJoinTime(new Date());
|
|
|
+ item.setDelFlag(0);
|
|
|
+ item.setCreateBy(order.getUserId() == null ? null : String.valueOf(order.getUserId()));
|
|
|
+ item.setCreateTime(new Date());
|
|
|
+ try {
|
|
|
+ groupBuyItemMapper.insertFsStoreProductGroupBuyItem(item);
|
|
|
+ return true;
|
|
|
+ } catch (DuplicateKeyException e) {
|
|
|
+ // 回调重入,item 已存在,视为已处理
|
|
|
+ log.info("[团购] item 已存在,跳过重复插入,groupId={}, userId={}", group.getId(), order.getUserId());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 团号:G + 13位时间戳 + 6位随机串,便于业务侧识别
|
|
|
+ */
|
|
|
+ private String generateGroupNo() {
|
|
|
+ String ts = String.valueOf(System.currentTimeMillis());
|
|
|
+ String suffix = UUID.randomUUID().toString().replace("-", "").substring(0, 6).toUpperCase();
|
|
|
+ return "G" + ts + suffix;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 后台查询:列表 / 详情 ====================
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<FsStoreGroupBuyListVO> selectGroupBuyListForAdmin(String groupNo,
|
|
|
+ Long productId,
|
|
|
+ String productName,
|
|
|
+ Integer status,
|
|
|
+ String beginTime,
|
|
|
+ String endTime) {
|
|
|
+ List<FsStoreGroupBuyListVO> list = groupBuyMapper.selectGroupBuyListForAdmin(
|
|
|
+ null, groupNo, productId, productName, status, beginTime, endTime);
|
|
|
+ fillStatusText(list);
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<FsStoreGroupBuyListVO> selectGroupBuyListByProduct(Long productId) {
|
|
|
+ if (productId == null) {
|
|
|
+ return java.util.Collections.emptyList();
|
|
|
+ }
|
|
|
+ List<FsStoreGroupBuyListVO> list = groupBuyMapper.selectGroupBuyListForAdmin(
|
|
|
+ null, null, productId, null, null, null, null);
|
|
|
+ fillStatusText(list);
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public FsStoreGroupBuyListVO selectGroupBuyDetailForAdmin(Long id) {
|
|
|
+ if (id == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ List<FsStoreGroupBuyListVO> list = groupBuyMapper.selectGroupBuyListForAdmin(
|
|
|
+ id, null, null, null, null, null, null);
|
|
|
+ if (list == null || list.isEmpty()) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ FsStoreGroupBuyListVO vo = list.get(0);
|
|
|
+ fillStatusText(java.util.Collections.singletonList(vo));
|
|
|
+ // 拉团员,包含各自的订单状态
|
|
|
+ List<FsStoreGroupBuyMemberVO> members = groupBuyMapper.selectGroupBuyMembers(id);
|
|
|
+ vo.setMembers(members != null ? members : java.util.Collections.emptyList());
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 给列表/详情填上状态文本,前端少写 if-else */
|
|
|
+ private void fillStatusText(List<FsStoreGroupBuyListVO> list) {
|
|
|
+ if (list == null) return;
|
|
|
+ for (FsStoreGroupBuyListVO vo : list) {
|
|
|
+ if (vo.getStatus() == null) {
|
|
|
+ vo.setStatusText("");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ switch (vo.getStatus()) {
|
|
|
+ case 0: vo.setStatusText("进行中"); break;
|
|
|
+ case 1: vo.setStatusText("拼团成功"); break;
|
|
|
+ case 2: vo.setStatusText("拼团失败"); break;
|
|
|
+ default: vo.setStatusText("未知");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|