|
@@ -0,0 +1,436 @@
|
|
|
|
|
+package com.etotem.cfc.service;
|
|
|
|
|
+
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
|
|
+import com.etotem.cfc.entity.Coupon;
|
|
|
|
|
+import com.etotem.cfc.entity.CouponGiftTransfer;
|
|
|
|
|
+import com.etotem.cfc.entity.FamilyCoupon;
|
|
|
|
|
+import com.etotem.cfc.entity.FamilyCouponGrantLog;
|
|
|
|
|
+import com.etotem.cfc.entity.ReferralTree;
|
|
|
|
|
+import com.etotem.cfc.entity.User;
|
|
|
|
|
+import com.etotem.cfc.mapper.CouponGiftTransferMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.CouponMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.FamilyCouponGrantLogMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.FamilyCouponMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.ReferralTreeMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.UserMapper;
|
|
|
|
|
+import com.etotem.cfc.service.NotificationDispatchService;
|
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
|
|
+
|
|
|
|
|
+import javax.annotation.Resource;
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.Collections;
|
|
|
|
|
+import java.util.Date;
|
|
|
|
|
+import java.util.HashMap;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+import java.util.Objects;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 会员优惠券转赠服务。
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>仅支持将「会员专属券」(coupon.applicable_to = MEMBERSHIP)转赠给自己的推荐下级:
|
|
|
|
|
+ * 直推(referral_tree.level = 1)与隔级(level = 2)。转赠采用「转移已有券实例」方式,
|
|
|
|
|
+ * 接收方无需是会员即可用该券购买会员(family_coupon.is_gift = 1,apply 时豁免会员校验)。
|
|
|
|
|
+ * 赠送方在接收方核销前可撤回。</p>
|
|
|
|
|
+ */
|
|
|
|
|
+@Service
|
|
|
|
|
+public class CouponGiftService {
|
|
|
|
|
+
|
|
|
|
|
+ private static final Logger logger = LoggerFactory.getLogger(CouponGiftService.class);
|
|
|
|
|
+
|
|
|
|
|
+ private static final int GIFT_LEVEL_DIRECT = 1;
|
|
|
|
|
+ private static final int GIFT_LEVEL_INDIRECT = 2;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private FamilyCouponMapper familyCouponMapper;
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private CouponMapper couponMapper;
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private ReferralTreeMapper referralTreeMapper;
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private UserMapper userMapper;
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private FamilyCouponGrantLogMapper familyCouponGrantLogMapper;
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private CouponGiftTransferMapper couponGiftTransferMapper;
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private NotificationDispatchService notificationDispatchService;
|
|
|
|
|
+
|
|
|
|
|
+ /** 仅允许赠送的券范围 */
|
|
|
|
|
+ private static final String GIFTABLE_APPLICABLE_TO = "MEMBERSHIP";
|
|
|
|
|
+
|
|
|
|
|
+ // ----------------------------------------------------------------
|
|
|
|
|
+ // 查询
|
|
|
|
|
+ // ----------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 可赠送的接收人列表:赠送人的推荐下级(直推 + 隔级),且已加入家庭、非同家庭。
|
|
|
|
|
+ */
|
|
|
|
|
+ public List<Map<String, Object>> listRecipients(Long giverUserId) {
|
|
|
|
|
+ if (giverUserId == null) return new ArrayList<>();
|
|
|
|
|
+ Long giverFamilyId = getFamilyId(giverUserId);
|
|
|
|
|
+ if (giverFamilyId == null) return new ArrayList<>();
|
|
|
|
|
+ List<ReferralTree> rows = referralTreeMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<ReferralTree>()
|
|
|
|
|
+ .eq(ReferralTree::getParentId, giverUserId)
|
|
|
|
|
+ .in(ReferralTree::getLevel, GIFT_LEVEL_DIRECT, GIFT_LEVEL_INDIRECT)
|
|
|
|
|
+ .orderByAsc(ReferralTree::getLevel)
|
|
|
|
|
+ .orderByAsc(ReferralTree::getId));
|
|
|
|
|
+ if (rows.isEmpty()) return new ArrayList<>();
|
|
|
|
|
+ Map<Long, Integer> levelByChild = new HashMap<>();
|
|
|
|
|
+ List<Long> childIds = new ArrayList<>();
|
|
|
|
|
+ for (ReferralTree r : rows) {
|
|
|
|
|
+ if (r.getChildId() == null) continue;
|
|
|
|
|
+ if (!levelByChild.containsKey(r.getChildId())
|
|
|
|
|
+ || levelByChild.get(r.getChildId()) > r.getLevel()) {
|
|
|
|
|
+ levelByChild.put(r.getChildId(), r.getLevel());
|
|
|
|
|
+ }
|
|
|
|
|
+ childIds.add(r.getChildId());
|
|
|
|
|
+ }
|
|
|
|
|
+ List<User> users = userMapper.selectBatchIds(childIds);
|
|
|
|
|
+ if (users == null || users.isEmpty()) return new ArrayList<>();
|
|
|
|
|
+ List<Map<String, Object>> result = new ArrayList<>();
|
|
|
|
|
+ for (User u : users) {
|
|
|
|
|
+ if (u == null || u.getFamilyId() == null) continue;
|
|
|
|
|
+ if (Objects.equals(u.getFamilyId(), giverFamilyId)) continue; // 同家庭不赠送
|
|
|
|
|
+ Map<String, Object> m = new HashMap<>();
|
|
|
|
|
+ m.put("userId", u.getId());
|
|
|
|
|
+ m.put("nickname", StringUtils.hasText(u.getNickname()) ? u.getNickname() : "微信用户");
|
|
|
|
|
+ m.put("avatar", u.getAvatar());
|
|
|
|
|
+ m.put("familyId", u.getFamilyId());
|
|
|
|
|
+ m.put("giftLevel", levelByChild.getOrDefault(u.getId(), GIFT_LEVEL_DIRECT));
|
|
|
|
|
+ m.put("memberLevel", u.getMemberLevel());
|
|
|
|
|
+ m.put("isMember", "FAMILY".equals(u.getMemberLevel()) || "PREMIUM".equals(u.getMemberLevel()));
|
|
|
|
|
+ result.add(m);
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 赠送人可赠送的券列表:自己家庭券包中、可用的、applicableTo=MEMBERSHIP 的券。
|
|
|
|
|
+ */
|
|
|
|
|
+ public List<Map<String, Object>> listGiftableCoupons(Long giverUserId) {
|
|
|
|
|
+ Long familyId = getFamilyId(giverUserId);
|
|
|
|
|
+ if (familyId == null) return new ArrayList<>();
|
|
|
|
|
+ List<FamilyCoupon> familyCoupons = familyCouponMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<FamilyCoupon>()
|
|
|
|
|
+ .eq(FamilyCoupon::getFamilyId, familyId)
|
|
|
|
|
+ .eq(FamilyCoupon::getStatus, "AVAILABLE"));
|
|
|
|
|
+ if (familyCoupons.isEmpty()) return new ArrayList<>();
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ List<Map<String, Object>> result = new ArrayList<>();
|
|
|
|
|
+ for (FamilyCoupon fc : familyCoupons) {
|
|
|
|
|
+ Coupon coupon = couponMapper.selectById(fc.getCouponId());
|
|
|
|
|
+ if (coupon == null) continue;
|
|
|
|
|
+ if (!GIFTABLE_APPLICABLE_TO.equals(coupon.getApplicableTo())) continue;
|
|
|
|
|
+ if (!"ACTIVE".equals(coupon.getStatus())) continue;
|
|
|
|
|
+ if (coupon.getValidFrom() != null && now.before(coupon.getValidFrom())) continue;
|
|
|
|
|
+ if (coupon.getValidUntil() != null && now.after(coupon.getValidUntil())) continue;
|
|
|
|
|
+ // 已处于转赠中(is_gift=1 且仍在赠送方名下不可能,这里 family_id 仍是赠送方才列出)
|
|
|
|
|
+ result.add(toCouponVO(fc, coupon));
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 赠送人发出的转赠记录(含接收人信息、状态)。
|
|
|
|
|
+ */
|
|
|
|
|
+ public List<Map<String, Object>> listSent(Long giverUserId) {
|
|
|
|
|
+ if (giverUserId == null) return new ArrayList<>();
|
|
|
|
|
+ List<CouponGiftTransfer> transfers = couponGiftTransferMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<CouponGiftTransfer>()
|
|
|
|
|
+ .eq(CouponGiftTransfer::getGiverUserId, giverUserId)
|
|
|
|
|
+ .orderByDesc(CouponGiftTransfer::getCreatedAt));
|
|
|
|
|
+ if (transfers.isEmpty()) return new ArrayList<>();
|
|
|
|
|
+ List<Map<String, Object>> result = new ArrayList<>();
|
|
|
|
|
+ for (CouponGiftTransfer t : transfers) {
|
|
|
|
|
+ Coupon coupon = couponMapper.selectById(t.getCouponId());
|
|
|
|
|
+ User receiver = userMapper.selectById(t.getReceiverUserId());
|
|
|
|
|
+ Map<String, Object> m = new HashMap<>();
|
|
|
|
|
+ m.put("transferId", t.getId());
|
|
|
|
|
+ m.put("couponId", t.getCouponId());
|
|
|
|
|
+ m.put("couponName", coupon != null ? coupon.getName() : "优惠券");
|
|
|
|
|
+ m.put("couponValue", coupon != null ? coupon.getValue() : null);
|
|
|
|
|
+ m.put("receiverUserId", t.getReceiverUserId());
|
|
|
|
|
+ m.put("receiverNickname", receiver != null && StringUtils.hasText(receiver.getNickname())
|
|
|
|
|
+ ? receiver.getNickname() : "微信用户");
|
|
|
|
|
+ m.put("giftLevel", t.getGiftLevel());
|
|
|
|
|
+ m.put("giftLevelLabel", t.getGiftLevel() != null && t.getGiftLevel() == GIFT_LEVEL_INDIRECT ? "隔级" : "直推");
|
|
|
|
|
+ m.put("status", t.getStatus());
|
|
|
|
|
+ m.put("remark", t.getRemark());
|
|
|
|
|
+ m.put("createdAt", t.getCreatedAt());
|
|
|
|
|
+ m.put("usedAt", t.getUsedAt());
|
|
|
|
|
+ m.put("revokedAt", t.getRevokedAt());
|
|
|
|
|
+ result.add(m);
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 接收人收到的、且仍可使用的转赠券(供前端在「我的券」中提示来源,可选)。
|
|
|
|
|
+ */
|
|
|
|
|
+ public List<Map<String, Object>> listReceived(Long receiverUserId) {
|
|
|
|
|
+ if (receiverUserId == null) return new ArrayList<>();
|
|
|
|
|
+ List<CouponGiftTransfer> transfers = couponGiftTransferMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<CouponGiftTransfer>()
|
|
|
|
|
+ .eq(CouponGiftTransfer::getReceiverUserId, receiverUserId)
|
|
|
|
|
+ .eq(CouponGiftTransfer::getStatus, "GIFTED")
|
|
|
|
|
+ .orderByDesc(CouponGiftTransfer::getCreatedAt));
|
|
|
|
|
+ if (transfers.isEmpty()) return new ArrayList<>();
|
|
|
|
|
+ return transfers.stream().map(t -> {
|
|
|
|
|
+ Coupon coupon = couponMapper.selectById(t.getCouponId());
|
|
|
|
|
+ User giver = userMapper.selectById(t.getGiverUserId());
|
|
|
|
|
+ Map<String, Object> m = new HashMap<>();
|
|
|
|
|
+ m.put("transferId", t.getId());
|
|
|
|
|
+ m.put("giverNickname", giver != null && StringUtils.hasText(giver.getNickname())
|
|
|
|
|
+ ? giver.getNickname() : "好友");
|
|
|
|
|
+ m.put("couponName", coupon != null ? coupon.getName() : "优惠券");
|
|
|
|
|
+ m.put("giftLevelLabel", t.getGiftLevel() != null && t.getGiftLevel() == GIFT_LEVEL_INDIRECT ? "隔级" : "直推");
|
|
|
|
|
+ m.put("createdAt", t.getCreatedAt());
|
|
|
|
|
+ return m;
|
|
|
|
|
+ }).collect(Collectors.toList());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ----------------------------------------------------------------
|
|
|
|
|
+ // 写操作
|
|
|
|
|
+ // ----------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 转赠一张券给推荐下级。返回 transferId;失败抛出 IllegalArgumentException。
|
|
|
|
|
+ */
|
|
|
|
|
+ @Transactional
|
|
|
|
|
+ public Long transfer(Long giverUserId, Long receiverUserId, Long familyCouponId, String remark) {
|
|
|
|
|
+ if (giverUserId == null || receiverUserId == null || familyCouponId == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("参数不完整");
|
|
|
|
|
+ }
|
|
|
|
|
+ Long giverFamilyId = getFamilyId(giverUserId);
|
|
|
|
|
+ if (giverFamilyId == null) throw new IllegalArgumentException("请先加入家庭");
|
|
|
|
|
+ if (Objects.equals(giverUserId, receiverUserId)) throw new IllegalArgumentException("不能转赠给自己");
|
|
|
|
|
+
|
|
|
|
|
+ FamilyCoupon fc = familyCouponMapper.selectById(familyCouponId);
|
|
|
|
|
+ if (fc == null || !Objects.equals(fc.getFamilyId(), giverFamilyId)
|
|
|
|
|
+ || !"AVAILABLE".equals(fc.getStatus())) {
|
|
|
|
|
+ throw new IllegalArgumentException("该优惠券不可用或不属于你");
|
|
|
|
|
+ }
|
|
|
|
|
+ Coupon coupon = couponMapper.selectById(fc.getCouponId());
|
|
|
|
|
+ if (coupon == null || !"ACTIVE".equals(coupon.getStatus())) {
|
|
|
|
|
+ throw new IllegalArgumentException("优惠券已失效");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!GIFTABLE_APPLICABLE_TO.equals(coupon.getApplicableTo())) {
|
|
|
|
|
+ throw new IllegalArgumentException("仅会员专属券可转赠");
|
|
|
|
|
+ }
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ if (coupon.getValidFrom() != null && now.before(coupon.getValidFrom())
|
|
|
|
|
+ || coupon.getValidUntil() != null && now.after(coupon.getValidUntil())) {
|
|
|
|
|
+ throw new IllegalArgumentException("优惠券已过期");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ User receiver = userMapper.selectById(receiverUserId);
|
|
|
|
|
+ if (receiver == null || receiver.getFamilyId() == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("接收人未加入家庭,无法接收");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (Objects.equals(receiver.getFamilyId(), giverFamilyId)) {
|
|
|
|
|
+ throw new IllegalArgumentException("不能转赠给同家庭成员");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 必须是赠送人的推荐下级(直推 / 隔级)
|
|
|
|
|
+ ReferralTree referral = referralTreeMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<ReferralTree>()
|
|
|
|
|
+ .eq(ReferralTree::getParentId, giverUserId)
|
|
|
|
|
+ .eq(ReferralTree::getChildId, receiverUserId)
|
|
|
|
|
+ .in(ReferralTree::getLevel, GIFT_LEVEL_DIRECT, GIFT_LEVEL_INDIRECT)
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ if (referral == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("只能转赠给自己的直推或隔级推荐人");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 转移券实例到接收方家庭,并打 is_gift 标记(接收方非会员也可用其购买会员)
|
|
|
|
|
+ fc.setFamilyId(receiver.getFamilyId());
|
|
|
|
|
+ fc.setIsGift(1);
|
|
|
|
|
+ familyCouponMapper.updateById(fc);
|
|
|
|
|
+
|
|
|
|
|
+ CouponGiftTransfer transfer = new CouponGiftTransfer();
|
|
|
|
|
+ transfer.setGiverUserId(giverUserId);
|
|
|
|
|
+ transfer.setGiverFamilyId(giverFamilyId);
|
|
|
|
|
+ transfer.setReceiverUserId(receiverUserId);
|
|
|
|
|
+ transfer.setReceiverFamilyId(receiver.getFamilyId());
|
|
|
|
|
+ transfer.setCouponId(coupon.getId());
|
|
|
|
|
+ transfer.setFamilyCouponId(fc.getId());
|
|
|
|
|
+ transfer.setGiftLevel(referral.getLevel());
|
|
|
|
|
+ transfer.setStatus("GIFTED");
|
|
|
|
|
+ transfer.setRemark(remark);
|
|
|
|
|
+ transfer.setCreatedAt(now);
|
|
|
|
|
+ couponGiftTransferMapper.insert(transfer);
|
|
|
|
|
+
|
|
|
|
|
+ // 通知接收人收到券(失败不影响转赠主流程)
|
|
|
|
|
+ try {
|
|
|
|
|
+ User giver = userMapper.selectById(giverUserId);
|
|
|
|
|
+ String giverName = (giver != null && StringUtils.hasText(giver.getNickname()))
|
|
|
|
|
+ ? giver.getNickname() : "好友";
|
|
|
|
|
+ Map<String, Object> vars = NotificationDispatchService.vars("giverName", giverName);
|
|
|
|
|
+ vars.put("couponName", coupon.getName());
|
|
|
|
|
+ notificationDispatchService.sendByTemplate(
|
|
|
|
|
+ "coupon_gift",
|
|
|
|
|
+ receiverUserId,
|
|
|
|
|
+ vars,
|
|
|
|
|
+ "coupon_gift",
|
|
|
|
|
+ transfer.getId(),
|
|
|
|
|
+ "你收到一张会员优惠券",
|
|
|
|
|
+ "{giverName} 送了你一张「{couponName}」会员专属券,可用于开通会员。");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ logger.warn("转赠通知发送失败(不影响转赠): transferId={}, error={}",
|
|
|
|
|
+ transfer.getId(), e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 发放流水(审计)
|
|
|
|
|
+ FamilyCouponGrantLog log = new FamilyCouponGrantLog();
|
|
|
|
|
+ log.setFamilyId(receiver.getFamilyId());
|
|
|
|
|
+ log.setCouponId(coupon.getId());
|
|
|
|
|
+ log.setGrantType("GIFT_TRANSFER");
|
|
|
|
|
+ log.setPeriod(null);
|
|
|
|
|
+ log.setQuantity(1);
|
|
|
|
|
+ log.setSource("transfer:" + transfer.getId());
|
|
|
|
|
+ log.setCreatedAt(now);
|
|
|
|
|
+ familyCouponGrantLogMapper.insert(log);
|
|
|
|
|
+
|
|
|
|
|
+ logger.info("会员券转赠成功: giver={}, receiver={}, familyCouponId={}, level={}, transferId={}",
|
|
|
|
|
+ giverUserId, receiverUserId, familyCouponId, referral.getLevel(), transfer.getId());
|
|
|
|
|
+ return transfer.getId();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 撤回一笔转赠(接收方未使用前)。返回 true 成功。
|
|
|
|
|
+ */
|
|
|
|
|
+ @Transactional
|
|
|
|
|
+ public boolean revoke(Long transferId, Long giverUserId) {
|
|
|
|
|
+ CouponGiftTransfer transfer = couponGiftTransferMapper.selectById(transferId);
|
|
|
|
|
+ if (transfer == null) throw new IllegalArgumentException("转赠记录不存在");
|
|
|
|
|
+ if (!Objects.equals(transfer.getGiverUserId(), giverUserId)) {
|
|
|
|
|
+ throw new IllegalArgumentException("无权操作该转赠");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!"GIFTED".equals(transfer.getStatus())) {
|
|
|
|
|
+ throw new IllegalArgumentException("该券已被使用或已撤回,无法撤回");
|
|
|
|
|
+ }
|
|
|
|
|
+ FamilyCoupon fc = familyCouponMapper.selectById(transfer.getFamilyCouponId());
|
|
|
|
|
+ if (fc == null || !Objects.equals(fc.getFamilyId(), transfer.getReceiverFamilyId())
|
|
|
|
|
+ || !"AVAILABLE".equals(fc.getStatus()) || !Objects.equals(fc.getIsGift(), 1)) {
|
|
|
|
|
+ throw new IllegalArgumentException("该券已被使用,无法撤回");
|
|
|
|
|
+ }
|
|
|
|
|
+ // 券实例回退到赠送方家庭
|
|
|
|
|
+ fc.setFamilyId(transfer.getGiverFamilyId());
|
|
|
|
|
+ fc.setIsGift(0);
|
|
|
|
|
+ familyCouponMapper.updateById(fc);
|
|
|
|
|
+
|
|
|
|
|
+ transfer.setStatus("REVOKED");
|
|
|
|
|
+ transfer.setRevokedAt(new Date());
|
|
|
|
|
+ couponGiftTransferMapper.updateById(transfer);
|
|
|
|
|
+ logger.info("会员券转赠撤回: transferId={}, giver={}", transferId, giverUserId);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 券被核销时由 CouponService.markUsed 回调:将对应转赠记录置为 USED。
|
|
|
|
|
+ */
|
|
|
|
|
+ @Transactional
|
|
|
|
|
+ public void markUsedByFamilyCouponId(Long familyCouponId, Long orderId) {
|
|
|
|
|
+ if (familyCouponId == null) return;
|
|
|
|
|
+ CouponGiftTransfer transfer = couponGiftTransferMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<CouponGiftTransfer>()
|
|
|
|
|
+ .eq(CouponGiftTransfer::getFamilyCouponId, familyCouponId)
|
|
|
|
|
+ .eq(CouponGiftTransfer::getStatus, "GIFTED")
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ if (transfer != null) {
|
|
|
|
|
+ transfer.setStatus("USED");
|
|
|
|
|
+ transfer.setUsedAt(new Date());
|
|
|
|
|
+ couponGiftTransferMapper.updateById(transfer);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ----------------------------------------------------------------
|
|
|
|
|
+ // 内部辅助
|
|
|
|
|
+ // ----------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 管理端:转赠记录分页查询(可按赠送方家庭 / 接收方家庭 / 状态 / 券模板过滤)。
|
|
|
|
|
+ */
|
|
|
|
|
+ public Page<Map<String, Object>> adminList(Long giverFamilyId, Long receiverFamilyId,
|
|
|
|
|
+ String status, Long couponId, int page, int size) {
|
|
|
|
|
+ LambdaQueryWrapper<CouponGiftTransfer> wrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ if (giverFamilyId != null) {
|
|
|
|
|
+ wrapper.eq(CouponGiftTransfer::getGiverFamilyId, giverFamilyId);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (receiverFamilyId != null) {
|
|
|
|
|
+ wrapper.eq(CouponGiftTransfer::getReceiverFamilyId, receiverFamilyId);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (status != null && !status.isEmpty()) {
|
|
|
|
|
+ wrapper.eq(CouponGiftTransfer::getStatus, status);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (couponId != null) {
|
|
|
|
|
+ wrapper.eq(CouponGiftTransfer::getCouponId, couponId);
|
|
|
|
|
+ }
|
|
|
|
|
+ wrapper.orderByDesc(CouponGiftTransfer::getCreatedAt);
|
|
|
|
|
+ Page<CouponGiftTransfer> pg = couponGiftTransferMapper.selectPage(new Page<>(page, size), wrapper);
|
|
|
|
|
+
|
|
|
|
|
+ List<Map<String, Object>> records = pg.getRecords().stream().map(t -> {
|
|
|
|
|
+ Coupon c = couponMapper.selectById(t.getCouponId());
|
|
|
|
|
+ User giver = userMapper.selectById(t.getGiverUserId());
|
|
|
|
|
+ User receiver = userMapper.selectById(t.getReceiverUserId());
|
|
|
|
|
+ Map<String, Object> m = new HashMap<>();
|
|
|
|
|
+ m.put("transferId", t.getId());
|
|
|
|
|
+ m.put("giverUserId", t.getGiverUserId());
|
|
|
|
|
+ m.put("giverNickname", giver != null && StringUtils.hasText(giver.getNickname())
|
|
|
|
|
+ ? giver.getNickname() : "用户" + t.getGiverUserId());
|
|
|
|
|
+ m.put("giverFamilyId", t.getGiverFamilyId());
|
|
|
|
|
+ m.put("receiverUserId", t.getReceiverUserId());
|
|
|
|
|
+ m.put("receiverNickname", receiver != null && StringUtils.hasText(receiver.getNickname())
|
|
|
|
|
+ ? receiver.getNickname() : "用户" + t.getReceiverUserId());
|
|
|
|
|
+ m.put("receiverFamilyId", t.getReceiverFamilyId());
|
|
|
|
|
+ m.put("couponId", t.getCouponId());
|
|
|
|
|
+ m.put("couponName", c != null ? c.getName() : "优惠券");
|
|
|
|
|
+ m.put("couponValue", c != null ? c.getValue() : null);
|
|
|
|
|
+ m.put("giftLevel", t.getGiftLevel());
|
|
|
|
|
+ m.put("giftLevelLabel", t.getGiftLevel() != null && t.getGiftLevel() == GIFT_LEVEL_INDIRECT ? "隔级" : "直推");
|
|
|
|
|
+ m.put("status", t.getStatus());
|
|
|
|
|
+ m.put("remark", t.getRemark());
|
|
|
|
|
+ m.put("createdAt", t.getCreatedAt());
|
|
|
|
|
+ m.put("usedAt", t.getUsedAt());
|
|
|
|
|
+ m.put("revokedAt", t.getRevokedAt());
|
|
|
|
|
+ return m;
|
|
|
|
|
+ }).collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ Page<Map<String, Object>> result = new Page<>(pg.getCurrent(), pg.getSize(), pg.getTotal());
|
|
|
|
|
+ result.setRecords(records);
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Long getFamilyId(Long userId) {
|
|
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
|
|
+ if (user == null || user.getFamilyId() == null) return null;
|
|
|
|
|
+ return user.getFamilyId();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Map<String, Object> toCouponVO(FamilyCoupon fc, Coupon coupon) {
|
|
|
|
|
+ Map<String, Object> item = new HashMap<>();
|
|
|
|
|
+ item.put("userCouponId", fc.getId());
|
|
|
|
|
+ item.put("id", fc.getId());
|
|
|
|
|
+ item.put("couponId", coupon.getId());
|
|
|
|
|
+ item.put("name", coupon.getName());
|
|
|
|
|
+ item.put("type", coupon.getType());
|
|
|
|
|
+ item.put("value", coupon.getValue());
|
|
|
|
|
+ item.put("discountRate", coupon.getDiscountRate());
|
|
|
|
|
+ item.put("minSpend", coupon.getMinSpend());
|
|
|
|
|
+ item.put("applicableTo", coupon.getApplicableTo());
|
|
|
|
|
+ item.put("validFrom", coupon.getValidFrom());
|
|
|
|
|
+ item.put("validUntil", coupon.getValidUntil());
|
|
|
|
|
+ item.put("status", "AVAILABLE");
|
|
|
|
|
+ item.put("isGift", 0);
|
|
|
|
|
+ return item;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|