会员积分代码提交

This commit is contained in:
Tankaikai
2025-02-25 19:02:51 +08:00
parent 978f7f1bdb
commit eb748897cf
43 changed files with 2480 additions and 23 deletions

View File

@@ -0,0 +1,16 @@
package com.czg.service.account.mapper;
import com.czg.account.entity.MemberPointsLog;
import com.mybatisflex.core.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 会员积分变动记录
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@Mapper
public interface MemberPointsLogMapper extends BaseMapper<MemberPointsLog> {
}

View File

@@ -0,0 +1,16 @@
package com.czg.service.account.mapper;
import com.czg.account.entity.MemberPoints;
import com.mybatisflex.core.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 会员积分
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@Mapper
public interface MemberPointsMapper extends BaseMapper<MemberPoints> {
}

View File

@@ -1,7 +1,8 @@
package com.czg.service.account.mapper;
import com.mybatisflex.core.BaseMapper;
import com.czg.account.entity.PointsBasicSetting;
import com.mybatisflex.core.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 积分基本设置 映射层。
@@ -9,6 +10,7 @@ import com.czg.account.entity.PointsBasicSetting;
* @author ww
* @since 2025-02-20
*/
@Mapper
public interface PointsBasicSettingMapper extends BaseMapper<PointsBasicSetting> {
}

View File

@@ -0,0 +1,16 @@
package com.czg.service.account.mapper;
import com.czg.account.entity.PointsExchangeRecord;
import com.mybatisflex.core.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 积分兑换记录
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@Mapper
public interface PointsExchangeRecordMapper extends BaseMapper<PointsExchangeRecord> {
}

View File

@@ -0,0 +1,16 @@
package com.czg.service.account.mapper;
import com.czg.account.entity.PointsGoodsSetting;
import com.mybatisflex.core.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 积分商品设置
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@Mapper
public interface PointsGoodsSettingMapper extends BaseMapper<PointsGoodsSetting> {
}

View File

