This commit is contained in:
2025-12-09 18:41:30 +08:00
parent 82efd1886f
commit 76b93cf8b2
93 changed files with 2131 additions and 3112 deletions

View File

@@ -1,16 +0,0 @@
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

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

View File

@@ -1,19 +0,0 @@
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> {
/**
* 自动取消兑换订单
*/
int authCancel();
}

View File

@@ -1,16 +0,0 @@
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

@@ -1,119 +0,0 @@
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.ShopInfo;
import com.czg.account.entity.ShopUser;
import com.czg.account.service.MemberPointsLogService;
import com.czg.account.service.ShopInfoService;
import com.czg.account.service.ShopUserService;
import com.czg.account.vo.PointsShopListVO;
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 jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
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 {
@Resource
private ShopUserService shopUserService;
@Resource
private ShopInfoService shopInfoService;
private QueryWrapper buildQueryWrapper(MemberPointsLogDTO param) {
QueryWrapper queryWrapper = PageUtil.buildSortQueryWrapper();
if (ObjUtil.isNotNull(param.getUserId())) {
queryWrapper.eq(MemberPointsLog::getShopUserId, 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.SHOP_USER_ID));
return queryWrapper;
}
@Override
public Page<MemberPointsLogDTO> getMemberPointsLogPage(MemberPointsLogDTO param) {
QueryWrapper queryWrapper = buildFullQueryWrapper(param);
return super.pageAs(PageUtil.buildPage(), queryWrapper, MemberPointsLogDTO.class);
}
@Override
public List<PointsShopListVO> getList(long userId, String shopName) {
ArrayList<PointsShopListVO> pointsShopListVOS = new ArrayList<>();
List<ShopUser> shopUserList = shopUserService.list(new QueryWrapper().eq(ShopUser::getUserId, userId).gt(ShopUser::getAccountPoints, 0));
if (shopUserList.isEmpty()) {
return pointsShopListVOS;
}
Set<Long> shopIdList = shopUserList.stream().map(ShopUser::getSourceShopId).collect(Collectors.toSet());
QueryWrapper queryWrapper = new QueryWrapper().in(ShopInfo::getId, shopIdList);
if (StrUtil.isNotBlank(shopName)) {
queryWrapper.like(ShopInfo::getShopName, shopName);
}
Map<Long, ShopInfo> shopInfoMap = shopInfoService.list(queryWrapper).stream().collect(Collectors.toMap(ShopInfo::getId, item -> item));
for (ShopUser shopUser : shopUserList) {
ShopInfo shopInfo = shopInfoMap.get(shopUser.getSourceShopId());
if (shopInfo == null) {
continue;
}
pointsShopListVOS.add(new PointsShopListVO()
.setShopName(shopInfo.getShopName())
.setLogo(shopInfo.getLogo())
.setAccountPoints(shopUser.getAccountPoints())
.setShopId(shopInfo.getId()));
}
return pointsShopListVOS;
}
}

View File

