|
|
|
|
@@ -0,0 +1,279 @@
|
|
|
|
|
package com.czg.service.account.service.impl;
|
|
|
|
|
|
|
|
|
|
import cn.hutool.core.bean.BeanUtil;
|
|
|
|
|
import cn.hutool.core.collection.CollUtil;
|
|
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
|
|
import com.alibaba.fastjson2.JSONArray;
|
|
|
|
|
import com.alibaba.fastjson2.JSONObject;
|
|
|
|
|
import com.czg.account.dto.MemberConfigDTO;
|
|
|
|
|
import com.czg.account.dto.MemberLevelDTO;
|
|
|
|
|
import com.czg.account.entity.*;
|
|
|
|
|
import com.czg.account.service.*;
|
|
|
|
|
import com.czg.account.vo.MemberConfigVO;
|
|
|
|
|
import com.czg.account.vo.MemberLevelVO;
|
|
|
|
|
import com.czg.exception.CzgException;
|
|
|
|
|
import com.czg.order.entity.OrderInfo;
|
|
|
|
|
import com.czg.order.entity.OrderPayment;
|
|
|
|
|
import com.czg.order.service.OrderInfoService;
|
|
|
|
|
import com.czg.order.service.OrderPaymentService;
|
|
|
|
|
import com.czg.service.order.enums.OrderStatusEnums;
|
|
|
|
|
import com.czg.validator.ValidatorUtil;
|
|
|
|
|
import com.czg.validator.group.member.MemberLevelCycleRewardGroup;
|
|
|
|
|
import com.mybatisflex.core.query.QueryWrapper;
|
|
|
|
|
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
|
|
|
|
import com.czg.service.account.mapper.TbMemberConfigMapper;
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
import org.apache.dubbo.config.annotation.DubboReference;
|
|
|
|
|
import org.apache.dubbo.config.annotation.DubboService;
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Optional;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 会员基础配置 服务层实现。
|
|
|
|
|
*
|
|
|
|
|
* @author zs
|
|
|
|
|
* @since 2025-09-10
|
|
|
|
|
*/
|
|
|
|
|
@Slf4j
|
|
|
|
|
@DubboService
|
|
|
|
|
public class TbMemberConfigServiceImpl extends ServiceImpl<TbMemberConfigMapper, TbMemberConfig> implements TbMemberConfigService {
|
|
|
|
|
private final List<String> conditionMap = CollUtil.toList("BIND_PHONE", "ORDER", "COST_AMOUNT", "RECHARGE_AMOUNT");
|
|
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
private MemberLevelConfigService levelConfigService;
|
|
|
|
|
@Resource
|
|
|
|
|
private UserInfoService userInfoService;
|
|
|
|
|
@Resource
|
|
|
|
|
private ShopUserService shopUserService;
|
|
|
|
|
@DubboReference
|
|
|
|
|
private OrderInfoService orderInfoService;
|
|
|
|
|
@DubboReference
|
|
|
|
|
private OrderPaymentService orderPaymentService;
|
|
|
|
|
@DubboReference
|
|
|
|
|
private MemberPointsService memberPointsService;
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public MemberConfigVO detail(Long shopId) {
|
|
|
|
|
TbMemberConfig memberConfig = getOne(new QueryWrapper().eq(TbMemberConfig::getShopId, shopId));
|
|
|
|
|
if (memberConfig == null) {
|
|
|
|
|
memberConfig = new TbMemberConfig();
|
|
|
|
|
memberConfig.setShopId(shopId);
|
|
|
|
|
save(memberConfig);
|
|
|
|
|
memberConfig = getOne(new QueryWrapper().eq(TbMemberConfig::getShopId, shopId));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MemberConfigVO memberConfigVO = BeanUtil.copyProperties(memberConfig, MemberConfigVO.class, "configList", "conditionList");
|
|
|
|
|
if (StrUtil.isNotBlank(memberConfig.getConfigList())) {
|
|
|
|
|
memberConfigVO.setConfigList(JSONArray.parseArray(memberConfig.getConfigList()).toList(MemberConfigDTO.ConfigList.class));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (StrUtil.isNotBlank(memberConfig.getConditionList())) {
|
|
|
|
|
memberConfigVO.setConditionList(JSONArray.parseArray(memberConfig.getConditionList()).toList(MemberConfigDTO.condition.class));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (StrUtil.isNotBlank(memberConfig.getMemberPriceShopIdList())) {
|
|
|
|
|
memberConfigVO.setMemberPriceShopIdList(JSONArray.parseArray(memberConfig.getMemberPriceShopIdList()).toList(Long.class));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return memberConfigVO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Boolean edit(Long shopId, MemberConfigDTO memberDTO) {
|
|
|
|
|
TbMemberConfig memberConfig = getOne(new QueryWrapper().eq(TbMemberConfig::getShopId, shopId));
|
|
|
|
|
BeanUtil.copyProperties(memberDTO, memberConfig);
|
|
|
|
|
if ((memberDTO.getConfigList() == null || memberDTO.getConfigList().isEmpty()) && memberDTO.getOpenAmount() == null) {
|
|
|
|
|
throw new CzgException("会员开通方式必须选择一个");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (memberDTO.getConfigList() != null && !memberDTO.getConfigList().isEmpty()) {
|
|
|
|
|
memberDTO.getConfigList().forEach(item -> {
|
|
|
|
|
if (item.getReward() == null && (item.getCouponList() == null || item.getCouponList().isEmpty())) {
|
|
|
|
|
throw new CzgException("方案列表中赠送成长值和赠送优惠券不能同时为空");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
memberConfig.setConfigList(JSONObject.toJSONString(memberDTO.getConfigList()));
|
|
|
|
|
memberConfig.setConditionList(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (memberConfig.getConfigList() != null && !memberConfig.getConfigList().isEmpty() && memberConfig.getOpenAmount() != null) {
|
|
|
|
|
throw new CzgException("会员开通方式为单选条件");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (memberDTO.getConditionList() != null && !memberDTO.getConditionList().isEmpty()) {
|
|
|
|
|
// ArrayList<String> conditionList = CollUtil.newArrayList(conditionMap);
|
|
|
|
|
memberDTO.getConditionList().forEach(item -> {
|
|
|
|
|
if (!conditionMap.contains(item.getCode())) {
|
|
|
|
|
throw new CzgException("条件列表中code值错误");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
// conditionList.forEach(item -> {
|
|
|
|
|
// memberDTO.getConditionList().add(new MemberDTO.condition(item, null));
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
|
|
memberConfig.setConditionList(JSONObject.toJSONString(memberDTO.getConditionList()));
|
|
|
|
|
memberConfig.setOpenAmount(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (memberDTO.getMemberPriceShopIdList() != null && !memberDTO.getMemberPriceShopIdList().isEmpty()) {
|
|
|
|
|
memberConfig.setMemberPriceShopIdList(JSONObject.toJSONString(memberDTO.getMemberPriceShopIdList()));
|
|
|
|
|
}
|
|
|
|
|
return updateById(memberConfig, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void checkLevelDto(Long shopId, MemberLevelDTO levelDTO) {
|
|
|
|
|
long count = levelConfigService.count(new QueryWrapper().eq(MemberLevelConfig::getShopId, shopId).eq(MemberLevelConfig::getName, levelDTO.getName()).ne(MemberLevelConfig::getId, levelDTO.getId()));
|
|
|
|
|
if (count > 0) {
|
|
|
|
|
throw new CzgException("会员等级名称已存在");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MemberLevelConfig lastConfig = levelConfigService.getOne(new QueryWrapper().eq(MemberLevelConfig::getShopId, shopId)
|
|
|
|
|
.lt(MemberLevelConfig::getId, levelDTO.getId())
|
|
|
|
|
.limit(1).orderBy(MemberLevelConfig::getId, false).ne(MemberLevelConfig::getId, levelDTO.getId()));
|
|
|
|
|
if (lastConfig == null && levelDTO.getExperienceValue() > 0) {
|
|
|
|
|
throw new CzgException("1级时本字段必须为0");
|
|
|
|
|
} else if (lastConfig != null && levelDTO.getExperienceValue() <= lastConfig.getExperienceValue()) {
|
|
|
|
|
throw new CzgException("会员等级经验值必须大于上一等级经验值");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (levelDTO.getIsCycleReward() == 1) {
|
|
|
|
|
if (levelDTO.getCycleRewardPoints() == null && (levelDTO.getCycleRewardCouponList() == null || levelDTO.getCycleRewardCouponList().isEmpty())) {
|
|
|
|
|
throw new CzgException("周期奖励成长值和优惠券不能同时为空");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ValidatorUtil.validateEntity(levelDTO, MemberLevelCycleRewardGroup.class);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Boolean addLevel(Long shopId, MemberLevelDTO levelDTO) {
|
|
|
|
|
checkLevelDto(shopId, levelDTO);
|
|
|
|
|
|
|
|
|
|
MemberLevelConfig levelConfig = BeanUtil.copyProperties(levelDTO, MemberLevelConfig.class);
|
|
|
|
|
levelConfig.setShopId(shopId);
|
|
|
|
|
if (levelDTO.getCycleRewardCouponList() != null && !levelDTO.getCycleRewardCouponList().isEmpty()) {
|
|
|
|
|
levelConfig.setCycleRewardCouponList(JSONObject.toJSONString(levelDTO.getCycleRewardCouponList()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return levelConfigService.save(levelConfig);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Boolean editLevel(Long shopId, MemberLevelDTO levelDTO) {
|
|
|
|
|
MemberLevelConfig levelConfig = levelConfigService.getOne(new QueryWrapper().eq(MemberLevelConfig::getId, levelDTO.getId()).eq(MemberLevelConfig::getShopId, shopId));
|
|
|
|
|
Optional.ofNullable(levelConfig).orElseThrow(() -> new CzgException("会员等级不存在"));
|
|
|
|
|
checkLevelDto(shopId, levelDTO);
|
|
|
|
|
BeanUtil.copyProperties(levelDTO, levelConfig);
|
|
|
|
|
|
|
|
|
|
if (levelDTO.getCycleRewardCouponList() != null && !levelDTO.getCycleRewardCouponList().isEmpty()) {
|
|
|
|
|
levelConfig.setCycleRewardCouponList(JSONObject.toJSONString(levelDTO.getCycleRewardCouponList()));
|
|
|
|
|
} else {
|
|
|
|
|
levelConfig.setCycleRewardCouponList(null);
|
|
|
|
|
}
|
|
|
|
|
return levelConfigService.updateById(levelConfig, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public ArrayList<MemberLevelVO> listLevel(Long shopId) {
|
|
|
|
|
ArrayList<MemberLevelVO> memberLevelVOS = new ArrayList<>();
|
|
|
|
|
levelConfigService.list(new QueryWrapper().eq(MemberLevelConfig::getShopId, shopId)).forEach(item -> {
|
|
|
|
|
MemberLevelVO memberLevelVO = BeanUtil.copyProperties(item, MemberLevelVO.class, "cycleRewardCouponList");
|
|
|
|
|
if (StrUtil.isNotBlank(item.getCycleRewardCouponList())) {
|
|
|
|
|
memberLevelVO.setCycleRewardCouponList(JSONArray.parseArray(item.getCycleRewardCouponList()).toList(ShopCoupon.class));
|
|
|
|
|
}
|
|
|
|
|
memberLevelVOS.add(memberLevelVO);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return memberLevelVOS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean deliver(Long shopId, Long userId, BigDecimal money, boolean isCost) {
|
|
|
|
|
ShopUser shopUser = shopUserService.getOne(new QueryWrapper().eq(ShopUser::getShopId, shopId).eq(ShopUser::getUserId, userId));
|
|
|
|
|
if (shopUser == null || shopUser.getIsVip() == 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
MemberConfigVO memberConfig = detail(shopId);
|
|
|
|
|
if (memberConfig == null) {
|
|
|
|
|
log.warn("会员配置不存在, 店铺id: {}", shopId);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (shopUser.getExperience() == null) {
|
|
|
|
|
shopUser.setExperience(0L);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 消费经验
|
|
|
|
|
if (memberConfig.getCostReward() != null && isCost) {
|
|
|
|
|
shopUser.setExperience(money.longValue() * memberConfig.getCostReward() + shopUser.getExperience());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 充值经验
|
|
|
|
|
if (memberConfig.getRechargeReward() != null && !isCost) {
|
|
|
|
|
shopUser.setExperience(money.longValue() * memberConfig.getRechargeReward() + shopUser.getExperience());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MemberLevelConfig levelConfig = levelConfigService.getById(shopUser.getMemberLevelId());
|
|
|
|
|
if (levelConfig == null) {
|
|
|
|
|
log.warn("会员等级配置不存在, 店铺id: {}, 等级id: {}", shopId, shopUser.getMemberLevelId());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 消费送积分
|
|
|
|
|
if (isCost && levelConfig.getCostRewardPoints() != null) {
|
|
|
|
|
int points = (int) (money.floatValue() / levelConfig.getCostRewardPoints());
|
|
|
|
|
memberPointsService.addPoints(shopUser.getId(), points, "会员消费送积分", null);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean joinMember(Long shopId, Long userId) {
|
|
|
|
|
MemberConfigVO memberConfigVO = detail(shopId);
|
|
|
|
|
ShopUser shopUser = shopUserService.getOne(new QueryWrapper().eq(ShopUser::getShopId, shopId).eq(ShopUser::getUserId, userId));
|
|
|
|
|
if (shopUser == null) {
|
|
|
|
|
log.warn("用户不存在, 店铺id: {}, 用户id: {}", shopId, userId);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
boolean canOpen = false;
|
|
|
|
|
// 条件开通
|
|
|
|
|
if (memberConfigVO.getOpenAmount() == null) {
|
|
|
|
|
for (MemberConfigDTO.condition item : memberConfigVO.getConditionList()) {
|
|
|
|
|
canOpen = switch (item.getCode()) {
|
|
|
|
|
case "BIND_PHONE" -> StrUtil.isNotBlank(shopUser.getPhone());
|
|
|
|
|
case "ORDER" ->
|
|
|
|
|
orderInfoService.count(new QueryWrapper().eq(OrderInfo::getShopId, shopId).eq(OrderInfo::getUserId, userId)
|
|
|
|
|
.notIn(OrderInfo::getStatus, OrderStatusEnums.UNPAID.getCode(), OrderStatusEnums.CANCELLED.getCode())) > Integer.parseInt(item.getValue());
|
|
|
|
|
case "COST_AMOUNT" ->
|
|
|
|
|
orderInfoService.list(new QueryWrapper().eq(OrderInfo::getShopId, shopId).eq(OrderInfo::getUserId, userId)
|
|
|
|
|
.notIn(OrderInfo::getStatus, OrderStatusEnums.UNPAID.getCode(), OrderStatusEnums.CANCELLED.getCode()))
|
|
|
|
|
.stream().map(OrderInfo::getPayAmount).reduce(BigDecimal.ZERO, BigDecimal::add).compareTo(new BigDecimal(item.getValue())) > 0;
|
|
|
|
|
case "RECHARGE_AMOUNT" ->
|
|
|
|
|
orderPaymentService.list(new QueryWrapper().eq(OrderPayment::getShopId, shopId)
|
|
|
|
|
.eq(OrderPayment::getSourceId, shopUser.getId()).isNotNull(OrderPayment::getTradeNumber))
|
|
|
|
|
.stream().map(OrderPayment::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add).compareTo(new BigDecimal(item.getValue())) > 0;
|
|
|
|
|
default -> throw new CzgException("会员开通条件类型错误");
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 购买开通
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (canOpen) {
|
|
|
|
|
MemberLevelConfig levelConfig = levelConfigService.getOne(new QueryWrapper().eq(MemberLevelConfig::getShopId, shopId).orderBy(MemberLevelConfig::getExperienceValue, true).limit(1));
|
|
|
|
|
shopUser.setMemberLevelId(levelConfig.getId());
|
|
|
|
|
shopUser.setIsVip(1);
|
|
|
|
|
shopUserService.updateById(shopUser);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|