@@ -0,0 +1,76 @@
package com.czg.service.account.service.impl;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.czg.account.dto.points.MemberPointsLogDTO;
import com.czg.account.entity.MemberPointsLog;
import com.czg.account.entity.ShopUser;
import com.czg.account.service.MemberPointsLogService;
import com.czg.sa.StpKit;
import com.czg.service.account.mapper.MemberPointsLogMapper;
import com.czg.utils.PageUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import static com.czg.account.entity.table.MemberPointsLogTableDef.MEMBER_POINTS_LOG;
import static com.czg.account.entity.table.MemberPointsTableDef.MEMBER_POINTS;
import static com.czg.account.entity.table.ShopUserTableDef.SHOP_USER;
/**
* 会员积分变动记录
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@Service
public class MemberPointsLogServiceImpl extends ServiceImpl<MemberPointsLogMapper, MemberPointsLog> implements MemberPointsLogService {
private QueryWrapper buildQueryWrapper(MemberPointsLogDTO param) {
QueryWrapper queryWrapper = PageUtil.buildSortQueryWrapper();
if (ObjUtil.isNotNull(param.getUserId())) {
queryWrapper.eq(MemberPointsLog::getUserId, param.getUserId());
}
if (StrUtil.isNotBlank(param.getNickName())) {
queryWrapper.like(ShopUser::getNickName, param.getNickName());
}
if (StrUtil.isNotBlank(param.getPhone())) {
queryWrapper.like(ShopUser::getPhone, param.getPhone());
}
if (StrUtil.isNotBlank(param.getContent())) {
queryWrapper.like(MemberPointsLog::getContent, param.getContent());
}
if (StrUtil.isNotBlank(param.getFloatType())) {
queryWrapper.eq(MemberPointsLog::getFloatType, param.getFloatType());
}
if (StrUtil.isNotBlank(param.getOrderNo())) {
queryWrapper.eq(MemberPointsLog::getOrderNo, param.getOrderNo());
}
if (StrUtil.isNotEmpty(param.getBeginDate())) {
queryWrapper.ge(MemberPointsLog::getCreateTime, param.getBeginDate() + " 00:00:00");
}
if (StrUtil.isNotEmpty(param.getEndDate())) {
queryWrapper.le(MemberPointsLog::getCreateTime, param.getEndDate() + " 23:59:59");
}
Long shopId = StpKit.USER.getShopId(0L);
queryWrapper.eq(MemberPointsLog::getShopId, shopId);
queryWrapper.orderBy(MemberPointsLog::getId, false);
return queryWrapper;
}
private QueryWrapper buildFullQueryWrapper(MemberPointsLogDTO param) {
QueryWrapper queryWrapper = buildQueryWrapper(param);
queryWrapper.select(MEMBER_POINTS.DEFAULT_COLUMNS)
.select(SHOP_USER.NICK_NAME.as(MemberPointsLogDTO::getNickName), SHOP_USER.HEAD_IMG.as(MemberPointsLogDTO::getHeadImg), SHOP_USER.PHONE.as(MemberPointsLogDTO::getPhone))
.from(MEMBER_POINTS_LOG)
.leftJoin(SHOP_USER).on(SHOP_USER.ID.eq(MEMBER_POINTS.USER_ID));
return queryWrapper;
}
@Override
public Page<MemberPointsLogDTO> getMemberPointsLogPage(MemberPointsLogDTO param) {
QueryWrapper queryWrapper = buildFullQueryWrapper(param);
return super.pageAs(PageUtil.buildPage(), queryWrapper, MemberPointsLogDTO.class);
}
}

View File

@@ -0,0 +1,287 @@
package com.czg.service.account.service.impl;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.czg.account.dto.points.OrderDeductionPointsDTO;
import com.czg.account.entity.MemberPoints;
import com.czg.account.entity.MemberPointsLog;
import com.czg.account.entity.PointsBasicSetting;
import com.czg.account.enums.PointUserGroupEnum;
import com.czg.account.param.MemberPointsParam;
import com.czg.account.service.MemberPointsService;
import com.czg.enums.YesNoEnum;
import com.czg.exception.CzgException;
import com.czg.order.entity.OrderInfo;
import com.czg.order.service.OrderInfoService;
import com.czg.sa.StpKit;
import com.czg.service.account.mapper.MemberPointsLogMapper;
import com.czg.service.account.mapper.MemberPointsMapper;
import com.czg.service.account.mapper.PointsBasicSettingMapper;
import com.czg.utils.PageUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
/**
* 会员积分变动记录
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@Service
public class MemberPointsServiceImpl extends ServiceImpl<MemberPointsMapper, MemberPoints> implements MemberPointsService {
@Resource
private PointsBasicSettingMapper pointsBasicSettingMapper;
@Resource
private MemberPointsLogMapper memberPointsLogMapper;
@DubboReference
private OrderInfoService orderInfoService;
private QueryWrapper buildQueryWrapper(MemberPointsParam param) {
QueryWrapper queryWrapper = PageUtil.buildSortQueryWrapper();
if (StrUtil.isNotBlank(param.getNickName())) {
queryWrapper.like(MemberPoints::getNickName, param.getNickName());
}
if (StrUtil.isNotBlank(param.getPhone())) {
queryWrapper.like(MemberPoints::getPhone, param.getPhone());
}
Long shopId = StpKit.USER.getShopId(0L);
queryWrapper.eq(MemberPoints::getShopId, shopId);
queryWrapper.orderBy(MemberPoints::getId, false);
return queryWrapper;
}
@Override
public Page<MemberPoints> getMemberPointsPage(MemberPointsParam param) {
QueryWrapper queryWrapper = buildQueryWrapper(param);
return super.page(PageUtil.buildPage(), queryWrapper);
}
@Override
public MemberPoints initMemberPoints(Long userId) {
MemberPoints entity = super.getOne(query().eq(MemberPoints::getUserId, userId));
if (entity == null) {
throw new CzgException("会员信息不存在");
}
if (entity.getAccountPoints() == null) {
entity.setAccountPoints(0);
super.updateById(entity);
}
return entity;
}
@Override
public MemberPoints getMemberPoints(Long userId) {
return initMemberPoints(userId);
}
@Override
public OrderDeductionPointsDTO getMemberUsablePoints(Long userId, BigDecimal orderAmount) {
MemberPoints entity = initMemberPoints(userId);
Long shopId = entity.getShopId();
Integer accountPoints = entity.getAccountPoints();
OrderDeductionPointsDTO dto = new OrderDeductionPointsDTO();
dto.setAccountPoints(accountPoints);
dto.setUsable(false);
dto.setMaxDeductionAmount(BigDecimal.ZERO);
dto.setMinDeductionAmount(BigDecimal.ZERO);
PointsBasicSetting basic = pointsBasicSettingMapper.selectOneById(shopId);
if (basic == null) {
dto.setUnusableReason("商家未启用积分抵扣功能");
return dto;
}
if (basic.getEnableDeduction() == 0) {
dto.setUnusableReason("商家未启用积分抵扣功能");
return dto;
}
if (PointUserGroupEnum.VIP.value().equals(basic.getDeductionGroup()) && ObjectUtil.defaultIfNull(entity.getVip(), YesNoEnum.NO.value()) != YesNoEnum.YES.value()) {
dto.setUnusableReason("仅VIP用户可用");
return dto;
}
dto.setMinPaymentAmount(basic.getMinPaymentAmount());
dto.setOrderAmount(orderAmount);
if (NumberUtil.isLess(orderAmount, basic.getMinPaymentAmount())) {
dto.setUnusableReason(StrUtil.format("实付金额不足¥{}元,无法进行抵扣", basic.getMinPaymentAmount()));
return dto;
}
dto.setEquivalentPoints(basic.getEquivalentPoints());
// 最低抵扣积分 = 1元等值积分 = 1.0元
dto.setMinDeductionPoints(basic.getEquivalentPoints());
// 计算抵扣门槛=?元
dto.setMinDeductionAmount(BigDecimal.ONE);
if (accountPoints == 0 || accountPoints < dto.getMinDeductionPoints()) {
dto.setUnusableReason("积分不足或小于最低使用门槛" + dto.getMinDeductionPoints());
return dto;
}
// 下单抵扣积分比例 1元=?积分
Integer equivalentPoints = basic.getEquivalentPoints();
// 计算账户积分=?元
BigDecimal accountYuan = NumberUtil.div(accountPoints, equivalentPoints);
// 下单最高抵扣比例
BigDecimal maxDeductionRatio = basic.getMaxDeductionRatio();
// 计算订单最多可以抵扣多少元,向下取整
BigDecimal orderYuan = NumberUtil.roundDown(NumberUtil.mul(orderAmount, NumberUtil.div(maxDeductionRatio, new BigDecimal("100"))), 0);
// 积分余额足够
if (NumberUtil.isGreaterOrEqual(accountYuan, orderYuan)) {
dto.setMaxDeductionAmount(orderYuan);
} else {
dto.setMaxDeductionAmount(NumberUtil.roundDown(accountYuan, 0));
}
if (NumberUtil.isLess(dto.getMaxDeductionAmount(), BigDecimal.ONE)) {
dto.setUnusableReason("积分不足1元无法进行抵扣");
return dto;
}
dto.setUsable(true);
// 计算最多可抵扣的积分
BigDecimal mul = NumberUtil.mul(dto.getMaxDeductionAmount(), equivalentPoints);
BigDecimal round = NumberUtil.round(mul, 0, RoundingMode.CEILING);
dto.setMaxUsablePoints(round.intValue());
return dto;
}
@Override
public int calcUsedPoints(Long userId, BigDecimal orderAmount, BigDecimal deductionAmount) {
OrderDeductionPointsDTO core = getMemberUsablePoints(userId, orderAmount);
if (!core.getUsable()) {
throw new CzgException(core.getUnusableReason());
}
if (NumberUtil.isGreater(deductionAmount, core.getMaxDeductionAmount())) {
throw new CzgException(StrUtil.format("抵扣金额不能超过最大抵扣金额{}元", core.getMaxDeductionAmount()));
}
if (NumberUtil.isGreater(deductionAmount, orderAmount)) {
throw new CzgException(StrUtil.format("抵扣金额不能超过订单金额{}元", orderAmount));
}
// 计算可抵扣的积分
BigDecimal mul = NumberUtil.mul(deductionAmount, core.getEquivalentPoints());
BigDecimal round = NumberUtil.round(mul, 0, RoundingMode.CEILING);
return round.intValue();
}
@Override
public BigDecimal calcDeductionAmount(Long userId, BigDecimal orderAmount, int points) {
OrderDeductionPointsDTO core = getMemberUsablePoints(userId, orderAmount);
if (!core.getUsable()) {
throw new CzgException(core.getUnusableReason());
}
if (points < core.getMinDeductionPoints()) {
throw new CzgException(StrUtil.format("使用积分不能低于使用门槛(每次最少使用{}积分)", core.getMinDeductionPoints()));
}
if (points > core.getMaxUsablePoints()) {
throw new CzgException(StrUtil.format("使用积分不能超过最大使用限制{}", core.getMaxUsablePoints()));
}
BigDecimal money = NumberUtil.mul(points, NumberUtil.div(BigDecimal.ONE, core.getEquivalentPoints()));
BigDecimal maxDeductionAmount = NumberUtil.roundDown(money, 0);
if (NumberUtil.isGreater(maxDeductionAmount, core.getMaxDeductionAmount())) {
return core.getMaxDeductionAmount();
}
return maxDeductionAmount;
}
@Override
public boolean deductPoints(Long userId, int points, String content, Long orderId) {
MemberPoints entity = initMemberPoints(userId);
// 扣除账户积分
entity.setAccountPoints(entity.getAccountPoints() - points);
entity.setLastPointsChangeTime(LocalDateTime.now());
entity.setLastFloatPoints(-points);
// 记录积分变动记录
MemberPointsLog log = new MemberPointsLog();
log.setShopId(entity.getShopId());
log.setUserId(entity.getUserId());
log.setContent(content);
log.setFloatType("subtract");
log.setFloatPoints(-points);
// 有关联订单的需要回置订单表的相关积分使用字段
if (orderId != null) {
OrderInfo orderInfo = orderInfoService.getById(orderId);
if (orderInfo != null) {
log.setOrderNo(orderInfo.getOrderNo());
// TODO 是否需要回执“使用的积分数量(points_num)”和“积分抵扣金额(points_discount_amount)”,目前不清楚是创建订单的时候置入还是支付完成后回调时置入需要商议后决定
}
}
super.updateById(entity);
memberPointsLogMapper.insert(log);
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean addPoints(Long memberId, int points, String content, Long orderId) {
MemberPoints entity = initMemberPoints(memberId);
// 增加账户积分
entity.setAccountPoints(entity.getAccountPoints() + points);
entity.setLastPointsChangeTime(LocalDateTime.now());
entity.setLastFloatPoints(points);
// 记录积分变动记录
MemberPointsLog log = new MemberPointsLog();
log.setShopId(entity.getShopId());
log.setUserId(entity.getUserId());
log.setContent(content);
log.setFloatType("add");
log.setFloatPoints(points);
// 有关联订单的需要回置订单表的相关积分使用字段
if (orderId != null) {
OrderInfo orderInfo = orderInfoService.getById(orderId);
if (orderInfo != null) {
log.setOrderNo(orderInfo.getOrderNo());
}
}
super.updateById(entity);
memberPointsLogMapper.insert(log);
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void consumeAwardPoints(Long memberId, Long orderId) {
OrderInfo orderInfo = orderInfoService.getById(orderId);
if (orderInfo == null) {
throw new CzgException("订单不存在");
}
BigDecimal payAmount = orderInfo.getPayAmount();
if (NumberUtil.isLessOrEqual(payAmount, BigDecimal.ZERO)) {
return;
}
PointsBasicSetting basicSetting = pointsBasicSettingMapper.selectOneById(orderInfo.getShopId());
if (basicSetting == null) {
return;
}
Integer enableRewards = basicSetting.getEnableRewards();
if (enableRewards == 0) {
return;
}
String rewardsGroup = basicSetting.getRewardsGroup();
MemberPoints entity;
try {
entity = initMemberPoints(memberId);
} catch (Exception e) {
return;
}
Integer vip = entity.getVip();
if (PointUserGroupEnum.VIP.value().equals(rewardsGroup) && ObjectUtil.defaultIfNull(vip, 0) != 1) {
return;
}
BigDecimal consumeAmount = basicSetting.getConsumeAmount();
if (consumeAmount == null) {
return;
}
if (NumberUtil.isLessOrEqual(consumeAmount, BigDecimal.ZERO)) {
return;
}
BigDecimal awardPoints = NumberUtil.roundDown(NumberUtil.div(payAmount, consumeAmount), 0);
addPoints(memberId, awardPoints.intValue(), StrUtil.format("消费¥{}送{}积分", payAmount, awardPoints.intValue()), orderId);
}
}

View File

@@ -5,6 +5,7 @@ import com.czg.account.service.PointsBasicSettingService;
import com.czg.service.account.mapper.PointsBasicSettingMapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.stereotype.Service;
/**
* 积分基本设置 服务层实现。
@@ -12,6 +13,7 @@ import org.apache.dubbo.config.annotation.DubboService;
* @author ww
* @since 2025-02-20
*/
@Service
@DubboService
public class PointsBasicSettingServiceImpl extends ServiceImpl<PointsBasicSettingMapper, PointsBasicSetting> implements PointsBasicSettingService{

View File

@@ -0,0 +1,234 @@
package com.czg.service.account.service.impl;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.czg.account.dto.points.PointsExchangeRecordDTO;
import com.czg.account.entity.*;
import com.czg.account.enums.PointsExchangeStatusEnum;
import com.czg.account.param.PointsExchangeCfParam;
import com.czg.account.param.PointsExchangeRecordParam;
import com.czg.account.service.PointsExchangeRecordService;
import com.czg.account.vo.PointsExchangeSummaryVo;
import com.czg.exception.CzgException;
import com.czg.sa.StpKit;
import com.czg.service.account.mapper.*;
import com.czg.utils.PageUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.update.UpdateChain;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
/**
* 积分兑换记录
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@AllArgsConstructor
@Service
public class PointsExchangeRecordServiceImpl extends ServiceImpl<PointsExchangeRecordMapper, PointsExchangeRecord> implements PointsExchangeRecordService {
private final PointsGoodsSettingMapper pointsGoodsSettingMapper;
private final MemberPointsMapper memberPointsMapper;
private final MemberPointsLogMapper memberPointsLogMapper;
private final ShopInfoMapper shopInfoMapper;
private final ShopMerchantMapper shopMerchantMapper;
private QueryWrapper buildQueryWrapper(PointsExchangeRecordParam param) {
QueryWrapper queryWrapper = PageUtil.buildSortQueryWrapper();
if (StrUtil.isNotEmpty(param.getPickupMethod())) {
queryWrapper.eq(PointsExchangeRecord::getPickupMethod, param.getPickupMethod());
}
if (StrUtil.isNotEmpty(param.getStatus())) {
queryWrapper.eq(PointsExchangeRecord::getStatus, param.getStatus());
}
if (ObjUtil.isNotNull(param.getUserId())) {
queryWrapper.eq(PointsExchangeRecord::getUserId, param.getUserId());
}
if (StrUtil.isNotEmpty(param.getKeywords())) {
queryWrapper.and(q -> {
q.like(PointsExchangeRecord::getOrderNo, param.getKeywords()).or(r -> {
r.like(PointsExchangeRecord::getCouponCode, param.getKeywords());
});
});
}
if (StrUtil.isNotEmpty(param.getBeginDate())) {
queryWrapper.ge(PointsExchangeRecord::getCreateTime, param.getBeginDate() + " 00:00:00");
}
if (StrUtil.isNotEmpty(param.getEndDate())) {
queryWrapper.le(PointsExchangeRecord::getCreateTime, param.getEndDate() + " 23:59:59");
}
Long shopId = StpKit.USER.getShopId(0L);
queryWrapper.eq(PointsExchangeRecord::getShopId, shopId);
queryWrapper.orderBy(PointsExchangeRecord::getId, false);
return queryWrapper;
}
@Override
public Page<PointsExchangeRecordDTO> getPointsExchangeRecordPage(PointsExchangeRecordParam param) {
QueryWrapper queryWrapper = buildQueryWrapper(param);
return super.pageAs(PageUtil.buildPage(), queryWrapper, PointsExchangeRecordDTO.class);
}
@Override
public PointsExchangeRecordDTO getPointsExchangeRecordById(Long id) {
Long shopId = StpKit.USER.getShopId(0L);
return super.mapper.selectOneByQueryAs(query().eq(PointsExchangeRecord::getShopId, shopId).eq(PointsExchangeRecord::getId, id), PointsExchangeRecordDTO.class);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void checkout(String couponCode) {
if (StrUtil.isBlank(couponCode)) {
throw new CzgException("兑换券券码不能为空");
}
PointsExchangeRecord entity = super.getOne(query().eq(PointsExchangeRecord::getCouponCode, couponCode));
if (entity == null) {
throw new CzgException("兑换券券码无效");
}
entity.setStatus(PointsExchangeStatusEnum.DONE.value());
super.updateById(entity);
PointsGoodsSetting goods = pointsGoodsSettingMapper.selectOneById(entity.getPointsGoodsId());
if (goods == null) {
throw new CzgException("积分商品不存在");
}
// 更新累计兑换数量
goods.setTotalExchangeCount(goods.getTotalExchangeCount() + 1);
pointsGoodsSettingMapper.update(goods);
}
@Override
public void cancel(PointsExchangeCfParam param) {
if (param.getId() == null) {
throw new CzgException("订单ID不能为空");
}
PointsExchangeRecord entity = super.getById(param.getId());
if (entity == null) {
throw new CzgException("订单不存在");
}
if (!PointsExchangeStatusEnum.UNPAID.value().equals(entity.getStatus())) {
throw new CzgException("当前订单状态不支持取消");
}
entity.setStatus(PointsExchangeStatusEnum.CANCEL.value());
entity.setCancelOrRefundReason(param.getCancelOrRefundReason());
entity.setCancelOrRefundTime(LocalDateTime.now());
super.updateById(entity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void refund(PointsExchangeCfParam param) {
if (param.getId() == null) {
throw new CzgException("订单ID不能为空");
}
PointsExchangeRecord entity = super.getById(param.getId());
if (entity == null) {
throw new CzgException("订单不存在");
}
if (!PointsExchangeStatusEnum.WAITING.value().equals(entity.getStatus())) {
throw new CzgException("当前订单状态不支持退款");
}
// 先退积分
entity.setStatus(PointsExchangeStatusEnum.CANCEL.value());
entity.setCancelOrRefundReason(param.getCancelOrRefundReason());
entity.setCancelOrRefundTime(LocalDateTime.now());
super.updateById(entity);
MemberPoints memberPoints = memberPointsMapper.selectOneByQuery(query().eq(MemberPoints::getUserId, entity.getUserId()));
if (memberPoints == null) {
throw new CzgException("会员信息不存在");
}
memberPoints.setAccountPoints(memberPoints.getAccountPoints() + entity.getSpendPoints());
memberPoints.setLastPointsChangeTime(LocalDateTime.now());
memberPoints.setLastFloatPoints(entity.getSpendPoints());
memberPointsMapper.update(memberPoints);
// 回滚库存
PointsGoodsSetting goods = pointsGoodsSettingMapper.selectOneById(entity.getPointsGoodsId());
if (goods == null) {
throw new CzgException("积分商品不存在");
}
goods.setQuantity(goods.getQuantity() + 1);
pointsGoodsSettingMapper.update(goods);
// 记录积分浮动流水
MemberPointsLog log = new MemberPointsLog();
log.setShopId(entity.getShopId());
log.setUserId(entity.getUserId());
log.setContent(StrUtil.format("(退单)兑换商品:{} * {}", entity.getPointsGoodsName(), "1"));
log.setFloatType("add");
log.setFloatPoints(+entity.getSpendPoints());
log.setCreateTime(LocalDateTime.now());
memberPointsLogMapper.insert(log);
// 如果额外付款了则需要退款
if (NumberUtil.equals(entity.getExtraPaymentAmount(), BigDecimal.ZERO)) {
return;
}
// 需要额外支付
ShopInfo shopInfo = shopInfoMapper.selectOneById(entity.getShopId());
if (shopInfo == null) {
throw new CzgException("店铺信息不存在");
}
ShopMerchant shopMerchant = shopMerchantMapper.selectOneById(shopInfo.getId());
if (shopMerchant == null) {
throw new CzgException("支付通道不存在");
}
if ("alipay".equalsIgnoreCase(entity.getPayType()) && StrUtil.isBlank(shopMerchant.getAlipaySmallAppid())) {
throw new CzgException("店铺未配置支付宝小程序appId");
}
// 准备退款参数
// 应用ID
// TODO 对接退款接口
/* String appId = thirdApply.getAppId();
String appToken = thirdApply.getAppToken();
// 交易金额 单位分 100 表示1元
long amount = NumberUtil.mul(record.getExtraPaymentAmount(), new BigDecimal("100")).longValue();
// 退款订单号
String mchRefundNo = entity.getOrderNo();
PublicResp<OrderReturnResp> publicResp;
try {
publicResp = thirdPayService.returnOrder(thirdUrl, appId, mchRefundNo, entity.getPayOrderId(), null, entity.getCancelOrRefundReason(), amount, null, null, appToken);
} catch (Exception e) {
super.log.error("发起退款失败:", e);
throw new CzgException(StrUtil.format("发起退款失败:{}", e.getMessage()));
}
if (publicResp == null) {
throw new CzgException("发起退款失败:无响应");
}
if (!"000000".equals(publicResp.getCode())) {
throw new CzgException(publicResp.getMsg());
}
OrderReturnResp returnResp = publicResp.getObjData();
if (!"SUCCESS".equals(returnResp.getState())) {
throw new CzgException(StrUtil.format("退款失败原因:{}", returnResp.getState()));
}*/
}
@Override
public PointsExchangeSummaryVo total(PointsExchangeRecordParam param) {
QueryWrapper queryWrapper = buildQueryWrapper(param);
queryWrapper.select(new QueryColumn("count(*) as count"), new QueryColumn("sum(extra_payment_amount) as totalAmount"));
PointsExchangeRecord summary = super.getOne(queryWrapper);
PointsExchangeSummaryVo result = new PointsExchangeSummaryVo();
result.setCount(summary.getCount());
result.setTotalAmount(NumberUtil.nullToZero(summary.getTotalAmount()));
return result;
}
@Override
public void authCancel() {
UpdateChain.of(PointsExchangeRecord.class)
.set(PointsExchangeRecord::getStatus, PointsExchangeStatusEnum.CANCEL.value())
.set(PointsExchangeRecord::getCancelOrRefundTime, new Date())
.set(PointsExchangeRecord::getCancelOrRefundReason, "超时未支付,系统自动取消订单")
.eq(PointsExchangeRecord::getStatus, PointsExchangeStatusEnum.UNPAID.value())
.ge("TIMESTAMPDIFF(MINUTE, create_time, NOW())", 5);
}
}

