|
@@ -1,23 +1,48 @@
|
|
|
package com.chelvc.cloud.vehicle.server.service.impl;
|
|
|
|
|
|
+import cn.hutool.json.JSONArray;
|
|
|
+import cn.hutool.json.JSONUtil;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.chelvc.cloud.uc.api.service.UserService;
|
|
|
+import com.chelvc.cloud.vehicle.api.constant.CouponActivitySendType;
|
|
|
+import com.chelvc.cloud.vehicle.api.constant.CouponActivityType;
|
|
|
+import com.chelvc.cloud.vehicle.api.constant.PromotionScopeType;
|
|
|
+import com.chelvc.cloud.vehicle.api.constant.PromotionStatus;
|
|
|
+import com.chelvc.cloud.vehicle.api.constant.PromotionType;
|
|
|
+import com.chelvc.cloud.vehicle.api.constant.UserCouponStatus;
|
|
|
import com.chelvc.cloud.vehicle.api.dto.CouponActivityDTO;
|
|
|
import com.chelvc.cloud.vehicle.api.param.CouponActivityModifyParam;
|
|
|
import com.chelvc.cloud.vehicle.api.param.CouponActivityPagingParam;
|
|
|
+import com.chelvc.cloud.vehicle.api.util.PromotionUtils;
|
|
|
import com.chelvc.cloud.vehicle.server.copier.CouponActivityCopier;
|
|
|
import com.chelvc.cloud.vehicle.server.dao.CouponActivityMapper;
|
|
|
+import com.chelvc.cloud.vehicle.server.entity.Coupon;
|
|
|
import com.chelvc.cloud.vehicle.server.entity.CouponActivity;
|
|
|
+import com.chelvc.cloud.vehicle.server.entity.CouponActivityItem;
|
|
|
+import com.chelvc.cloud.vehicle.server.entity.User;
|
|
|
+import com.chelvc.cloud.vehicle.server.entity.UserCoupon;
|
|
|
+import com.chelvc.cloud.vehicle.server.service.CouponActivityItemService;
|
|
|
import com.chelvc.cloud.vehicle.server.service.CouponActivityService;
|
|
|
+import com.chelvc.cloud.vehicle.server.service.CouponService;
|
|
|
+import com.chelvc.cloud.vehicle.server.service.UserCouponService;
|
|
|
+import com.chelvc.framework.base.exception.ResourceUnavailableException;
|
|
|
import com.chelvc.framework.base.util.ResourceUtils;
|
|
|
import com.chelvc.framework.common.model.Pagination;
|
|
|
import com.chelvc.framework.database.util.PagingUtils;
|
|
|
+import com.chelvc.framework.redis.cache.Cache;
|
|
|
+import com.chelvc.framework.redis.cache.CachePrefix;
|
|
|
import lombok.NonNull;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
import org.apache.dubbo.config.annotation.DubboService;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
import org.springframework.util.CollectionUtils;
|
|
|
import org.springframework.util.StringUtils;
|
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Date;
|
|
|
import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
import java.util.Objects;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
@@ -27,10 +52,19 @@ import java.util.stream.Collectors;
|
|
|
* @author qizai
|
|
|
* @date 2023/11/10
|
|
|
*/
|
|
|
+@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
|
|
@DubboService(interfaceClass = com.chelvc.cloud.vehicle.api.service.CouponActivityService.class)
|
|
|
-public class CouponActivityServiceImpl extends ServiceImpl<CouponActivityMapper, CouponActivity> implements CouponActivityService,
|
|
|
+public class CouponActivityServiceImpl extends AbstractPromotionServiceImpl<CouponActivityMapper, CouponActivity> implements CouponActivityService,
|
|
|
com.chelvc.cloud.vehicle.api.service.CouponActivityService {
|
|
|
|
|
|
+ private final UserService userService;
|
|
|
+ private final CouponService couponService;
|
|
|
+ private final UserCouponService userCouponService;
|
|
|
+ private final CouponActivityItemService couponActivityItemService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private Cache<List<CouponActivity>> cache;
|
|
|
+
|
|
|
@Override
|
|
|
public Long addCouponActivity(@NonNull CouponActivityModifyParam param) {
|
|
|
CouponActivity couponActivity = CouponActivityCopier.INSTANCE.copying(param);
|
|
@@ -77,6 +111,248 @@ public class CouponActivityServiceImpl extends ServiceImpl<CouponActivityMapper,
|
|
|
return PagingUtils.convert(page, clients);
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public void initPromotion(CouponActivity promotion) {
|
|
|
+ super.initPromotion(promotion);
|
|
|
+ //如果有会员,则写入会员信息
|
|
|
+ if (promotion.getUsers() != null && !promotion.getUsers().isEmpty()) {
|
|
|
+ promotion.setActivityScopeInfo(JSONUtil.toJsonStr(promotion.getUsers()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查优惠券活动参数
|
|
|
+ *
|
|
|
+ * @param couponActivity 优惠券活动实体
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void checkPromotion(CouponActivity couponActivity) {
|
|
|
+ super.checkPromotion(couponActivity);
|
|
|
+ //指定会员判定
|
|
|
+ if (couponActivity.getActivityScope().equals(CouponActivitySendType.DESIGNATED.name())
|
|
|
+ && couponActivity.getUsers().isEmpty()) {
|
|
|
+ throw new ResourceUnavailableException("指定精准发券则必须指定会员,会员不可以为空");
|
|
|
+ }
|
|
|
+ // 检查优惠券
|
|
|
+ this.checkCouponActivityItem(couponActivity.getCouponActivityItems());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新优惠券活动商品信息
|
|
|
+ *
|
|
|
+ * @param couponActivity 优惠券活动实体
|
|
|
+ * @return 是否更新成功
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = {Exception.class})
|
|
|
+ public boolean updatePromotionsGoods(CouponActivity couponActivity) {
|
|
|
+ boolean result = super.updatePromotionsGoods(couponActivity);
|
|
|
+ if (!PromotionStatus.CLOSE.name().equals(couponActivity.getPromotionStatus())
|
|
|
+ && PromotionScopeType.PORTION_GOODS.name().equals(couponActivity.getScopeType())) {
|
|
|
+ //创建优惠券活动子列表
|
|
|
+ for (CouponActivityItem couponActivityItem : couponActivity.getCouponActivityItems()) {
|
|
|
+ couponActivityItem.setActivityId(couponActivity.getId());
|
|
|
+ }
|
|
|
+ // 更新优惠券活动项信息
|
|
|
+ result = this.couponActivityItemService.saveBatch(couponActivity.getCouponActivityItems());
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新优惠券活动信息到商品索引
|
|
|
+ *
|
|
|
+ * @param couponActivity 促销实体
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void updateEsGoodsIndex(CouponActivity couponActivity) {
|
|
|
+ switch (couponActivity.getCouponActivityType()) {
|
|
|
+ // 精准发券 则立即发放
|
|
|
+ case SPECIFY:
|
|
|
+ this.specify(couponActivity);
|
|
|
+ break;
|
|
|
+ //其他活动则是缓存模块,根据缓存中的优惠券活动信息来确认发放优惠券策略
|
|
|
+ case INVITE_NEW:
|
|
|
+ case AUTO_COUPON:
|
|
|
+ case REGISTERED:
|
|
|
+ this.resetCache(couponActivity.getCouponActivityType());
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void specify(CouponActivity couponActivity) {
|
|
|
+ //获取活动优惠券发送范围
|
|
|
+ List<Map<String, Object>> users = this.getUserList(couponActivity);
|
|
|
+
|
|
|
+ //如果指定会员发券,则当下直接进行发送,如果是全体会员发券,则变更为用户登录首页进行请求发券
|
|
|
+ //PS:即不主动发券,需要用户在活动时间内登录自动领取优惠券,类似美团、饿了么 的发放方式
|
|
|
+ if (couponActivity.getActivityScope().equals(CouponActivitySendType.DESIGNATED.name())) {
|
|
|
+ //会员拆成多个小组进行发送
|
|
|
+ List<List<Map<String, Object>>> userGroup = new ArrayList<>();
|
|
|
+
|
|
|
+ //循环分组
|
|
|
+ for (int i = 0; i < (users.size() / 100 + (users.size() % 100 == 0 ? 0 : 1)); i++) {
|
|
|
+ int endPoint = Math.min((100 + (i * 100)), users.size());
|
|
|
+ userGroup.add(users.subList((i * 100), endPoint));
|
|
|
+ }
|
|
|
+
|
|
|
+ //优惠券活动的优惠券列表
|
|
|
+ List<CouponActivityItem> couponActivityItems = this.couponActivityItemService
|
|
|
+ .listCouponActivityItems(couponActivity.getId());
|
|
|
+ //发送优惠券
|
|
|
+ for (List<Map<String, Object>> memberList : userGroup) {
|
|
|
+ sendCoupon(memberList, couponActivityItems);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 重写缓存中的活动优惠券信息
|
|
|
+ */
|
|
|
+ private List<CouponActivity> resetCache(CouponActivityType couponActivityType) {
|
|
|
+ //查询出结果,缓存后返回
|
|
|
+ List<CouponActivity> couponActivities = this.lambdaQuery()
|
|
|
+ .gt(CouponActivity::getEndTime, new Date())
|
|
|
+ .eq(CouponActivity::getActivityScope, CouponActivitySendType.ALL)
|
|
|
+ .eq(CouponActivity::getCouponActivityType, couponActivityType)
|
|
|
+ .list();
|
|
|
+ List<CouponActivity> couponActivitys = new ArrayList<>();
|
|
|
+ for (CouponActivity couponActivity : couponActivities) {
|
|
|
+ couponActivity.setCouponActivityItems(
|
|
|
+ this.couponActivityItemService.listCouponActivityItems(couponActivity.getId()));
|
|
|
+ couponActivitys.add(couponActivity);
|
|
|
+ }
|
|
|
+
|
|
|
+ cache.put(cacheKey(couponActivityType.name()), couponActivitys);
|
|
|
+ return couponActivitys;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 缓存key生成策略
|
|
|
+ *
|
|
|
+ * @param couponActivityType 优惠券活动类型
|
|
|
+ * @return 缓存key
|
|
|
+ */
|
|
|
+ private String cacheKey(String couponActivityType) {
|
|
|
+ return CachePrefix.CURRENT_COUPON_ACTIVITY.getPrefix() + couponActivityType;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 定向发送优惠券
|
|
|
+ *
|
|
|
+ * @param userList 用户列表
|
|
|
+ * @param couponActivityItems 优惠券列表
|
|
|
+ */
|
|
|
+ private void sendCoupon(List<Map<String, Object>> userList, List<CouponActivityItem> couponActivityItems) {
|
|
|
+ for (Map<String, Object> map : userList) {
|
|
|
+ User user = new User();
|
|
|
+ user.setId(Long.valueOf(map.get("id").toString()));
|
|
|
+ user.setNickname(map.get("nick_name").toString());
|
|
|
+ this.sendCoupon(user, couponActivityItems);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 给当前用户发送优惠券
|
|
|
+ * 1.循环优惠券列表
|
|
|
+ * 2.判断优惠券每个会员发送数量
|
|
|
+ * 3.记录优惠券发送数量
|
|
|
+ *
|
|
|
+ * @param user 发送目标用户
|
|
|
+ * @param couponActivityItems 优惠券列表
|
|
|
+ */
|
|
|
+ private List<UserCoupon> sendCoupon(User user, List<? extends CouponActivityItem> couponActivityItems) {
|
|
|
+ //最终优惠券列表
|
|
|
+ List<UserCoupon> finalCoupons = new ArrayList<>();
|
|
|
+
|
|
|
+ //循环优惠券赠送列表
|
|
|
+ for (CouponActivityItem couponActivityItem : couponActivityItems) {
|
|
|
+ //获取优惠券
|
|
|
+ Coupon coupon = this.couponService.getById(couponActivityItem.getCouponId());
|
|
|
+ //判断优惠券是否存在
|
|
|
+ if (coupon != null) {
|
|
|
+ //循环优惠券的领取数量
|
|
|
+ int activitySendNum = couponActivityItem.getNum();
|
|
|
+
|
|
|
+ List<UserCoupon> userCouponList = new ArrayList<>();
|
|
|
+ for (int i = 1; i <= activitySendNum; i++) {
|
|
|
+ UserCoupon userCoupon = UserCoupon.builder()
|
|
|
+ .userId(user.getId())
|
|
|
+ .couponId(coupon.getId())
|
|
|
+ .userCouponStatus(UserCouponStatus.CLAIMED)
|
|
|
+ .platformFlag(PromotionUtils.PLATFORM_ID.equals(coupon.getMerchantId()))
|
|
|
+ .build();
|
|
|
+ userCouponList.add(userCoupon);
|
|
|
+ }
|
|
|
+
|
|
|
+ finalCoupons.addAll(userCouponList);
|
|
|
+ //批量添加优惠券
|
|
|
+ this.userCouponService.saveBatch(userCouponList);
|
|
|
+ //添加优惠券已领取数量
|
|
|
+ this.couponService.updateCouponReceivedNum(couponActivityItem.getCouponId(), userCouponList.size());
|
|
|
+ } else {
|
|
|
+ log.error("赠送优惠券失败,当前优惠券不存在:" + couponActivityItem.getCouponId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (finalCoupons.isEmpty()) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+ return finalCoupons;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 当前促销类型
|
|
|
+ *
|
|
|
+ * @return 当前促销类型
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public PromotionType getPromotionType() {
|
|
|
+ return PromotionType.COUPON_ACTIVITY;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取优惠券的范围
|
|
|
+ * 此方法用于精准发券
|
|
|
+ *
|
|
|
+ * @param couponActivity 优惠券活动
|
|
|
+ * @return 获取优惠券的用戶列表
|
|
|
+ */
|
|
|
+ private List<Map<String, Object>> getUserList(CouponActivity couponActivity) {
|
|
|
+ //判断优惠券的发送范围,获取用戶列表
|
|
|
+ List<String> ids = new ArrayList<>();
|
|
|
+ if (JSONUtil.isTypeJSONArray(couponActivity.getActivityScopeInfo())) {
|
|
|
+ JSONArray array = JSONUtil.parseArray(couponActivity.getActivityScopeInfo());
|
|
|
+ ids = array.toList(Map.class).stream().map(i -> i.get("id").toString()).collect(Collectors.toList());
|
|
|
+ }
|
|
|
+ return this.userService.listFieldsByUserIds("id,nick_name", ids);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查优惠券
|
|
|
+ *
|
|
|
+ * @param couponActivityItems 优惠券列表
|
|
|
+ */
|
|
|
+ private void checkCouponActivityItem(List<CouponActivityItem> couponActivityItems) {
|
|
|
+ //优惠券数量判定
|
|
|
+ if (couponActivityItems.isEmpty()) {
|
|
|
+ throw new ResourceUnavailableException("优惠券活动必须指定优惠券,不能为空");
|
|
|
+ } else if (couponActivityItems.size() > 10) {
|
|
|
+ throw new ResourceUnavailableException("优惠券活动最多指定10个优惠券");
|
|
|
+ } else {
|
|
|
+ for (CouponActivityItem item : couponActivityItems) {
|
|
|
+ if (item.getNum() == null || item.getNum() <= 0) {
|
|
|
+ throw new ResourceUnavailableException("赠券数量必须大于0");
|
|
|
+ }
|
|
|
+ if (item.getNum() > 2) {
|
|
|
+ throw new ResourceUnavailableException("赠券数量最大为2");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 转换优惠券活动信息
|
|
|
*
|