@@ -1,291 +0,0 @@
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.entity.ShopInfo;
import com.czg.account.enums.PointUserGroupEnum;
import com.czg.account.param.MemberPointsParam;
import com.czg.account.service.MemberPointsService;
import com.czg.account.service.ShopInfoService;
import com.czg.enums.YesNoEnum;
import com.czg.exception.CzgException;
import com.czg.order.entity.OrderInfo;
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.DubboService;
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
*/
@DubboService
public class MemberPointsServiceImpl extends ServiceImpl<MemberPointsMapper, MemberPoints> implements MemberPointsService {
@Resource
private PointsBasicSettingMapper pointsBasicSettingMapper;
@Resource
private MemberPointsLogMapper memberPointsLogMapper;
@Resource
private ShopInfoService shopInfoService;
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();
ShopInfo byId = shopInfoService.getById(shopId);
if (byId == null) {
throw new CzgException("店铺不存在");
}
if (byId.getIsEnableVipSync().equals(1)) {
shopId = byId.getMainId();
}
queryWrapper.eq(MemberPoints::getSourceShopId, shopId);
queryWrapper.orderBy(MemberPoints::getShopUserId, 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 shopUserId) {
MemberPoints entity = super.getOne(query().eq(MemberPoints::getShopUserId, shopUserId));
if (entity == null) {
throw new CzgException("会员信息不存在");
}
if (entity.getAccountPoints() == null) {
entity.setAccountPoints(0);
super.updateById(entity);
}
return entity;
}
@Override
public MemberPoints getMemberPoints(Long shopUserId) {
return initMemberPoints(shopUserId);
}
@Override
public OrderDeductionPointsDTO getMemberUsablePoints(Long shopUserId, BigDecimal orderAmount) {
MemberPoints entity = initMemberPoints(shopUserId);
Long shopId = entity.getMainShopId();
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;
}
if (basic.getEquivalentPoints() == 0) {
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 shopUserId, BigDecimal orderAmount, BigDecimal deductionAmount) {
OrderDeductionPointsDTO core = getMemberUsablePoints(shopUserId, 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 shopUserId, BigDecimal orderAmount, int points) {
OrderDeductionPointsDTO core = getMemberUsablePoints(shopUserId, 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 shopUserId, int points, String content, OrderInfo orderInfo) {
MemberPoints entity = initMemberPoints(shopUserId);
// 扣除账户积分
entity.setAccountPoints(entity.getAccountPoints() - points);
entity.setLastPointsChangeTime(LocalDateTime.now());
entity.setLastFloatPoints(-points);
// 记录积分变动记录
MemberPointsLog log = new MemberPointsLog();
log.setShopId(entity.getSourceShopId());
log.setShopUserId(entity.getShopUserId());
log.setContent(content);
log.setFloatType("subtract");
log.setFloatPoints(-points);
// 有关联订单的需要回置订单表的相关积分使用字段
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 shopUserId, int points, String content, OrderInfo orderInfo) {
MemberPoints entity = initMemberPoints(shopUserId);
// 增加账户积分
entity.setAccountPoints(entity.getAccountPoints() + points);
entity.setLastPointsChangeTime(LocalDateTime.now());
entity.setLastFloatPoints(points);
// 记录积分变动记录
MemberPointsLog log = new MemberPointsLog();
log.setShopId(entity.getSourceShopId());
log.setShopUserId(entity.getShopUserId());
log.setContent(content);
log.setFloatType("add");
log.setFloatPoints(points);
// 有关联订单的需要回置订单表的相关积分使用字段
if (orderInfo != null) {
log.setOrderNo(orderInfo.getOrderNo());
}
super.updateById(entity);
memberPointsLogMapper.insert(log);
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void consumeAwardPoints(Long shopUserId, OrderInfo orderInfo) {
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(shopUserId);
} 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(shopUserId, awardPoints.intValue(), StrUtil.format("消费¥{}送{}积分", payAmount, awardPoints.intValue()), orderInfo);
}
}

View File

@@ -1,27 +0,0 @@
package com.czg.service.account.service.impl;
import com.czg.account.entity.PointsBasicSetting;
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;
/**
* 积分基本设置 服务层实现。
*
* @author ww
* @since 2025-02-20
*/
@DubboService
public class PointsBasicSettingServiceImpl extends ServiceImpl<PointsBasicSettingMapper, PointsBasicSetting> implements PointsBasicSettingService{
@Override
public void initInfo(Long id) {
PointsBasicSetting record = getById(id);
if (record == null) {
PointsBasicSetting pointsBasicSetting = new PointsBasicSetting();
pointsBasicSetting.setShopId(id);
save(pointsBasicSetting);
}
}
}

View File

@@ -1,456 +0,0 @@
package com.czg.service.account.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import com.czg.account.dto.points.PointsExchangeRecordDTO;
import com.czg.account.entity.*;
import com.czg.account.enums.PointsExchangeStatusEnum;
import com.czg.account.enums.PointsOrderPayTypeEnum;
import com.czg.account.param.PointsExchangeCfParam;
import com.czg.account.param.PointsExchangeRecordParam;
import com.czg.account.param.PointsOrderCreateParam;
import com.czg.account.param.PointsOrderPayParam;
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.HttpContextUtil;
import com.czg.utils.PageUtil;
import com.czg.validator.ValidatorUtil;
import com.czg.validator.group.DefaultGroup;
import com.czg.validator.group.InsertGroup;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.servlet.http.HttpServletRequest;
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 final PointsBasicSettingMapper pointsBasicSettingMapper;
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.getShopUserId())) {
queryWrapper.eq(PointsExchangeRecord::getShopUserId, param.getShopUserId());
}
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);
}
private Object[] verify(PointsOrderCreateParam param) {
ValidatorUtil.validateEntity(param, InsertGroup.class);
PointsBasicSetting basic = pointsBasicSettingMapper.selectOneByQuery(query().eq(PointsBasicSetting::getShopId, param.getShopId()));
if (basic == null) {
throw new CzgException("商家未配置积分锁客基本设置");
}
if (basic.getEnablePointsMall() != 1) {
throw new CzgException("积分商城未开启");
}
PointsGoodsSetting goods = pointsGoodsSettingMapper.selectOneById(param.getPointsGoodsId());
if (goods == null) {
throw new CzgException("兑换的商品信息不存在");
}
if (goods.getDelFlag() == 1) {
throw new CzgException("兑换的商品信息不存在");
}
param.setPointsGoodsName(goods.getGoodsName());
param.setGoodsImageUrl(goods.getGoodsImageUrl());
Integer status = goods.getStatus();
if (status != 1) {
throw new CzgException("兑换的商品已下架");
}
Integer quantity = goods.getQuantity();
if (quantity <= 0) {
throw new CzgException("兑换的商品库存不足");
}
MemberPoints memberPoints = memberPointsMapper.selectOneByQuery(query().eq(MemberPoints::getShopUserId, param.getUserId()));
if (memberPoints == null) {
throw new CzgException("会员积分不足无法兑换此商品");
}
Integer accountPoints = memberPoints.getAccountPoints();
Integer requiredPoints = goods.getRequiredPoints();
if (accountPoints < requiredPoints) {
throw new CzgException("会员积分不足无法兑换此商品");
}
return new Object[]{basic, goods, memberPoints};
}
@Override
public PointsExchangeRecordDTO create(PointsOrderCreateParam param) {
Long shopId = StpKit.USER.getShopId(0L);
param.setShopId(shopId);
Object[] verify = verify(param);
PointsBasicSetting basic = (PointsBasicSetting) verify[0];
PointsGoodsSetting goods = (PointsGoodsSetting) verify[1];
MemberPoints memberPoints = (MemberPoints) verify[2];
PointsExchangeRecord entity = BeanUtil.copyProperties(param, PointsExchangeRecord.class);
entity.setExtraPaymentAmount(goods.getExtraPrice());
entity.setSpendPoints(goods.getRequiredPoints());
Snowflake seqNo = IdUtil.getSnowflake(0, 0);
String orderNo = DateUtil.format(new Date(), "yyyyMMddHH") + StrUtil.subSuf(seqNo.nextIdStr(), -12);
entity.setOrderNo(orderNo);
entity.setStatus(PointsExchangeStatusEnum.UNPAID.value());
// 生成订单
super.save(entity);
return BeanUtil.copyProperties(entity, PointsExchangeRecordDTO.class);
}
@Override
@Transactional(rollbackFor = Exception.class)
public PointsExchangeRecordDTO pay(PointsOrderPayParam param) {
ValidatorUtil.validateEntity(param, DefaultGroup.class);
PointsExchangeRecord entity = super.getById(param.getId());
if (entity == null) {
throw new CzgException("订单不存在");
}
try {
PointsOrderCreateParam pointsOrderCreateParam = BeanUtil.copyProperties(entity, PointsOrderCreateParam.class);
verify(pointsOrderCreateParam);
} catch (Exception e) {
throw new CzgException(e.getMessage().concat(",请您取消订单"));
}
if (!PointsExchangeStatusEnum.UNPAID.value().equals(entity.getStatus())) {
throw new CzgException("订单状态异常,请刷新后重试");
}
entity.setPayType(param.getPayType().toUpperCase());
if (!PointsOrderPayTypeEnum.getValues().contains(param.getPayType().toUpperCase())) {
throw new CzgException("支付方式不合法");
}
// 设置支付方式
entity.setPayMethod(PointsOrderPayTypeEnum.getText(param.getPayType().toUpperCase()));
// 纯积分支付并且商品需要额外支付金额,需要抛出异常
if (PointsOrderPayTypeEnum.POINTS.getValue().equals(entity.getPayType()) && NumberUtil.isGreater(entity.getExtraPaymentAmount(), BigDecimal.ZERO)) {
throw new CzgException("此商品需要额外支付金额");
}
if (!PointsOrderPayTypeEnum.POINTS.getValue().equals(entity.getPayType()) && NumberUtil.equals(entity.getExtraPaymentAmount(), BigDecimal.ZERO)) {
throw new CzgException("此商品不需要额外支付金额");
}
// 如果不需要额外支付
if (NumberUtil.equals(entity.getExtraPaymentAmount(), BigDecimal.ZERO)) {
// 设置核销券码
entity.setCouponCode(IdUtil.getSnowflakeNextIdStr());
entity.setStatus("waiting");
entity.setPayTime(LocalDateTime.now());
entity.setPayType(param.getPayType().toUpperCase());
super.updateById(entity);
waitingAfter(entity);
return BeanUtil.copyProperties(entity, PointsExchangeRecordDTO.class);
}
if (StrUtil.isBlank(param.getOpenId())) {
throw new CzgException("openId/userId不能为空");
}
HttpServletRequest request = HttpContextUtil.getHttpServletRequest();
String ip = null;
if (request != null) {
ip = JakartaServletUtil.getClientIP(request);
}
if (StrUtil.isBlank(ip)) {
throw new CzgException("无法获取用户ip地址");
}
// 需要额外支付
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");
}
if (1 == 1) {
// TODO 对接支付接口
throw new CzgException("TODO暂未对接支付接口");
}
return null;
/*// 准备支付参数
// 应用ID
String appId = thirdApply.getAppId();
String appToken = thirdApply.getAppToken();
String storeId = thirdApply.getStoreId();
// 订单标题(商品名称)
String subject = entity.getPointsGoodsName();
// 商品描述
String body = StrUtil.format("兑换商品:{} * {}", entity.getPointsGoodsName(), "1");
// 交易金额 单位分 100 表示1元
long amount = NumberUtil.mul(entity.getExtraPaymentAmount(), new BigDecimal("100")).longValue();
// 支付方式
String payType = entity.getPayType().toUpperCase();
// 子商户APPID
String subAppid = thirdApply.getSmallAppid();
if ("alipay".equalsIgnoreCase(entity.getPayType())) {
subAppid = thirdApply.getAlipaySmallAppid();
}
// 用户唯一标识 微信支付时传入用户的openId支付宝支付和银联支付时传入用户的userId
String openId = record.getOpenId();
// 用户IP
String clientIp = record.getIp();
// 商户订单号 mchOrderNo
String mchOrderNo = IdUtil.getSnowflakeNextIdStr();
// 回调地址
String notifyUrl = pointsGoodsOrderCallBack;
// 支付成功后的跳转页面
String returnUrl = null;
PublicResp<WxScanPayResp> publicResp;
try {
publicResp = thirdPayService.scanpay(thirdUrl, appId, subject, body, amount, payType, subAppid, openId, clientIp, mchOrderNo, storeId, notifyUrl, returnUrl, appToken);
} catch (Exception e) {
log.error("拉起支付失败:", e);
throw new MsgException(StrUtil.format("拉起支付失败:{}", e.getMessage()));
}
if (publicResp == null) {
throw new MsgException("拉起支付失败:无响应");
}
if (!"000000".equals(publicResp.getCode())) {
throw new MsgException(publicResp.getMsg());
}
WxScanPayResp payResp = publicResp.getObjData();
if (!"TRADE_AWAIT".equals(payResp.getState())) {
throw new MsgException(StrUtil.format("拉起支付失败:{}", payResp.getState()));
}
entity.setPayOrderId(payResp.getPayOrderId());
super.updateById(entity);
entity.setPayInfo(payResp.getPayInfo());
return entity;*/
}
/**
* 支付完成的后续操作
*
* @param entity 积分兑换实体
*/
private void waitingAfter(PointsExchangeRecord entity) {
PointsGoodsSetting goods = pointsGoodsSettingMapper.selectOneById(entity.getPointsGoodsId());
if (goods == null) {
throw new CzgException("积分商品不存在");
}
MemberPoints memberPoints = memberPointsMapper.selectOneById(entity.getShopUserId());
if (memberPoints == null) {
throw new CzgException("会员积分不足无法兑换此商品");
}
Integer quantity = goods.getQuantity();
Integer accountPoints = memberPoints.getAccountPoints();
Integer requiredPoints = goods.getRequiredPoints();
// 扣减积分
memberPoints.setAccountPoints(accountPoints - requiredPoints);
memberPoints.setLastPointsChangeTime(LocalDateTime.now());
memberPoints.setLastFloatPoints(-requiredPoints);
memberPointsMapper.update(memberPoints);
// 扣减库存
goods.setQuantity(quantity - 1);
pointsGoodsSettingMapper.update(goods);
// 记录积分浮动流水
MemberPointsLog log = new MemberPointsLog();
log.setShopId(entity.getShopId());
log.setShopUserId(entity.getShopUserId());
log.setContent(StrUtil.format("兑换商品:{} * {}", entity.getPointsGoodsName(), "1"));
log.setFloatType("subtract");
log.setFloatPoints(-requiredPoints);
memberPointsLogMapper.insert(log);
}
@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::getShopUserId, entity.getShopUserId()));
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.setShopUserId(entity.getShopUserId());
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("count(*) as count,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() {
super.mapper.authCancel();
}
}