View File

@@ -0,0 +1,50 @@
package com.czg.service.account.service.impl;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.czg.account.dto.points.PointsGoodsSettingDTO;
import com.czg.account.entity.PointsGoodsSetting;
import com.czg.account.service.PointsGoodsSettingService;
import com.czg.enums.DeleteEnum;
import com.czg.sa.StpKit;
import com.czg.service.account.mapper.PointsGoodsSettingMapper;
import com.czg.utils.PageUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* 积分商品设置
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@Service
public class PointsGoodsSettingServiceImpl extends ServiceImpl<PointsGoodsSettingMapper, PointsGoodsSetting> implements PointsGoodsSettingService {
private QueryWrapper buildQueryWrapper(PointsGoodsSettingDTO param) {
QueryWrapper queryWrapper = PageUtil.buildSortQueryWrapper();
if (StrUtil.isNotEmpty(param.getGoodsName())) {
queryWrapper.like(PointsGoodsSetting::getGoodsName, param.getGoodsName());
}
if (StrUtil.isNotEmpty(param.getGoodsCategory())) {
queryWrapper.eq(PointsGoodsSetting::getGoodsCategory, param.getGoodsCategory());
}
if (ObjUtil.isNotNull(param.getStatus())) {
queryWrapper.eq(PointsGoodsSetting::getStatus, param.getStatus());
}
Long shopId = StpKit.USER.getShopId(0L);
queryWrapper.eq(PointsGoodsSetting::getDelFlag, DeleteEnum.NORMAL.value());
queryWrapper.eq(PointsGoodsSetting::getShopId, shopId);
queryWrapper.orderBy(PointsGoodsSetting::getSort, false);
queryWrapper.orderBy(PointsGoodsSetting::getId, false);
return queryWrapper;
}
@Override
public Page<PointsGoodsSettingDTO> getPointsGoodsSettingPage(PointsGoodsSettingDTO param) {
QueryWrapper queryWrapper = buildQueryWrapper(param);
return super.pageAs(PageUtil.buildPage(), queryWrapper, PointsGoodsSettingDTO.class);
}
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.account.mapper.MemberPointsLogMapper">
</mapper>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.account.mapper.PointsBasicSettingMapper">
</mapper>
</mapper>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.account.mapper.PointsExchangeRecordMapper">
</mapper>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.account.mapper.PointsGoodsSettingMapper">
</mapper>