View File

@@ -1,50 +0,0 @@
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

@@ -75,8 +75,6 @@ public class ShopInfoServiceImpl extends ServiceImpl<ShopInfoMapper, ShopInfo> i
@Resource
private FreeDineConfigService freeDineConfigService;
@Resource
private PointsBasicSettingService pointsBasicSettingService;
@Resource
private ShopConfigService shopConfigService;
@DubboReference
@@ -203,7 +201,6 @@ public class ShopInfoServiceImpl extends ServiceImpl<ShopInfoMapper, ShopInfo> i
shopPayTypeService.addInfo(shopInfo.getId());
// 初始化积分霸王餐设置
freeDineConfigService.getConfig(shopInfo.getId());
pointsBasicSettingService.initInfo(shopInfo.getId());
updateById(shopInfo);
syncSaveConfig(shopInfo, shopInfoAddDTO);

View File

@@ -1,6 +0,0 @@
<?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,6 +0,0 @@
<?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.PointsBasicSettingMapper">
</mapper>

View File

@@ -1,14 +0,0 @@
<?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">
<update id="authCancel">
update tb_points_exchange_record
set status = 'cancel',
cancel_or_refund_time = now(),
cancel_or_refund_reason = '超时未支付,系统自动取消订单'
where status = 'unpaid'
and TIMESTAMPDIFF(MINUTE, create_time, NOW()) >= 5
</update>
</mapper>

View File

@@ -1,6 +0,0 @@
<?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>