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,103 +0,0 @@
package com.czg.controller.admin;
import com.czg.account.dto.points.OrderDeductionPointsDTO;
import com.czg.account.entity.MemberPoints;
import com.czg.account.param.ConsumeAwardPointsParam;
import com.czg.account.param.MemberPointsParam;
import com.czg.account.param.PayedDeductPointsParam;
import com.czg.account.service.MemberPointsService;
import com.czg.log.annotation.OperationLog;
import com.czg.resp.CzgResult;
import com.mybatisflex.core.paginate.Page;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
/**
* 会员积分
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@AllArgsConstructor
@RestController
@RequestMapping("/admin/points/memberPoints")
public class MemberPointsController {
private final MemberPointsService memberPointsService;
/**
* 分页
*/
@GetMapping("page")
@OperationLog("会员积分-分页")
//@SaAdminCheckPermission("memberPoints:page")
public CzgResult<Page<MemberPoints>> getMemberPointsPage(MemberPointsParam param) {
Page<MemberPoints> page = memberPointsService.getMemberPointsPage(param);
return CzgResult.success(page);
}
/**
* 001-会员积分账户信息
*/
@GetMapping("userPoints")
public CzgResult<MemberPoints> getMemberPoints(@RequestParam Long shopUserId) {
MemberPoints data = memberPointsService.getMemberPoints(shopUserId);
return CzgResult.success(data);
}
/**
* 002-获取订单可用积分及抵扣金额(支付页面使用)
*
* @param shopUserId 用户id
* @param orderAmount 订单金额
*/
@GetMapping("calcUsablePoints")
public CzgResult<OrderDeductionPointsDTO> getMemberUsablePoints(@RequestParam Long shopUserId, @RequestParam BigDecimal orderAmount) {
OrderDeductionPointsDTO usablePoints = memberPointsService.getMemberUsablePoints(shopUserId, orderAmount);
return CzgResult.success(usablePoints);
}
/**
* 004-根据抵扣金额计算所需积分
*
* @param shopUserId 用户id
* @param orderAmount 订单金额
* @param deductionAmount 抵扣金额
*/
@GetMapping("calcUsedPoints")
public CzgResult<Integer> calcUsedPoints(@RequestParam Long shopUserId, @RequestParam BigDecimal orderAmount, @RequestParam BigDecimal deductionAmount) {
int points = memberPointsService.calcUsedPoints(shopUserId, orderAmount, deductionAmount);
return CzgResult.success(points);
}
/**
* 003-根据积分计算可抵扣金额
*/
@GetMapping("calcDeductionAmount")
public CzgResult<BigDecimal> calcDeductionAmount(@RequestParam Long shopUserId, @RequestParam BigDecimal orderAmount, @RequestParam Integer points) {
BigDecimal deductionAmount = memberPointsService.calcDeductionAmount(shopUserId, orderAmount, points);
return CzgResult.success(deductionAmount);
}
/**
* 005-支付完成扣减积分(支付成功回调中使用)
*/
@PostMapping("payedDeductPoints")
public CzgResult<Boolean> deductPoints(@RequestBody PayedDeductPointsParam param) {
//boolean ret = memberPointsService.deductPoints(param.getUserId(), param.getPoints(), param.getContent(), param.getOrderId());
return CzgResult.success();
}
/**
* 006-消费赠送积分(支付成功回调中使用)
*/
@PostMapping("consumeAwardPoints")
public CzgResult<Void> consumeAwardPoints(@RequestBody ConsumeAwardPointsParam param) {
//memberPointsService.consumeAwardPoints(param.getUserId(), param.getOrderId());
return CzgResult.success();
}
}

View File

@@ -1,37 +0,0 @@
package com.czg.controller.admin;
import com.czg.account.dto.points.MemberPointsLogDTO;
import com.czg.account.service.MemberPointsLogService;
import com.czg.log.annotation.OperationLog;
import com.czg.resp.CzgResult;
import com.mybatisflex.core.paginate.Page;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 会员积分变动记录
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@AllArgsConstructor
@RestController
@RequestMapping("/admin/points/memberPointsLog")
public class MemberPointsLogController {
private final MemberPointsLogService memberPointsLogService;
/**
* 分页
*/
@GetMapping("page")
@OperationLog("会员积分变动记录-分页")
//@SaAdminCheckPermission("memberPointsLog:page")
public CzgResult<Page<MemberPointsLogDTO>> getMemberPointsLogPage(MemberPointsLogDTO param) {
Page<MemberPointsLogDTO> page = memberPointsLogService.getMemberPointsLogPage(param);
return CzgResult.success(page);
}
}

View File

@@ -1,96 +0,0 @@
package com.czg.controller.admin;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import com.czg.account.dto.points.PointsBasicSettingDTO;
import com.czg.account.entity.PointsBasicSetting;
import com.czg.account.service.PointsBasicSettingService;
import com.czg.exception.CzgException;
import com.czg.log.annotation.OperationLog;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit;
import com.czg.validator.ValidatorUtil;
import com.czg.validator.group.DefaultGroup;
import com.czg.validator.group.InsertGroup;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
/**
* 积分基本设置
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@AllArgsConstructor
@RestController
@RequestMapping("/admin/points/basicSetting")
public class PointsBasicSettingController {
private final PointsBasicSettingService pointsBasicSettingService;
/**
* 详情
*/
@GetMapping()
@OperationLog("积分基本设置-详情")
//@SaAdminCheckPermission("basicSetting:info")
public CzgResult<PointsBasicSettingDTO> getPointsBasicSetting() {
Long shopId = StpKit.USER.getShopId(0L);
PointsBasicSetting entity = pointsBasicSettingService.getById(shopId);
PointsBasicSettingDTO data = BeanUtil.copyProperties(entity, PointsBasicSettingDTO.class);
return CzgResult.success(data);
}
/**
* 保存
*/
@PostMapping
@OperationLog("积分基本设置-保存")
//@SaAdminCheckPermission("basicSetting:save")
public CzgResult<Void> savePointsBasicSetting(@RequestBody PointsBasicSettingDTO dto) {
Long shopId = StpKit.USER.getShopId(0L);
dto.setShopId(shopId);
ValidatorUtil.validateEntity(dto, InsertGroup.class, DefaultGroup.class);
validateDTO(dto);
PointsBasicSetting entity = BeanUtil.copyProperties(dto, PointsBasicSetting.class);
PointsBasicSetting record = pointsBasicSettingService.getById(shopId);
if (record == null) {
pointsBasicSettingService.save(entity);
} else {
pointsBasicSettingService.updateById(entity);
}
return CzgResult.success();
}
private void validateDTO(PointsBasicSettingDTO dto) {
try {
Assert.notNull(dto.getShopId(), "{}({})不能为空", "店铺id", "shopId");
Assert.notNull(dto.getEnableRewards(), "{}({})不能为空", "开启消费赠送积分", "enableRewards");
Assert.notEmpty(dto.getRewardsGroup(), "{}({})不能为空", "赠积分适用群体", "rewardsGroup");
Assert.notNull(dto.getConsumeAmount(), "{}({})不能为空", "每消费xx元赠送1积分", "consumeAmount");
Assert.notNull(dto.getEnableDeduction(), "{}({})不能为空", "开启下单积分抵扣", "enableDeduction");
Assert.notEmpty(dto.getDeductionGroup(), "{}({})不能为空", "抵扣适用群体", "deductionGroup");
Assert.notNull(dto.getMinPaymentAmount(), "{}({})不能为空", "下单实付抵扣门槛", "minPaymentAmount");
Assert.notNull(dto.getMaxDeductionRatio(), "{}({})不能为空", "下单最高抵扣比例", "maxDeductionRatio");
Assert.notNull(dto.getEquivalentPoints(), "{}({})不能为空", "下单抵扣积分比例", "equivalentPoints");
Assert.notNull(dto.getEnablePointsMall(), "{}({})不能为空", "开启积分商城", "enablePointsMall");
Assert.notEmpty(dto.getBrowseMode(), "{}({})不能为空", "浏览模式", "browseMode");
} catch (IllegalArgumentException e) {
throw new CzgException(e.getMessage());
}
if (NumberUtil.isLessOrEqual(dto.getMinPaymentAmount(), BigDecimal.ZERO)) {
throw new CzgException("下单实付抵扣门槛不能小于等于0");
}
if (NumberUtil.isLessOrEqual(dto.getMaxDeductionRatio(), BigDecimal.ZERO)) {
throw new CzgException("下单最高抵扣比例不能小于等于0");
}
if (NumberUtil.isGreater(dto.getMaxDeductionRatio(), new BigDecimal("100"))) {
throw new CzgException("下单最高抵扣比例不能大于100");
}
}
}

View File

@@ -1,74 +0,0 @@
package com.czg.controller.admin;
import com.czg.account.dto.points.PointsExchangeRecordDTO;
import com.czg.account.param.PointsExchangeRecordParam;
import com.czg.account.service.PointsExchangeRecordService;
import com.czg.account.vo.PointsExchangeSummaryVo;
import com.czg.log.annotation.OperationLog;
import com.czg.resp.CzgResult;
import com.mybatisflex.core.paginate.Page;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
/**
* 积分兑换记录
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@AllArgsConstructor
@RestController
@RequestMapping("/admin/points/exchangeRecord")
public class PointsExchangeRecordController {
private final PointsExchangeRecordService pointsExchangeRecordService;
/**
* 分页
*/
@GetMapping("page")
@OperationLog("积分兑换-分页")
//@SaAdminCheckPermission("pointsExchangeRecord:page")
public CzgResult<Page<PointsExchangeRecordDTO>> getPointsExchangeRecordPage(PointsExchangeRecordParam param) {
Page<PointsExchangeRecordDTO> page = pointsExchangeRecordService.getPointsExchangeRecordPage(param);
return CzgResult.success(page);
}
/**
* 详情
*
* @param id 兑换id
*/
@GetMapping("{id}")
@OperationLog("积分兑换-详情")
//@SaAdminCheckPermission("pointsExchangeRecord:info")
public CzgResult<PointsExchangeRecordDTO> getPointsExchangeRecordById(@PathVariable("id") Long id) {
PointsExchangeRecordDTO data = pointsExchangeRecordService.getPointsExchangeRecordById(id);
return CzgResult.success(data);
}
/**
* 核销
*
* @param couponCode 兑换码
*/
@PostMapping("checkout")
@OperationLog("积分兑换-核销")
//@SaAdminCheckPermission("pointsExchangeRecord:checkout")
public CzgResult<Void> checkout(@RequestParam String couponCode) {
pointsExchangeRecordService.checkout(couponCode);
return CzgResult.success();
}
/**
* 统计
*/
@GetMapping("total")
@OperationLog("积分兑换-统计")
//@SaAdminCheckPermission("pointsExchangeRecord:total")
public CzgResult<PointsExchangeSummaryVo> total(PointsExchangeRecordParam param) {
PointsExchangeSummaryVo data = pointsExchangeRecordService.total(param);
return CzgResult.success(data);
}
}

View File

@@ -1,100 +0,0 @@
package com.czg.controller.admin;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
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.log.annotation.OperationLog;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit;
import com.czg.validator.ValidatorUtil;
import com.czg.validator.group.DefaultGroup;
import com.czg.validator.group.InsertGroup;
import com.czg.validator.group.UpdateGroup;
import com.mybatisflex.core.paginate.Page;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
/**
* 积分商品设置
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@AllArgsConstructor
@RestController
@RequestMapping("/admin/points/goodsSetting")
public class PointsGoodsSettingController {
private final PointsGoodsSettingService pointsGoodsSettingService;
/**
* 分页
*/
@GetMapping("page")
@OperationLog("积分商品设置-分页")
//@SaAdminCheckPermission("pointsGoodsSetting:page")
public CzgResult<Page<PointsGoodsSettingDTO>> getPointsGoodsSettingPage(PointsGoodsSettingDTO param) {
Page<PointsGoodsSettingDTO> data = pointsGoodsSettingService.getPointsGoodsSettingPage(param);
return CzgResult.success(data);
}
/**
* 详情
*/
@GetMapping("{id}")
@OperationLog("积分商品设置-详情")
//@SaAdminCheckPermission("pointsGoodsSetting:info")
public CzgResult<PointsGoodsSettingDTO> getPointsGoodsSettingById(@PathVariable("id") Long id) {
PointsGoodsSetting entity = pointsGoodsSettingService.getById(id);
PointsGoodsSettingDTO data = BeanUtil.copyProperties(entity, PointsGoodsSettingDTO.class);
return CzgResult.success(data);
}
/**
* 新增
*/
@PostMapping
@OperationLog("积分商品设置-新增")
//@SaAdminCheckPermission("pointsGoodsSetting:add")
public CzgResult<Boolean> addPointsGoodsSetting(@RequestBody PointsGoodsSettingDTO dto) {
Long shopId = StpKit.USER.getShopId(0L);
dto.setShopId(shopId);
ValidatorUtil.validateEntity(dto, InsertGroup.class, DefaultGroup.class);
PointsGoodsSetting entity = BeanUtil.copyProperties(dto, PointsGoodsSetting.class);
boolean ret = pointsGoodsSettingService.save(entity);
return CzgResult.success(ret);
}
/**
* 修改
*/
@PutMapping
@OperationLog("积分商品设置-修改")
//@SaAdminCheckPermission("pointsGoodsSetting:update")
public CzgResult<Boolean> updatePointsGoodsSetting(@RequestBody PointsGoodsSettingDTO dto) {
Long shopId = StpKit.USER.getShopId(0L);
dto.setShopId(shopId);
ValidatorUtil.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
PointsGoodsSetting entity = new PointsGoodsSetting();
BeanUtil.copyProperties(dto, entity, CopyOptions.create().setIgnoreNullValue(false));
boolean ret = pointsGoodsSettingService.updateById(entity);
return CzgResult.success(ret);
}
/**
* 删除
*/
@DeleteMapping("{id}")
@OperationLog("积分商品设置-删除")
//@SaAdminCheckPermission("pointsGoodsSetting:delete")
public CzgResult<Void> deletePointsGoodsSetting(@PathVariable("id") Long id) {
PointsGoodsSetting entity = pointsGoodsSettingService.getById(id);
entity.setDelFlag(DeleteEnum.DELETED.value());
pointsGoodsSettingService.updateById(entity);
return CzgResult.success();
}
}

View File

@@ -7,45 +7,51 @@ import com.czg.market.vo.UMemberConfigVO;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 会员相关
* 会员
*
* @author Administrator
*/
@RestController
@RequestMapping("/user/member")
@RequestMapping("/user")
public class UMemberController {
@Resource
private TbMemberConfigService memberConfigService;
/**
* 获取当前店铺会员开通配置信息
*
* @param shopId 店铺id
*/
@GetMapping("/config")
@GetMapping("/member/config")
public CzgResult<UMemberConfigVO> getConfig(@RequestParam Long shopId) {
return CzgResult.success(memberConfigService.detail(shopId, StpKit.USER.getLoginIdAsLong()));
}
/**
* 获取所有已开通的会员
*
* @return 列表
*/
@GetMapping("/list")
@GetMapping("/member/list")
public CzgResult<List<MemberListVO>> getMemberList() {
return CzgResult.success(memberConfigService.getMemberList(StpKit.USER.getLoginIdAsLong()));
}
/**
* 获取会员信息详情
*
* @param shopId 店铺id
*/
@GetMapping("/detail")
@GetMapping("/member/detail")
public CzgResult<MemberDetailVO> getDetail(@RequestParam Long shopId) {
return CzgResult.success(memberConfigService.getUserDetail(StpKit.USER.getLoginIdAsLong(), shopId));
}
}

View File

@@ -1,110 +0,0 @@
package com.czg.controller.user;
import cn.hutool.core.bean.BeanUtil;
import com.czg.account.dto.points.OrderDeductionPointsDTO;
import com.czg.account.dto.points.PointsBasicSettingDTO;
import com.czg.account.entity.MemberPoints;
import com.czg.account.entity.PointsBasicSetting;
import com.czg.account.param.ConsumeAwardPointsParam;
import com.czg.account.param.PayedDeductPointsParam;
import com.czg.account.service.MemberPointsService;
import com.czg.account.service.PointsBasicSettingService;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
/**
* 支付相关积分接口
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@AllArgsConstructor
@RestController
@RequestMapping("/user/points/memberPoints")
public class UMemberPointsController {
private final MemberPointsService memberPointsService;
private final PointsBasicSettingService pointsBasicSettingService;
/**
* 000-积分基本设置详情
*/
@GetMapping("pointsBasicSetting")
public CzgResult<PointsBasicSettingDTO> getPointsBasicSetting() {
Long shopId = StpKit.USER.getShopId();
PointsBasicSetting entity = pointsBasicSettingService.getById(shopId);
PointsBasicSettingDTO data = BeanUtil.copyProperties(entity, PointsBasicSettingDTO.class);
return CzgResult.success(data);
}
/**
* 001-会员积分账户信息
*/
@GetMapping("myPoints")
public CzgResult<MemberPoints> getMemberPoints() {
long shopUserId = StpKit.USER.getLoginIdAsLong();
MemberPoints data = memberPointsService.getMemberPoints(shopUserId);
return CzgResult.success(data);
}
/**
* 002-获取订单可用积分及抵扣金额(支付页面使用)
*
* @param shopUserId 用户id
* @param orderAmount 订单金额
*/
@GetMapping("calcUsablePoints")
public CzgResult<OrderDeductionPointsDTO> getMemberUsablePoints(@RequestParam Long shopUserId, @RequestParam BigDecimal orderAmount) {
OrderDeductionPointsDTO usablePoints = memberPointsService.getMemberUsablePoints(shopUserId, orderAmount);
return CzgResult.success(usablePoints);
}
/**
* 004-根据抵扣金额计算所需积分
*
* @param shopUserId 用户id
* @param orderAmount 订单金额
* @param deductionAmount 抵扣金额
*/
@GetMapping("calcUsedPoints")
public CzgResult<Integer> calcUsedPoints(@RequestParam Long shopUserId, @RequestParam BigDecimal orderAmount, @RequestParam BigDecimal deductionAmount) {
int points = memberPointsService.calcUsedPoints(shopUserId, orderAmount, deductionAmount);
return CzgResult.success(points);
}
/**
* 003-根据积分计算可抵扣金额
*
* @param shopUserId 用户id
* @param orderAmount 订单金额
* @param points 使用积分
*/
@GetMapping("calcDeductionAmount")
public CzgResult<BigDecimal> calcDeductionAmount(@RequestParam Long shopUserId, @RequestParam BigDecimal orderAmount, @RequestParam Integer points) {
BigDecimal deductionAmount = memberPointsService.calcDeductionAmount(shopUserId, orderAmount, points);
return CzgResult.success(deductionAmount);
}
/**
* 005-支付完成扣减积分(支付成功回调中使用)
*/
@PostMapping("payedDeductPoints")
public CzgResult<Boolean> deductPoints(@RequestBody PayedDeductPointsParam param) {
//boolean ret = memberPointsService.deductPoints(param.getUserId(), param.getPoints(), param.getContent(), param.getOrderId());
return CzgResult.success();
}
/**
* 006-消费赠送积分(支付成功回调中使用)
*/
@PostMapping("consumeAwardPoints")
public CzgResult<Void> consumeAwardPoints(@RequestBody ConsumeAwardPointsParam param) {
//memberPointsService.consumeAwardPoints(param.getUserId(), param.getOrderId());
return CzgResult.success();
}
}

View File

@@ -1,86 +0,0 @@
package com.czg.controller.user;
import com.czg.account.dto.points.PointsExchangeRecordDTO;
import com.czg.account.dto.points.PointsGoodsSettingDTO;
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.service.PointsGoodsSettingService;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit;
import com.mybatisflex.core.paginate.Page;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
/**
* 积分商城
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@AllArgsConstructor
@RestController
@RequestMapping("/user/points/mall")
public class UPointsMallController {
private final PointsGoodsSettingService pointsGoodsSettingService;
private final PointsExchangeRecordService pointsExchangeRecordService;
/**
* 商品列表
*/
@GetMapping("/goods/page")
public CzgResult<Page<PointsGoodsSettingDTO>> getPointsGoodsSettingPage(PointsGoodsSettingDTO param) {
Page<PointsGoodsSettingDTO> data = pointsGoodsSettingService.getPointsGoodsSettingPage(param);
return CzgResult.success(data);
}
/**
* 生成订单
*/
@PostMapping("/order/create")
public CzgResult<PointsExchangeRecordDTO> create(@RequestBody PointsOrderCreateParam param) {
PointsExchangeRecordDTO data = pointsExchangeRecordService.create(param);
return CzgResult.success(data);
}
/**
* 取消订单
*/
@PostMapping("/order/cancel")
public CzgResult<Void> cancel(@RequestBody PointsExchangeCfParam param) {
pointsExchangeRecordService.cancel(param);
return CzgResult.success();
}
/**
* 支付订单
*/
@PostMapping("/order/pay")
public CzgResult<PointsExchangeRecordDTO> pay(@RequestBody PointsOrderPayParam param) {
PointsExchangeRecordDTO data = pointsExchangeRecordService.pay(param);
return CzgResult.success(data);
}
/**
* 申请退单
*/
@PostMapping("/order/refund")
public CzgResult<Void> refund(@RequestBody PointsExchangeCfParam param) {
pointsExchangeRecordService.refund(param);
return CzgResult.success();
}
/**
* 我的兑换记录
*/
@GetMapping("/order/page")
public CzgResult<Page<PointsExchangeRecordDTO>> getPointsExchangeRecordPage(PointsExchangeRecordParam param) {
param.setShopUserId(StpKit.USER.getLoginIdAsLong());
Page<PointsExchangeRecordDTO> page = pointsExchangeRecordService.getPointsExchangeRecordPage(param);
return CzgResult.success(page);
}
}

View File

@@ -3,18 +3,13 @@ package com.czg.controller.user;
import com.czg.account.dto.shopuser.ShopUserAddDTO;
import com.czg.account.dto.shopuser.ShopUserDetailDTO;
import com.czg.account.dto.shopuser.ShopUserVipCardDTO;
import com.czg.account.entity.MemberPointsLog;
import com.czg.account.entity.ShopInfo;
import com.czg.account.entity.ShopUser;
import com.czg.account.entity.ShopUserFlow;
import com.czg.account.service.MemberPointsLogService;
import com.czg.account.service.ShopUserFlowService;
import com.czg.account.service.UShopUserService;
import com.czg.account.vo.MemberPointsLogVO;
import com.czg.account.vo.PointsShopListVO;
import com.czg.account.vo.ShopUserFlowInfoVO;
import com.czg.annotation.Debounce;
import com.czg.annotation.SaAdminCheckPermission;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit;
import com.czg.utils.MyQueryWrapper;
@@ -25,8 +20,6 @@ import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 店铺会员相关
*
@@ -39,8 +32,6 @@ public class UShopUserController {
private UShopUserService shopUserService;
@Resource
private ShopUserFlowService shopUserFlowService;
@Resource
private MemberPointsLogService memberPointsLogService;
/**
* 获取当前店铺会员信息
@@ -109,39 +100,4 @@ public class UShopUserController {
return CzgResult.success(shopUserFlowService.getOne(new QueryWrapper().eq(ShopUserFlow::getShopId, shopId)
.eq(ShopUserFlow::getUserId, StpKit.USER.getLoginIdAsLong()).eq(ShopUserFlow::getId, id)));
}
/**
* 获取用户所有门店下积分列表
*/
@GetMapping("/pointsShopList")
public CzgResult<List<PointsShopListVO>> getList(@RequestParam(required = false) String shopName) {
return CzgResult.success(memberPointsLogService.getList(StpKit.USER.getLoginIdAsLong(), shopName));
}
/**
* 获取积分明细
* @return 分页数据
*/
@GetMapping("/pointsRecord")
public CzgResult<Page<MemberPointsLogVO>> getPointsRecord(@RequestParam Long shopId) {
ShopUser shopUser = shopUserService.getShopUserInfo(shopId, StpKit.USER.getLoginIdAsLong());
return CzgResult.success(memberPointsLogService.pageAs(PageUtil.buildPage(), new MyQueryWrapper().eq(MemberPointsLog::getShopId, shopId)
.eq(MemberPointsLog::getShopUserId, shopUser.getId())
.selectAll(MemberPointsLog.class)
.select(ShopInfo::getShopName)
.leftJoin(ShopInfo.class).on(MemberPointsLog::getShopId, ShopInfo::getId)
.orderBy(MemberPointsLog::getId, false), MemberPointsLogVO.class));
}
/**
* 获取积分明细详情
* @param id 明细id
* @return 明细数据
*/
@GetMapping("/pointsRecord/detail")
public CzgResult<MemberPointsLog> getPointsRecordDetail(@RequestParam Long id,@RequestParam Long shopId) {
ShopUser shopUser = shopUserService.getShopUserInfo(shopId, StpKit.USER.getLoginIdAsLong());
return CzgResult.success(memberPointsLogService.getOne(new QueryWrapper().eq(MemberPointsLog::getShopId, shopId)
.eq(MemberPointsLog::getShopUserId, shopUser.getId()).eq(MemberPointsLog::getId, id)));
}
}

View File

@@ -1,40 +0,0 @@
package com.czg.task;
import com.czg.account.entity.ShopUser;
import com.czg.account.service.PointsExchangeRecordService;
import com.czg.account.service.ShopInfoService;
import com.czg.account.service.ShopUserFlowService;
import com.czg.account.service.ShopUserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 积分商品兑换定时任务
*
* @author tankaikai
* @since 2025-03-01 15:49
*/
@Slf4j
@Component
public class PointsProductExchangeTask {
@Resource
private PointsExchangeRecordService pointsExchangeRecordService;
@Resource
private ShopUserFlowService shopUserFlowService;
@Resource
private ShopInfoService shopInfoService;
@Resource
private ShopUserService shopUserService;
/**
* order 过期
*/
@Scheduled(cron = "0 */1 * * * ?")
public void run() {
pointsExchangeRecordService.authCancel();
}
}

View File

@@ -0,0 +1,92 @@
package com.czg.controller.admin;
import cn.hutool.core.bean.BeanUtil;
import com.czg.annotation.SaAdminCheckPermission;
import com.czg.market.dto.MkPointsConfigDTO;
import com.czg.market.dto.MkPointsUserDTO;
import com.czg.market.entity.MkPointsConfig;
import com.czg.market.entity.MkPointsUserRecord;
import com.czg.market.service.MkPointsConfigService;
import com.czg.market.service.MkPointsUserRecordService;
import com.czg.market.service.MkPointsUserService;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit;
import com.czg.utils.CzgStrUtils;
import com.czg.validator.ValidatorUtil;
import com.czg.validator.group.DefaultGroup;
import com.czg.validator.group.InsertGroup;
import com.mybatisflex.core.paginate.Page;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.Pattern;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* 积分配置
*
* @author ww
*/
@RestController
@RequestMapping("/admin/points/")
public class PointsConfigController {
@Resource
private MkPointsConfigService pointsConfigService;
@Resource
private MkPointsUserService pointsUserService;
@Resource
private MkPointsUserRecordService userRecordService;
/**
* 积分:配置:详情
*/
@GetMapping("/config")
@SaAdminCheckPermission(value = "points:config:info", name = "积分-配置-详情")
public CzgResult<MkPointsConfig> getPointsBasicSetting() {
MkPointsConfig entity = pointsConfigService.getById(StpKit.USER.getShopId());
return CzgResult.success(entity);
}
/**
* 积分:配置:新增/更新
*/
@PostMapping("/config")
@SaAdminCheckPermission(value = "points:config:up", name = "积分-配置-新增/更新")
public CzgResult<Void> savePointsBasicSetting(@RequestBody @Validated MkPointsConfigDTO dto) {
Long shopId = StpKit.USER.getShopId();
dto.setShopId(shopId);
ValidatorUtil.validateEntity(dto, InsertGroup.class, DefaultGroup.class);
MkPointsConfig entity = BeanUtil.copyProperties(dto, MkPointsConfig.class);
MkPointsConfig record = pointsConfigService.getById(shopId);
if (record == null) {
pointsConfigService.save(entity);
} else {
pointsConfigService.saveOrUpdate(entity);
}
return CzgResult.success();
}
/**
* 积分:用户:列表
*/
@GetMapping("/userPage")
@SaAdminCheckPermission(value = "points:user:list", name = "积分-用户-积分列表")
public CzgResult<Page<MkPointsUserDTO>> getPointsUserPage(
@RequestParam(required = false) @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") String phone,
@RequestParam(required = false, defaultValue = "1") Integer page,
@RequestParam(required = false, defaultValue = "10") Integer size) {
return CzgResult.success(pointsUserService.getPointsUserPage(CzgStrUtils.getStrOrNull(phone), page, size));
}
/**
* 积分:用户:积分详情
*/
@GetMapping("/userRecord")
@SaAdminCheckPermission(value = "points:user:record", name = "积分-用户-积分记录")
public CzgResult<Page<MkPointsUserRecord>> getPointsUserRecord(@RequestParam(required = false, defaultValue = "1") Integer page,
@RequestParam(required = false, defaultValue = "10") Integer size,
@RequestParam Long id) {
return CzgResult.success(userRecordService.pageByPointsUserId(page, size, id));
}
}

View File

@@ -0,0 +1,72 @@
package com.czg.controller.admin;
import cn.hutool.core.bean.BeanUtil;
import com.czg.BaseQueryParam;
import com.czg.annotation.SaAdminCheckPermission;
import com.czg.enums.DeleteEnum;
import com.czg.market.dto.MkPointsGoodsDTO;
import com.czg.market.entity.MkPointsGoods;
import com.czg.market.service.MkPointsGoodsService;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit;
import com.mybatisflex.core.paginate.Page;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* 积分商城
*
* @author ww
*/
@RestController
@RequestMapping("/admin/pointsGoods")
public class PointsGoodsController {
@Resource
private MkPointsGoodsService pointsGoodsSettingService;
/**
* 积分:商品:列表
*/
@GetMapping("page")
@SaAdminCheckPermission(value = "points:goods:info", name = "积分-商品-列表")
public CzgResult<Page<MkPointsGoods>> getPointsGoodsSettingPage(BaseQueryParam param) {
Page<MkPointsGoods> data = pointsGoodsSettingService.getPointsGoodsPage(param, StpKit.USER.getShopId());
return CzgResult.success(data);
}
/**
* 详情
*/
@GetMapping("/{id}")
@SaAdminCheckPermission(value = "points:goods:info", name = "积分-商品-列表")
public CzgResult<MkPointsGoods> getPointsGoodsSettingById(@PathVariable("id") Long id) {
return CzgResult.success(pointsGoodsSettingService.getById(id));
}
/**
* 积分:商品:新增/修改
*/
@PostMapping
@SaAdminCheckPermission(value = "points:goods:up", name = "积分-商品-新增/修改")
public CzgResult<Boolean> addPointsGoodsSetting(@RequestBody @Validated MkPointsGoodsDTO dto) {
dto.setShopId(StpKit.USER.getShopId());
MkPointsGoods entity = BeanUtil.copyProperties(dto, MkPointsGoods.class);
boolean ret = pointsGoodsSettingService.saveOrUpdate(entity);
return CzgResult.success(ret);
}
/**
* 删除
*/
@DeleteMapping("/{id}")
@SaAdminCheckPermission(value = "points:goods:delete", name = "积分-商品-新增/修改")
public CzgResult<Void> deletePointsGoodsSetting(@PathVariable("id") Long id) {
MkPointsGoods entity = pointsGoodsSettingService.getById(id);
entity.setDelFlag(DeleteEnum.DELETED.value());
pointsGoodsSettingService.updateById(entity);
return CzgResult.success();
}
}

View File

@@ -0,0 +1,89 @@
package com.czg.controller.user;
import com.czg.account.vo.PointsShopListVO;
import com.czg.market.entity.MkPointsConfig;
import com.czg.market.entity.MkPointsUser;
import com.czg.market.entity.MkPointsUserRecord;
import com.czg.market.service.MkPointsConfigService;
import com.czg.market.service.MkPointsUserRecordService;
import com.czg.market.service.MkPointsUserService;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 积分配置
*
* @author ww
*/
@RestController
@RequestMapping("/user/point/")
public class UPointsController {
@Resource
private MkPointsConfigService pointsConfigService;
@Resource
private MkPointsUserService pointsUserService;
@Resource
private MkPointsUserRecordService pointsUserRecordService;
/**
* 获取配置
*/
@GetMapping("pointsConfig")
public CzgResult<MkPointsConfig> getPointsConfig(@RequestParam(required = false) Long shopId) {
if (shopId == null) {
shopId = StpKit.USER.getShopId();
}
return CzgResult.success(pointsConfigService.getById(shopId));
}
/**
* 获取用户积分 包括配置信息
* {
* "pointsConfig": 配置信息,
* "pointsUser": 用户积分信息
* }
*/
@GetMapping("userPoints")
public CzgResult<Map<String, Object>> userPoints(@RequestParam(required = false) Long shopUserId) {
Long shopId = StpKit.USER.getShopId();
Map<String, Object> result = new HashMap<>(2);
MkPointsConfig pointsConfig = pointsConfigService.getById(shopId);
MkPointsUser pointsUser = pointsUserService.getOne(QueryWrapper.create().eq(MkPointsUser::getShopId, shopId).eq(MkPointsUser::getUserId, shopUserId));
result.put("pointsConfig", pointsConfig == null ? "" : pointsConfig);
result.put("pointsUser", pointsUser == null ? "" : pointsUser);
return CzgResult.success();
}
/**
* 获取用户所有门店下积分列表
*/
@GetMapping("/shopList")
public CzgResult<List<PointsShopListVO>> pointsShopList(@RequestParam(required = false) String shopName) {
return CzgResult.success(pointsUserService.pointsShopList(StpKit.USER.getLoginIdAsLong(), shopName));
}
/**
* 获取积分明细
*
* @return 分页数据
*/
@GetMapping("/userRecord")
public CzgResult<Page<MkPointsUserRecord>> getPointsRecord(@RequestParam(required = false, defaultValue = "1") Integer page,
@RequestParam(required = false, defaultValue = "10") Integer size,
@RequestParam Long id) {
return CzgResult.success(pointsUserRecordService.page(Page.of(page, size),
QueryWrapper.create().eq(MkPointsUserRecord::getMkPointsUserId, id).orderBy(MkPointsUserRecord::getCreateTime, false)));
}
}

View File

@@ -0,0 +1,86 @@
package com.czg.controller.admin;
import com.czg.account.vo.PointsExchangeSummaryVo;
import com.czg.annotation.SaAdminCheckPermission;
import com.czg.log.annotation.OperationLog;
import com.czg.market.dto.MkPointsGoodsRecordDTO;
import com.czg.market.dto.MkPointsGoodsRecordQueryDTO;
import com.czg.market.service.MkPointsGoodsRecordService;
import com.czg.market.service.MkPointsGoodsService;
import com.czg.order.dto.PointGoodsRefundDTO;
import com.czg.order.service.PointsGoodPayService;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit;
import com.mybatisflex.core.paginate.Page;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* 积分商品
*
* @author ww
*/
@RestController
@RequestMapping("/admin/points/goodsRecord")
public class PointsGoodsRecordController {
@Resource
private MkPointsGoodsRecordService goodsRecordService;
@Resource
private MkPointsGoodsService pointsGoodsService;
@Resource
private PointsGoodPayService goodPayService;
/**
* 积分:积分商品:兑换记录列表
*/
@GetMapping("page")
@SaAdminCheckPermission(value = "points:goodsRecord:list", name = "积分-积分商品-兑换记录")
public CzgResult<Page<MkPointsGoodsRecordDTO>> getGoodsRecordPage(MkPointsGoodsRecordQueryDTO param) {
param.setShopId(StpKit.USER.getShopId());
Page<MkPointsGoodsRecordDTO> page = goodsRecordService.getGoodsRecordPage(param);
return CzgResult.success(page);
}
/**
* 积分:积分商品:统计
*/
@GetMapping("total")
@SaAdminCheckPermission(value = "points:goodsRecord:total", name = "积分-积分商品-统计")
public CzgResult<PointsExchangeSummaryVo> total(MkPointsGoodsRecordQueryDTO param) {
param.setShopId(StpKit.USER.getShopId());
PointsExchangeSummaryVo data = goodsRecordService.total(param);
return CzgResult.success(data);
}
/**
* 核销
*
* @param couponCode 兑换码
*/
@PostMapping("checkout")
@OperationLog("积分兑换-核销")
@SaAdminCheckPermission(value = "points:goodsRecord:checkout", name = "积分-积分商品-核销")
public CzgResult<Boolean> checkout(@RequestBody String couponCode) {
return CzgResult.success(goodsRecordService.checkout(couponCode, StpKit.USER.getShopId()));
}
/**
* 退单/同意退单
*/
@PostMapping("/agreeRefund")
public CzgResult<Boolean> agreeRefund(@RequestBody @Validated PointGoodsRefundDTO param) {
return CzgResult.success(goodPayService.applyRefund(param, StpKit.USER.getShopId()));
}
/**
* 驳回退单
*/
@PostMapping("/rejectRefund")
public CzgResult<Boolean> rejectRefund(@RequestBody @Validated PointGoodsRefundDTO param) {
return CzgResult.success(pointsGoodsService.cancelRefund(param, StpKit.USER.getLoginIdAsLong()));
}
}

View File

@@ -0,0 +1,85 @@
package com.czg.controller.user;
import com.czg.market.entity.MkPointsGoods;
import com.czg.market.entity.MkPointsGoodsRecord;
import com.czg.market.service.MkPointsGoodsService;
import com.czg.order.dto.PointGoodsRefundDTO;
import com.czg.order.service.PointsGoodPayService;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit;
import com.czg.order.dto.PointGoodsExchangeDTO;
import com.czg.utils.ServletUtil;
import com.mybatisflex.core.paginate.Page;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* 积分商城
*
* @author ww
*/
@RestController
@RequestMapping("/user/pointGoods/")
public class UPointGoodsController {
@Resource
private MkPointsGoodsService pointsGoodsService;
@Resource
private PointsGoodPayService goodPayService;
/**
* 商品列表
*/
@GetMapping("/page")
public CzgResult<Page<MkPointsGoods>> getPointsGoodsSettingPage(@RequestParam(defaultValue = "1", required = false) Integer page,
@RequestParam(defaultValue = "10", required = false) Integer size,
Long shopId) {
Page<MkPointsGoods> data = pointsGoodsService.getPointsGoodsPageByUser(page, size, shopId, StpKit.USER.getLoginIdAsLong());
return CzgResult.success(data);
}
/**
* 生成订单
* 小程序支付
* payType 必填 支付方式aliPay 支付宝wechatPay 微信
* openId 必填
*/
@PostMapping("/exchange")
public CzgResult<Map<String, Object>> exchange(HttpServletRequest request, @Validated @RequestBody PointGoodsExchangeDTO param) {
param.setUserId(StpKit.USER.getLoginIdAsLong());
return goodPayService.exchange(ServletUtil.getClientIP(request), param);
}
/**
* 申请退单
*/
@PostMapping("/applyRefund")
public CzgResult<Boolean> applyRefund(@RequestBody @Validated PointGoodsRefundDTO param) {
return CzgResult.success(pointsGoodsService.applyRefund(param, StpKit.USER.getLoginIdAsLong()));
}
/**
* 取消退单
*/
@PostMapping("/cancelRefund")
public CzgResult<Boolean> cancelRefund(@RequestBody @Validated PointGoodsRefundDTO param) {
return CzgResult.success(pointsGoodsService.cancelRefund(param, StpKit.USER.getLoginIdAsLong()));
}
/**
* 我的兑换记录
*/
@GetMapping("/record/page")
public CzgResult<Page<MkPointsGoodsRecord>> getGoodsRecordPage(@RequestParam(defaultValue = "1", required = false) int page,
@RequestParam(defaultValue = "10", required = false) int size,
@RequestParam Long shopId) {
Page<MkPointsGoodsRecord> pages = pointsGoodsService.getGoodsRecordPage(page, size, shopId, StpKit.USER.getLoginIdAsLong());
return CzgResult.success(pages);
}
}

View File

@@ -1,17 +1,24 @@
package com.czg.task;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.czg.market.service.OrderInfoService;
import com.czg.order.entity.CashierCart;
import com.czg.order.entity.OrderInfo;
import com.czg.order.entity.OrderPayment;
import com.czg.order.service.CashierCartService;
import com.czg.order.service.OrderPaymentService;
import com.czg.service.order.enums.OrderStatusEnums;
import com.czg.service.order.service.PayService;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.List;
/**
* 订单定时任务
*
@@ -25,6 +32,10 @@ public class OTimeTask {
private OrderInfoService orderInfoService;
@Resource
private CashierCartService cartService;
@Resource
private OrderPaymentService orderPaymentService;
@Resource
private PayService payService;
/**
* order 过期
@@ -44,4 +55,22 @@ public class OTimeTask {
});
cartService.remove(cartUpdateWrapper);
}
/**
* 退钱补偿
*/
@Scheduled(cron = "0 50 23 * * ? ")
public void refundCompensate() {
LocalDateTime tenMinutesAgo = LocalDateTime.now().minusMinutes(10);
LocalDateTime thirdDayAgo = LocalDateTime.now().minusDays(3);
List<OrderPayment> list = orderPaymentService.list(QueryWrapper.create()
.gt(OrderPayment::getUpdateTime, thirdDayAgo)
.lt(OrderPayment::getUpdateTime, tenMinutesAgo)
.eq(OrderPayment::getPayType, "refund")
.ne(OrderPayment::getPayStatus, "success"));
for (OrderPayment payment : list) {
String refPayOrderNo = "REP" + IdUtil.getSnowflakeNextId();
payService.unifyRefund(payment, refPayOrderNo);
}
}
}

View File

@@ -1,75 +0,0 @@
package com.czg.account.dto.points;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 会员积分变动记录
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@Data
public class MemberPointsLogDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* id
*/
private Long id;
/**
* 店铺id
*/
private Long shopId;
/**
* 用户id(shopUserId)
*/
private Long userId;
/**
* 摘要信息(如:兑换某个商品/消费多少钱/充值多少钱/新会员赠送积分等)
*/
private String content;
/**
* 订单编号
*/
private String orderNo;
/**
* 浮动类型 add-累加 subtract-扣减
*/
private String floatType;
/**
* 浮动积分(非0正负数)
*/
private Integer floatPoints;
/**
* 创建时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
/**
* 用户昵称
*/
private String nickName;
/**
* 用户头像
*/
private String headImg;
/**
* 用户手机号码
*/
private String phone;
/**
* 查询开始日期 yyyy-MM-dd
*/
private String beginDate;
/**
* 查询结束日期 yyyy-MM-dd
*/
private String endDate;
}

View File

@@ -1,71 +0,0 @@
package com.czg.account.dto.points;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 获取会员可用积分
*
* @author tankaikai
* @since 2024-10-26 11:40
*/
@Data
public class OrderDeductionPointsDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 本单最多可抵扣多少积分
*/
private Integer maxUsablePoints;
/**
* 根据策略计算出的最少可以抵扣的金额
*/
private BigDecimal minDeductionAmount;
/**
* 根据策略计算出的最多可以抵扣的金额
*/
private BigDecimal maxDeductionAmount;
/**
* 下单实付抵扣门槛(实付金额不低于这个值)
*/
private BigDecimal minPaymentAmount;
/**
* 下单积分抵扣门槛(每次使用不低于这个值)
*/
private Integer minDeductionPoints;
/**
* 会员账户积分
*/
private Integer accountPoints;
/**
* 订单金额 (扣除各类折扣)
*/
private BigDecimal orderAmount;
/**
* 使用的积分数量
*/
private Integer usedPoints;
/**
* 实际抵扣的金额
*/
private BigDecimal deductionAmount;
/**
* 下单抵扣积分比例 1元=?积分
*/
private Integer equivalentPoints;
/**
* 不可抵扣原因
*/
private String unusableReason;
/**
* 是否可用 1-可用 0-不可用
*/
private Boolean usable;
}

View File

@@ -1,93 +0,0 @@
package com.czg.account.dto.points;
import com.alibaba.fastjson2.annotation.JSONField;
import com.czg.validator.group.DefaultGroup;
import com.czg.validator.group.InsertGroup;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 积分基本设置
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@Data
public class PointsBasicSettingDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 店铺id
*/
@NotNull(message = "店铺id不能为空", groups = InsertGroup.class)
private Long shopId;
/**
* 开启消费赠送积分 1-开启 0-关闭
*/
@NotNull(message = "开启消费赠送积分不能为空", groups = DefaultGroup.class)
private Integer enableRewards;
/**
* 赠积分适用群体 all-全部 vip-仅会员
*/
@NotNull(message = "赠积分适用群体不能为空", groups = DefaultGroup.class)
private String rewardsGroup;
/**
* 每消费xx元赠送1积分
*/
@NotNull(message = "每消费xx元赠送1积分不能为空", groups = DefaultGroup.class)
private BigDecimal consumeAmount;
/**
* 开启下单积分抵扣 1-开启 0-关闭
*/
@NotNull(message = "开启下单积分抵扣不能为空", groups = DefaultGroup.class)
private Integer enableDeduction;
/**
* 抵扣适用群体 all-全部 vip-仅会员
*/
@NotBlank(message = "抵扣适用群体不能为空", groups = DefaultGroup.class)
private String deductionGroup;
/**
* 下单实付抵扣门槛
*/
@NotNull(message = "下单实付抵扣门槛不能为空", groups = DefaultGroup.class)
@DecimalMin(value = "0", inclusive = false, message = "下单实付抵扣门槛不能小于0")
private BigDecimal minPaymentAmount;
/**
* 下单最高抵扣比例
*/
@NotNull(message = "下单最高抵扣比例不能为空", groups = DefaultGroup.class)
@DecimalMin(value = "0", inclusive = false, message = "下单最高抵扣比例不能小于等于0")
@DecimalMax(value = "100", message = "下单最高抵扣比例不能大于100")
private BigDecimal maxDeductionRatio;
/**
* 下单抵扣积分比例 1元=?积分
*/
@NotNull(message = "下单抵扣积分比例不能为空", groups = DefaultGroup.class)
private Integer equivalentPoints;
/**
* 开启积分商城
*/
@NotNull(message = "开启积分商城不能为空", groups = DefaultGroup.class)
private Integer enablePointsMall;
/**
* 浏览模式 list-列表 grid-宫格
*/
@NotBlank(message = "浏览模式不能为空", groups = DefaultGroup.class)
private String browseMode;
/**
* 创建时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
}

View File

@@ -1,124 +0,0 @@
package com.czg.account.dto.points;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 积分兑换记录
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
@Data
public class PointsExchangeRecordDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* id
*/
private Long id;
/**
* 订单编号
*/
private String orderNo;
/**
* 店铺id
*/
private Long shopId;
/**
* 积分商品id
*/
private Long pointsGoodsId;
/**
* 积分商品名称
*/
private String pointsGoodsName;
/**
* 商品图片URL
*/
private String goodsImageUrl;
/**
* 领取方式 self-自取 post-邮寄
*/
private String pickupMethod;
/**
* 用户id
*/
private Long userId;
/**
* 用户昵称
*/
private String nickName;
/**
* 手机号码
*/
private String mobile;
/**
* 会员头像
*/
private String avatarUrl;
/**
* 消耗积分
*/
private Integer spendPoints;
/**
* 额外支付
*/
private BigDecimal extraPaymentAmount;
/**
* 兑换券券码
*/
private String couponCode;
/**
* 支付平台订单号
*/
private String payOrderId;
/**
* 渠道订单号(微信/支付宝订单号)
*/
private String channelTradeNo;
/**
* 支付方式 积分支付/积分+微信/积分+支付宝
*/
private String payMethod;
/**
* 支付类型 POINTS-积分 WECHAT-微信 ALIPAY-支付宝 UNIONPAY-银联云闪付
*/
private String payType;
/**
* 状态 unpaid-待支付 waiting-待自取 done-已完成 cancel-已取消
*/
private String status;
/**
* 取消/退款原因
*/
private String cancelOrRefundReason;
/**
* 取消/退款时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime cancelOrRefundTime;
/**
* 实际支付时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime payTime;
/**
* 创建时间(下单时间)
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
/**
* 更新时间(核销时间)
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
}

View File

@@ -1,74 +0,0 @@
package com.czg.account.entity;
import com.alibaba.fastjson2.annotation.JSONField;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 会员积分
*
* @author Tankaikai tankaikai@aliyun.com
* @since 2.0 2024-10-25
*/
@Data
@Table("tb_shop_user")
public class MemberPoints implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 会员id
*/
@Id(keyType = KeyType.Auto)
@Column(value = "id")
private Long shopUserId;
/**
* 店铺id
*/
private Long sourceShopId;
private Long mainShopId;
/**
* 会员名称
*/
@Column("nick_name")
private String nickName;
/**
* 会员头像
*/
@Column("head_img")
private String headImg;
/**
* 手机号码
*/
@Column("phone")
private String phone;
/**
* 账户积分
*/
@Column("account_points")
private Integer accountPoints;
/**
* 最近一次积分变动时间
*/
@Column("last_points_change_time")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime lastPointsChangeTime;
/**
* 最近一次浮动积分
*/
@Column("last_float_points")
private Integer lastFloatPoints;
/**
* 是否会员 1-是 0-否
*/
@Column("is_vip")
private Integer vip;
}

View File

@@ -1,29 +0,0 @@
package com.czg.account.enums;
/**
* 抵扣适用群体枚举
*
* @author tankaikai
* @since 2025-02-25 17:50
*/
public enum PointUserGroupEnum {
/**
* 全部用户
*/
ALL("all"),
/**
* 仅会员
*/
VIP("vip");
private String value;
PointUserGroupEnum(String value) {
this.value = value;
}
public String value() {
return this.value;
}
}

View File

@@ -1,36 +0,0 @@
package com.czg.account.enums;
/**
* 积分兑换状态枚举
*
* @author tankaikai
* @since 2025-02-25 15:37
*/
public enum PointsExchangeStatusEnum {
/**
* 待支付
*/
UNPAID("unpaid"),
/**
* 待自取
*/
WAITING("waiting"),
/**
* 已完成
*/
DONE("done"),
/**
* 已取消
*/
CANCEL("cancel");
private String value;
PointsExchangeStatusEnum(String value) {
this.value = value;
}
public String value() {
return this.value;
}
}

View File

@@ -1,50 +0,0 @@
package com.czg.account.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
import java.util.List;
/**
* 积分兑换订单支付类型枚举
*
* @author tankaikai
* @since 2025-02-26 10:29
*/
@Getter
@RequiredArgsConstructor
public enum PointsOrderPayTypeEnum {
/**
* 积分
*/
POINTS("POINTS", "积分支付"),
/**
* 积分+微信
*/
WECHAT("WECHAT", "积分+微信"),
/**
* 积分+支付宝
*/
ALIPAY("ALIPAY", "积分+支付宝"),
;
private final String value;
private final String text;
public static List<String> getValues() {
return Arrays.stream(values()).map(PointsOrderPayTypeEnum::getValue).toList();
}
public static String getText(String value) {
PointsOrderPayTypeEnum item = Arrays.stream(values()).filter(obj -> value.equals(obj.getValue())).findFirst().orElse(null);
if (item != null) {
return item.getText();
}
return null;
}
}

View File

@@ -1,28 +0,0 @@
package com.czg.account.param;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 消费赠送积分入参
*
* @author tankaikai
* @since 2025-02-25 18:20
*/
@Data
public class ConsumeAwardPointsParam implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 用户ID(shopUserId)
*/
private Long shopUserId;
/**
* 订单ID
*/
private Long orderId;
}

View File

@@ -1,28 +0,0 @@
package com.czg.account.param;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 会员积分查询入参
*
* @author tankaikai
* @since 2025-02-25 17:18
*/
@Data
public class MemberPointsParam implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 用户昵称
*/
private String nickName;
/**
* 手机号
*/
private String phone;
}

View File

@@ -1,36 +0,0 @@
package com.czg.account.param;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 支付完成扣减积分入参
*
* @author tankaikai
* @since 2025-02-25 18:07
*/
@Data
public class PayedDeductPointsParam implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 用户ID(shopUserId)
*/
private Long shopUserId;
/**
* 消耗积分
*/
private Integer points;
/**
* 摘要信息(如:兑换某个商品/消费多少钱/充值多少钱/新会员赠送积分等)
*/
private String content;
/**
* 订单ID可为空
*/
private Long orderId;
}

View File

@@ -1,28 +0,0 @@
package com.czg.account.param;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 积分兑换 - 取消/退款入参
*
* @author tankaikai
* @since 2025-02-25 15:29
*/
@Data
public class PointsExchangeCfParam implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* id
*/
private Long id;
/**
* 取消或退款原因
*/
private String cancelOrRefundReason;
}

View File

@@ -1,44 +0,0 @@
package com.czg.account.param;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 兑换记录查询入侵
*
* @author tankaikai
* @since 2025-02-25 14:08
*/
@Data
public class PointsExchangeRecordParam implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 查询关键字
*/
private String keywords;
/**
* 查询开始日期 格式 yyyy-MM-dd
*/
private String beginDate;
/**
* 查询结束日期 格式 yyyy-MM-dd
*/
private String endDate;
/**
* 领取方式 self-自取 post-邮寄
*/
private String pickupMethod;
/**
* 状态 unpaid-待支付 waiting-待自取 done-已完成 cancel-已取消
*/
private String status;
/**
* 店铺用户id
*/
private Long shopUserId;
}

View File

@@ -1,67 +0,0 @@
package com.czg.account.param;
import com.alibaba.fastjson2.annotation.JSONField;
import com.czg.validator.group.InsertGroup;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 积分订单创建入参
*
* @author tankaikai
* @since 2025-02-26 10:03
*/
@Data
public class PointsOrderCreateParam implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 店铺id
*/
@JSONField(serialize = false)
@NotNull(message = "店铺id不能为空", groups = InsertGroup.class)
private Long shopId;
/**
* 积分商品id
*/
@NotNull(message = "积分商品id不能为空", groups = InsertGroup.class)
private Long pointsGoodsId;
/**
* 领取方式 self-自取 post-邮寄
*/
@NotBlank(message = "领取方式不能为空", groups = InsertGroup.class)
private String pickupMethod;
/**
* 用户id
*/
@NotNull(message = "用户id不能为空", groups = InsertGroup.class)
private Long userId;
/**
* 用户昵称
*/
@NotBlank(message = "用户昵称不能为空", groups = InsertGroup.class)
private String nickName;
/**
* 手机号码
*/
private String mobile;
/**
* 会员头像
*/
private String avatarUrl;
/**
* 积分商品名称
*/
@JSONField(serialize = false)
private String pointsGoodsName;
/**
* 商品图片URL
*/
@JSONField(serialize = false)
private String goodsImageUrl;
}

View File

@@ -1,37 +0,0 @@
package com.czg.account.param;
import com.czg.validator.group.DefaultGroup;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 积分订单支付入参
*
* @author tankaikai
* @since 2025-02-26 10:03
*/
@Data
public class PointsOrderPayParam implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 订单id(积分兑换记录id)
*/
@NotNull(message = "订单id不能为空", groups = DefaultGroup.class)
private Long id;
/**
* 支付类型 POINTS-积分 WECHAT-微信 ALIPAY-支付宝 UNIONPAY-银联云闪付
*/
@NotBlank(message = "支付类型不能为空", groups = DefaultGroup.class)
private String payType;
/**
* 微信openId/支付完userId
*/
@NotBlank(message = "openId不能为空", groups = DefaultGroup.class)
private String openId;
}

View File

@@ -1,22 +0,0 @@
package com.czg.account.service;
import com.czg.account.dto.points.MemberPointsLogDTO;
import com.czg.account.entity.MemberPointsLog;
import com.czg.account.vo.PointsShopListVO;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.service.IService;
import java.util.List;
/**
* 会员积分变动记录
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
public interface MemberPointsLogService extends IService<MemberPointsLog> {
Page<MemberPointsLogDTO> getMemberPointsLogPage(MemberPointsLogDTO param);
List<PointsShopListVO> getList(long userId, String shopName);
}

View File

@@ -1,100 +0,0 @@
package com.czg.account.service;
import com.czg.account.dto.points.OrderDeductionPointsDTO;
import com.czg.account.entity.MemberPoints;
import com.czg.account.param.MemberPointsParam;
import com.czg.order.entity.OrderInfo;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.service.IService;
import java.math.BigDecimal;
/**
* 会员积分
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
public interface MemberPointsService extends IService<MemberPoints> {
/**
* 会员积分分页
*
* @param param 查询参数
* @return 分页结果
*/
Page<MemberPoints> getMemberPointsPage(MemberPointsParam param);
/**
* 初始化会员积分
*
* @param shopUserId 用户id
* @return 会员积分等信息
*/
MemberPoints initMemberPoints(Long shopUserId);
/**
* 获取会员积分等信息
*
* @param shopUserId 用户id
* @return 会员积分等信息
*/
MemberPoints getMemberPoints(Long shopUserId);
/**
* 根据用户id及订单金额计算可抵扣积分及可抵扣金额
*
* @param shopUserId 用户id
* @param orderAmount 订单金额
* @return 积分及金额
*/
OrderDeductionPointsDTO getMemberUsablePoints(Long shopUserId, BigDecimal orderAmount);
/**
* 根据抵扣金额计算抵扣积分
*
* @param shopUserId 用户id
* @param orderAmount 订单金额
* @param deductionAmount 抵扣金额
*/
int calcUsedPoints(Long shopUserId, BigDecimal orderAmount, BigDecimal deductionAmount);
/**
* 根据抵扣积分计算抵扣金额
*
* @param shopUserId 用户id
* @param orderAmount 订单金额
* @param points 抵扣积分
*/
BigDecimal calcDeductionAmount(Long shopUserId, BigDecimal orderAmount, int points);
/**
* 扣除积分
*
* @param shopUserId 用户id
* @param points 积分
* @param content 摘要信息(如:兑换积分商品/积分抵扣账单/消费赠送积分/新会员送积分/储值赠送积分)
* @param orderInfo 订单信息,可以为空
*/
boolean deductPoints(Long shopUserId, int points, String content, OrderInfo orderInfo);
/**
* 追加积分
*
* @param shopUserId 用户id
* @param points 积分
* @param content 摘要信息(如:兑换积分商品/积分抵扣账单/消费赠送积分/新会员送积分/储值赠送积分)
* @param orderInfo 订单信息,可以为空
* @return 成功/失败
*/
boolean addPoints(Long shopUserId, int points, String content, OrderInfo orderInfo);
/**
* 消费赠送积分
*
* @param shopUserId 用户id
* @param orderInfo 订单信息,可以为空
*/
void consumeAwardPoints(Long shopUserId, OrderInfo orderInfo);
}

View File

@@ -1,19 +0,0 @@
package com.czg.account.service;
import com.mybatisflex.core.service.IService;
import com.czg.account.entity.PointsBasicSetting;
/**
* 积分基本设置 服务层。
*
* @author ww
* @since 2025-02-20
*/
public interface PointsBasicSettingService extends IService<PointsBasicSetting> {
/**
* 初始化∫配置信息
* @param id 店铺id
*/
void initInfo(Long id);
}

View File

@@ -1,87 +0,0 @@
package com.czg.account.service;
import com.czg.account.dto.points.PointsExchangeRecordDTO;
import com.czg.account.entity.PointsExchangeRecord;
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.vo.PointsExchangeSummaryVo;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.service.IService;
/**
* 积分兑换记录
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
public interface PointsExchangeRecordService extends IService<PointsExchangeRecord> {
/**
* 获取积分兑换记录分页
*
* @param param 查询参数
* @return 分页数据
*/
Page<PointsExchangeRecordDTO> getPointsExchangeRecordPage(PointsExchangeRecordParam param);
/**
* 获取积分兑换记录详情
*
* @param id 兑换记录id
* @return 兑换记录详情
*/
PointsExchangeRecordDTO getPointsExchangeRecordById(Long id);
/**
* 核销券码
*
* @param couponCode 券码
*/
void checkout(String couponCode);
/**
* 创建兑换订单
*
* @param param 兑换入参
* @return 兑换详情
*/
PointsExchangeRecordDTO create(PointsOrderCreateParam param);
/**
* 支付兑换订单
*
* @param param 支付入参
* @return 支付结果
*/
PointsExchangeRecordDTO pay(PointsOrderPayParam param);
/**
* 取消
*
* @param param 兑换记录
*/
void cancel(PointsExchangeCfParam param);
/**
* 退款
*
* @param param 兑换记录
*/
void refund(PointsExchangeCfParam param);
/**
* 统计兑换记录
*
* @param param 查询参数
* @return 统计结果
*/
PointsExchangeSummaryVo total(PointsExchangeRecordParam param);
/**
* 自动取消
*/
void authCancel();
}

View File

@@ -1,22 +0,0 @@
package com.czg.account.service;
import com.czg.account.dto.points.PointsGoodsSettingDTO;
import com.czg.account.entity.PointsGoodsSetting;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.service.IService;
/**
* 积分商品设置
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
*/
public interface PointsGoodsSettingService extends IService<PointsGoodsSetting> {
/**
* 获取积分商品设置分页
*
* @param param 查询参数
* @return 分页结果
*/
Page<PointsGoodsSettingDTO> getPointsGoodsSettingPage(PointsGoodsSettingDTO param);
}

View File

@@ -8,6 +8,7 @@ import lombok.experimental.Accessors;
public class PointsShopListVO {
private String shopName;
private String logo;
private String coverImg;
private Long shopId;
private Integer accountPoints;
}

View File

@@ -0,0 +1,81 @@
package com.czg.market.dto;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import com.alibaba.fastjson2.annotation.JSONField;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotNull;
import lombok.experimental.Accessors;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 积分基本设置 实体类。
*
* @author ww
* @since 2025-12-08
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class MkPointsConfigDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 店铺id
*/
private Long shopId;
/**
* 开启消费赠送积分 1-开启 0-关闭 开启后,所有用户可通过消费获得积分及可用积分抵扣支付
*/
@NotNull(message = "开启消费开关参数错误")
private Integer enableRewards;
/**
* 每消费xx元赠送1积分
*/
@NotNull(message = "消费送积分参数错误")
@DecimalMin(value = "0", inclusive = false, message = "消费送积分金额必须大于0")
private BigDecimal consumeAmount;
/**
* 下单实付抵扣门槛
*/
@NotNull(message = "抵扣门槛参数错误")
private BigDecimal minPaymentAmount;
/**
* 下单最高抵扣比例
*/
@NotNull(message = "抵扣比例参数错误")
@DecimalMin(value = "0", inclusive = false, message = "抵扣比例必须大于0")
private BigDecimal maxDeductionRatio;
/**
* 下单抵扣积分比例 1元=?积分
*/
@NotNull(message = "积分比例错误")
@DecimalMin(value = "0", inclusive = false, message = "积分比例必须大于0")
private Integer equivalentPoints;
/**
* 开启积分商城
*/
@NotNull(message = "积分商城开关参数错误")
private Integer enablePointsMall;
/**
* 创建时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
}

View File

@@ -1,27 +1,34 @@
package com.czg.account.dto.points;
import com.alibaba.fastjson2.annotation.JSONField;
import com.czg.validator.group.DefaultGroup;
import com.czg.validator.group.InsertGroup;
import com.czg.validator.group.UpdateGroup;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import lombok.Data;
package com.czg.market.dto;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import com.alibaba.fastjson2.annotation.JSONField;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.NonNull;
import lombok.experimental.Accessors;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 积分商品设置
* 积分商品设置 实体类
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
* @author ww
* @since 2025-12-08
*/
@Data
public class PointsGoodsSettingDTO implements Serializable {
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class MkPointsGoodsDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@@ -29,71 +36,97 @@ public class PointsGoodsSettingDTO implements Serializable {
/**
* id
*/
@Null(message = "ID必须为空", groups = InsertGroup.class)
@NotNull(message = "ID不能为空", groups = UpdateGroup.class)
private Long id;
/**
* 店铺id
*/
@NotNull(message = "店铺id不能为空", groups = DefaultGroup.class)
private Long shopId;
/**
* 商品类型 physical-实物 coupon-优惠劵
* 商品类型 优惠劵 其它商品
*/
@NotBlank(message = "商品类型不能为空", groups = DefaultGroup.class)
@NotBlank(message = "商品类型不能为空")
private String goodsCategory;
/**
* 商品名称
* 优惠券id
*/
@NotBlank(message = "商品名称不能为空", groups = DefaultGroup.class)
private Long couponId;
/**
* 商品名称/优惠券名称
*/
@NotBlank(message = "名称不能为空")
private String goodsName;
/**
* 商品图片URL
*/
private String goodsImageUrl;
/**
* 所需积分
*/
@NotNull(message = "所需积分不能为空", groups = DefaultGroup.class)
@NotNull(message = "所需积分不能为空")
@Min(value = 1, message = "所需积分不能小于1")
private Integer requiredPoints;
/**
* 额外价格
*/
@NotNull(message = "额外价格不能为空", groups = DefaultGroup.class)
private BigDecimal extraPrice;
/**
* 排序(权重)数字越高显示约靠前
*/
@NotNull(message = "排序(权重)不能为空", groups = DefaultGroup.class)
private Integer sort;
/**
* 数量
*/
@NotNull(message = "数量不能为空", groups = DefaultGroup.class)
@NotNull(message = "数量不能为空")
@Min(value = 1, message = "数量不能小于1")
private Integer quantity;
/**
* 排序(权重)数字越高显示约靠前
*/
private Integer sort;
/**
* 是否上架 1- 0-
*/
@NotNull(message = "上架状态不能为空")
private Integer status;
/**
* 领取方式 店内自取系统发放
*/
private String receiveType;
/**
* 限购数量
*/
private Integer limitQuota;
/**
* 商品详情
*/
private String goodsDescription;
/**
* 累计兑换数量
*/
private Integer totalExchangeCount;
/**
* 是否上架 1- 0-
*/
@NotNull(message = "是否上架不能为空", groups = DefaultGroup.class)
private Integer status;
/**
* 创建时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
/**
* 更新时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
/**
* 逻辑删除标志 1- 0-
*/

View File

@@ -0,0 +1,34 @@
package com.czg.market.dto;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import com.alibaba.fastjson2.annotation.JSONField;
import com.czg.BaseQueryParam;
import com.czg.market.entity.MkPointsGoodsRecord;
import lombok.experimental.Accessors;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 积分兑换记录 实体类。
*
* @author ww
* @since 2025-12-08
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class MkPointsGoodsRecordDTO extends MkPointsGoodsRecord implements Serializable {
private String headImg;
private String nickName;
private String phone;
}

View File

@@ -0,0 +1,68 @@
package com.czg.market.dto;
import com.czg.TimeQueryParam;
import com.czg.utils.CzgStrUtils;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 积分兑换记录 实体类。
*
* @author ww
* @since 2025-12-08
*/
@Data
public class MkPointsGoodsRecordQueryDTO extends TimeQueryParam implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 店铺id
*/
private Long shopId;
/**
* 对应 关键词 只查 积分商品名称
*/
private String pointsGoodsName;
/**
* 商品类型 优惠劵 其它商品
*/
private String goodsCategory;
/**
* 领取方式 店内自取、系统发放
*/
private String receiveType;
/**
* 会员id
*/
private Long shopUserId;
/**
* 状态 待支付 待核销 已完成 退款中 已退款
*/
private String status;
public String getPointsGoodsName() {
return CzgStrUtils.getStrOrNull(pointsGoodsName);
}
public String getGoodsCategory() {
return CzgStrUtils.getStrOrNull(goodsCategory);
}
public String getReceiveType() {
return CzgStrUtils.getStrOrNull(receiveType);
}
public String getStatus() {
return CzgStrUtils.getStrOrNull(status);
}
}

View File

@@ -0,0 +1,77 @@
package com.czg.market.dto;
import java.io.Serializable;
import java.time.LocalDateTime;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.experimental.Accessors;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 会员积分账户 实体类。
*
* @author ww
* @since 2025-12-08
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class MkPointsUserDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* id
*/
private Long id;
/**
* 用户Id
*/
private Long userId;
/**
* 店铺id
*/
private Long shopId;
/**
* 店铺用户id
*/
private Long shopUserId;
/**
* 头像
*/
private String headImg;
/**
* 昵称
*/
private String nickName;
/**
* 手机号
*/
private String phone;
/**
* 积分余额
*/
private Long pointBalance;
/**
* 创建时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
/**
* 更新时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
}

View File

@@ -1,30 +1,31 @@
package com.czg.account.entity;
package com.czg.market.entity;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 积分基本设置 实体类
*
* @author ww
* @since 2025-02-20
* @since 2025-12-08
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("tb_points_basic_setting")
public class PointsBasicSetting implements Serializable {
@Table("mk_points_config")
public class MkPointsConfig implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@@ -32,34 +33,19 @@ public class PointsBasicSetting implements Serializable {
/**
* 店铺id
*/
@Id(keyType = KeyType.None)
@Id
private Long shopId;
/**
* 开启消费赠送积分 1-开启 0-关闭
* 开启消费赠送积分 1-开启 0-关闭 开启后所有用户可通过消费获得积分及可用积分抵扣支付
*/
private Integer enableRewards;
/**
* 赠积分适用群体 all-全部 vip-仅会员
*/
private String rewardsGroup;
/**
* 每消费xx元赠送1积分
*/
private BigDecimal consumeAmount;
/**
* 开启下单积分抵扣 1-开启 0-关闭
*/
private Integer enableDeduction;
/**
* 抵扣适用群体 all-全部 vip-仅会员
*/
private String deductionGroup;
/**
* 下单实付抵扣门槛
*/
@@ -80,15 +66,10 @@ public class PointsBasicSetting implements Serializable {
*/
private Integer enablePointsMall;
/**
* 浏览模式 list-列表 grid-宫格
*/
private String browseMode;
/**
* 创建时间
*/
@Column(onInsertValue = "now()", onUpdateValue = "now()")
@Column(onInsertValue = "now()")
private LocalDateTime createTime;
}

View File

@@ -1,26 +1,32 @@
package com.czg.account.entity;
package com.czg.market.entity;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import com.mybatisflex.core.keygen.KeyGenerators;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 积分商品设置
* 积分商品设置 实体类
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
* @author ww
* @since 2025-12-08
*/
@Data
@Table("tb_points_goods_setting")
public class PointsGoodsSetting implements Serializable {
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("mk_points_goods")
public class MkPointsGoods implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@@ -28,65 +34,97 @@ public class PointsGoodsSetting implements Serializable {
/**
* id
*/
@Id(keyType = KeyType.Generator, value = KeyGenerators.snowFlakeId)
@Id(keyType = KeyType.Auto)
private Long id;
/**
* 店铺id
*/
private Long shopId;
/**
* 商品类型 physical-实物 coupon-优惠劵
* 商品类型 优惠劵 其它商品
*/
private String goodsCategory;
/**
* 优惠券id
*/
private Long couponId;
/**
* 商品名称
*/
private String goodsName;
/**
* 商品图片URL
*/
private String goodsImageUrl;
/**
* 所需积分
*/
private Integer requiredPoints;
/**
* 额外价格
*/
private BigDecimal extraPrice;
/**
* 排序(权重)数字越高显示约靠前
*/
private Integer sort;
/**
* 数量
*/
private Integer quantity;
/**
* 商品详情
* 排序(权重)数字越高显示约靠前
*/
private String goodsDescription;
/**
* 累计兑换数量
*/
private Integer totalExchangeCount;
private Integer sort;
/**
* 是否上架 1- 0-
*/
private Integer status;
/**
* 领取方式 店内自取系统发放
*/
private String receiveType;
/**
* 限购数量
*/
private Integer limitQuota;
/**
* 已购数量
*/
@Column(ignore = true)
private Integer boughtCount;
/**
* 商品详情
*/
private String goodsDescription;
/**
* 累计兑换数量
*/
private Integer totalExchangeCount;
/**
* 创建时间
*/
@Column(onInsertValue = "now()")
private LocalDateTime createTime;
/**
* 更新时间
*/
@Column(onInsertValue = "now()", onUpdateValue = "now()")
private LocalDateTime updateTime;
/**
* 逻辑删除标志 1- 0-
*/
@Column(onInsertValue = "0")
private Integer delFlag;
}

View File

@@ -1,26 +1,32 @@
package com.czg.account.entity;
package com.czg.market.entity;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import com.mybatisflex.core.keygen.KeyGenerators;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 积分兑换记录
* 积分兑换记录 实体类
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
* @author ww
* @since 2025-12-08
*/
@Data
@Table("tb_points_exchange_record")
public class PointsExchangeRecord implements Serializable {
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("mk_points_goods_record")
public class MkPointsGoodsRecord implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@@ -28,110 +34,115 @@ public class PointsExchangeRecord implements Serializable {
/**
* id
*/
@Id(keyType = KeyType.Generator, value = KeyGenerators.snowFlakeId)
@Id(keyType = KeyType.Auto)
private Long id;
/**
* 订单编号
*/
private String orderNo;
/**
* 店铺id
*/
private Long shopId;
/**
* 积分商品id
*/
private Long pointsGoodsId;
/**
* 积分商品名称
*/
private String pointsGoodsName;
/**
* 商品图片URL
*/
private String goodsImageUrl;
/**
* 领取方式 self-自取 post-邮寄
* 商品类型 优惠劵 其它商品
*/
private String pickupMethod;
private String goodsCategory;
/**
* 店铺用户id
* 领取方式 店内自取系统发放
*/
private String receiveType;
/**
* 会员id
*/
private Long shopUserId;
/**
* 用户昵称
* 用户id
*/
private String nickName;
private Long userId;
/**
* 手机号码
* 数量
*/
private String mobile;
/**
* 会员头像
*/
private String avatarUrl;
private int number;
/**
* 消耗积分
*/
private Integer spendPoints;
/**
* 额外支付
*/
private BigDecimal extraPaymentAmount;
/**
* 兑换券券码
*/
private String couponCode;
/**
* 支付平台订单号
*/
private String payOrderId;
private Long payOrderId;
/**
* 渠道订单号(微信/支付宝订单号)
*/
private String channelTradeNo;
/**
* 支付方式 积分支付/积分+微信/积分+支付宝
*/
private String payMethod;
/**
* 支付类型 POINTS-积分 WECHAT-微信 ALIPAY-支付宝 UNIONPAY-银联云闪付
*/
private String payType;
/**
* 状态 unpaid-待支付 waiting-待自取 done-已完成 cancel-已取消
* 状态 待支付 待核销 已完成 退款中 已退款
*/
private String status;
/**
* 取消/退款原因
*/
private String cancelOrRefundReason;
/**
* 取消/退款时间
*/
private LocalDateTime cancelOrRefundTime;
/**
* 实际支付时间
*/
private LocalDateTime payTime;
/**
* 创建时间下单时间
*/
@Column(onInsertValue = "now()")
private LocalDateTime createTime;
/**
* 更新时间核销时间
* 更新时间
*/
@Column(onInsertValue = "now()", onUpdateValue = "now()")
private LocalDateTime updateTime;
/**
* 次数
* 核销时间
*/
@Column(ignore = true)
private Long count;
private LocalDateTime checkoutTime;
/**
* 总额
* 是否删除 0否 1是
*/
@Column(ignore = true)
private BigDecimal totalAmount;
private Integer isDel;
}

View File

@@ -1,24 +1,31 @@
package com.czg.account.entity;
package com.czg.market.entity;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 会员积分变动记录
* 会员积分账户 实体类
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
* @author ww
* @since 2025-12-08
*/
@Data
@Table("tb_member_points_log")
public class MemberPointsLog implements Serializable {
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("mk_points_user")
public class MkPointsUser implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@@ -28,33 +35,37 @@ public class MemberPointsLog implements Serializable {
*/
@Id(keyType = KeyType.Auto)
private Long id;
/**
* 用户Id
*/
private Long userId;
/**
* 店铺id
*/
private Long shopId;
/**
* 店铺用户id
*/
private Long shopUserId;
/**
* 摘要信息兑换某个商品/消费多少钱/充值多少钱/新会员赠送积分等
* 积分余额
*/
private String content;
/**
* 订单编号
*/
private String orderNo;
/**
* 浮动类型 add-累加 subtract-扣减
*/
private String floatType;
/**
* 浮动积分(非0正负数)
*/
private Integer floatPoints;
private Long pointBalance;
/**
* 创建时间
*/
@Column(onInsertValue = "now()")
private LocalDateTime createTime;
/**
* 更新时间
*/
@Column(onInsertValue = "now()", onUpdateValue = "now()")
private LocalDateTime updateTime;
}

View File

@@ -1,24 +1,31 @@
package com.czg.account.vo;
package com.czg.market.entity;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 会员积分变动记录
* 会员积分变动记录 实体类
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-25
* @author ww
* @since 2025-12-08
*/
@Data
@Table("tb_member_points_log")
public class MemberPointsLogVO implements Serializable {
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("mk_points_user_record")
public class MkPointsUserRecord implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@@ -28,34 +35,36 @@ public class MemberPointsLogVO implements Serializable {
*/
@Id(keyType = KeyType.Auto)
private Long id;
/**
* 店铺id
* 积分用户Id
*/
private Long shopId;
/**
* 店铺用户id
*/
private Long shopUserId;
private Long mkPointsUserId;
/**
* 摘要信息兑换某个商品/消费多少钱/充值多少钱/新会员赠送积分等
*/
private String content;
/**
* 订单编号
*/
private String orderNo;
private String sourceId;
/**
* 浮动类型 add-累加 subtract-扣减
*/
private String floatType;
/**
* 浮动积分(非0正负数)
*/
private Integer floatPoints;
/**
* 创建时间
*/
@Column(onInsertValue = "now()")
private LocalDateTime createTime;
private String shopName;
}

View File

@@ -0,0 +1,21 @@
package com.czg.market.enums;
/**
* 积分 枚举类
*/
public enum PointsConstant {
ADD("add", "增加"),
SUB("subtract", "扣减");
private final String value;
private final String desc;
PointsConstant(String value, String desc) {
this.value = value;
this.desc = desc;
}
public String getValue() { return value; }
}

View File

@@ -0,0 +1,19 @@
package com.czg.market.service;
import com.czg.order.entity.OrderInfo;
import com.mybatisflex.core.service.IService;
import com.czg.market.entity.MkPointsConfig;
/**
* 积分基本设置 服务层。
*
* @author ww
* @since 2025-12-08
*/
public interface MkPointsConfigService extends IService<MkPointsConfig> {
/**
* 订单支付成功,通过计算 给用户赠送积分
*
*/
void consumeAwardPoints(Long shopUserId, OrderInfo orderInfo);
}

View File

@@ -0,0 +1,23 @@
package com.czg.market.service;
import com.czg.account.vo.PointsExchangeSummaryVo;
import com.czg.market.dto.MkPointsGoodsRecordDTO;
import com.czg.market.dto.MkPointsGoodsRecordQueryDTO;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.service.IService;
import com.czg.market.entity.MkPointsGoodsRecord;
/**
* 积分兑换记录 服务层。
*
* @author ww
* @since 2025-12-08
*/
public interface MkPointsGoodsRecordService extends IService<MkPointsGoodsRecord> {
Page<MkPointsGoodsRecordDTO> getGoodsRecordPage(MkPointsGoodsRecordQueryDTO param);
PointsExchangeSummaryVo total(MkPointsGoodsRecordQueryDTO param);
boolean checkout(String couponCode,Long shopId);
}

View File

@@ -0,0 +1,37 @@
package com.czg.market.service;
import com.czg.BaseQueryParam;
import com.czg.market.entity.MkPointsGoodsRecord;
import com.czg.order.dto.PointGoodsRefundDTO;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.service.IService;
import com.czg.market.entity.MkPointsGoods;
/**
* 积分商品设置 服务层。
*
* @author ww
* @since 2025-12-08
*/
public interface MkPointsGoodsService extends IService<MkPointsGoods> {
Page<MkPointsGoods> getPointsGoodsPage(BaseQueryParam param, Long shopId);
Page<MkPointsGoods> getPointsGoodsPageByUser(Integer page, Integer size, Long shopId, Long userId);
Page<MkPointsGoodsRecord> getGoodsRecordPage(Integer page, Integer size, Long shopId, Long userId);
//用户申请退款
boolean applyRefund(PointGoodsRefundDTO param, Long userId);
boolean cancelRefund(PointGoodsRefundDTO param, Long userId);
/**
* 更新商品数量
*
* @param id 商品id
* @param quantity 库存数量 最终值
* @param totalExchangeCount 累计兑换数量 最终值
*/
boolean upNumberById(Long id, Integer quantity, Integer totalExchangeCount);
}

View File

@@ -0,0 +1,20 @@
package com.czg.market.service;
import com.czg.account.vo.PointsShopListVO;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.service.IService;
import com.czg.market.entity.MkPointsUserRecord;
import java.util.List;
/**
* 会员积分变动记录 服务层。
*
* @author ww
* @since 2025-12-08
*/
public interface MkPointsUserRecordService extends IService<MkPointsUserRecord> {
Page<MkPointsUserRecord> pageByPointsUserId(Integer page, Integer size, Long mkPointsUserId);
}

View File

@@ -0,0 +1,51 @@
package com.czg.market.service;
import com.czg.account.vo.PointsShopListVO;
import com.czg.market.dto.MkPointsUserDTO;
import com.czg.market.enums.PointsConstant;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.service.IService;
import com.czg.market.entity.MkPointsUser;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.util.List;
/**
* 用户积分 服务层。
*
* @author ww
* @since 2025-12-08
*/
public interface MkPointsUserService extends IService<MkPointsUser> {
Page<MkPointsUserDTO> getPointsUserPage(String phone, Integer page, Integer size);
List<PointsShopListVO> pointsShopList(Long userId, String shopName);
/**
* 获取用户积分信息
* 返回的数据ID可能为空 不影响修改用户积分 统一接口的处理
*
* @param shopId 店铺Id
* @param shopUserId 店铺用户Id
* @param userId 会员Id
*/
MkPointsUser getPointsUser(Long shopId, Long shopUserId, Long userId);
/**
* 修改用户积分 统一接口 userId和shopUserId不能同时为空
*
* @param shopId 店铺Id 注意不要直接使用shopUser的ID
* @param floatType {@link com.czg.market.enums.PointsConstant}
* @param points 积分数 正数
* @param reason 事件
* 会员消费送积分
* 消费¥多少 送多少积分
* 积分抵扣账单
*/
Long alterPoints(Long userId, Long shopUserId, @NotNull Long shopId, @NotNull PointsConstant floatType,
@NotNull Integer points, String sourceId, @NotBlank String reason);
}

View File

@@ -0,0 +1,44 @@
package com.czg.order.dto;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
/**
* 积分商品兑换信息
*
* @author ww
*/
@Data
public class PointGoodsExchangeDTO {
/**
* 积分商品id
*/
@NotNull(message = "积分商品不能为空")
private Long pointsGoodsId;
/**
* 店铺Id
*/
@NotNull(message = "未指定店铺")
private Long shopId;
/**
* 兑换数量
*/
@NotNull(message = "兑换数量不能为空")
private int number;
/**
* 兑换价格
*/
private BigDecimal price;
/**
* 支付方式 支付方式aliPay 支付宝wechatPay 微信
*/
private String payType;
/**
* 用户openId
*/
private String openId;
private Long userId;
}

View File

@@ -0,0 +1,29 @@
package com.czg.order.dto;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
/**
* 积分商品退款
*
* @author ww
*/
@Data
public class PointGoodsRefundDTO {
/**
* 积分商品id
*/
@NotNull(message = "记录不能为空")
private Long recordId;
/**
* 订单编号
*/
@NotNull(message = "订单编号不能为空")
private String orderNo;
private String reason;
}

View File

@@ -0,0 +1,24 @@
package com.czg.order.service;
import com.czg.order.dto.PointGoodsExchangeDTO;
import com.czg.order.dto.PointGoodsRefundDTO;
import com.czg.resp.CzgResult;
import java.util.Map;
/**
* 积分商品兑换服务
*
* @author ww
*/
public interface PointsGoodPayService {
//积分商品兑换
CzgResult<Map<String, Object>> exchange(String ip, PointGoodsExchangeDTO param);
//同意退款
boolean applyRefund(PointGoodsRefundDTO param, Long shopId);
//支付回调 进行兑换内容发放
void payCallBack(Long recordId);
}

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>

View File

@@ -0,0 +1,14 @@
package com.czg.service.market.mapper;
import com.mybatisflex.core.BaseMapper;
import com.czg.market.entity.MkPointsConfig;
/**
* 积分基本设置 映射层。
*
* @author ww
* @since 2025-12-08
*/
public interface MkPointsConfigMapper extends BaseMapper<MkPointsConfig> {
}

View File

@@ -0,0 +1,17 @@
package com.czg.service.market.mapper;
import com.mybatisflex.core.BaseMapper;
import com.czg.market.entity.MkPointsGoods;
import java.util.List;
/**
* 积分商品设置 映射层。
*
* @author ww
* @since 2025-12-08
*/
public interface MkPointsGoodsMapper extends BaseMapper<MkPointsGoods> {
List<MkPointsGoods> getPointsGoodsPageByUser(Long shopId, Long userId);
}

View File

@@ -0,0 +1,14 @@
package com.czg.service.market.mapper;
import com.mybatisflex.core.BaseMapper;
import com.czg.market.entity.MkPointsGoodsRecord;
/**
* 积分兑换记录 映射层。
*
* @author ww
* @since 2025-12-08
*/
public interface MkPointsGoodsRecordMapper extends BaseMapper<MkPointsGoodsRecord> {
}

View File

@@ -0,0 +1,14 @@
package com.czg.service.market.mapper;
import com.mybatisflex.core.BaseMapper;
import com.czg.market.entity.MkPointsUser;
/**
* 会员积分账户 映射层。
*
* @author ww
* @since 2025-12-08
*/
public interface MkPointsUserMapper extends BaseMapper<MkPointsUser> {
}

View File

@@ -0,0 +1,14 @@
package com.czg.service.market.mapper;
import com.mybatisflex.core.BaseMapper;
import com.czg.market.entity.MkPointsUserRecord;
/**
* 会员积分变动记录 映射层。
*
* @author ww
* @since 2025-12-08
*/
public interface MkPointsUserRecordMapper extends BaseMapper<MkPointsUserRecord> {
}

View File

@@ -0,0 +1,55 @@
package com.czg.service.market.service.impl;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.czg.market.enums.PointsConstant;
import com.czg.market.service.MkPointsUserService;
import com.czg.order.entity.OrderInfo;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.czg.market.entity.MkPointsConfig;
import com.czg.market.service.MkPointsConfigService;
import com.czg.service.market.mapper.MkPointsConfigMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
/**
* 积分基本设置 服务层实现。
*
* @author ww
* @since 2025-12-08
*/
@Service
public class MkPointsConfigServiceImpl extends ServiceImpl<MkPointsConfigMapper, MkPointsConfig> implements MkPointsConfigService {
@Resource
private MkPointsUserService mkPointsUserService;
@Override
public void consumeAwardPoints(Long shopUserId, OrderInfo orderInfo) {
BigDecimal payAmount = orderInfo.getPayAmount();
if (NumberUtil.isLessOrEqual(payAmount, BigDecimal.ZERO)) {
return;
}
MkPointsConfig pointsConfig = getOne(query().eq(MkPointsConfig::getShopId, orderInfo.getShopId()));
if (pointsConfig == null) {
return;
}
Integer enableRewards = pointsConfig.getEnableRewards();
if (enableRewards == 0) {
return;
}
BigDecimal consumeAmount = pointsConfig.getConsumeAmount();
if (consumeAmount == null) {
return;
}
if (NumberUtil.isLessOrEqual(consumeAmount, BigDecimal.ZERO)) {
return;
}
BigDecimal awardPoints = NumberUtil.roundDown(NumberUtil.div(payAmount, consumeAmount), 0);
mkPointsUserService.alterPoints(null, shopUserId, orderInfo.getShopId(), PointsConstant.ADD,
awardPoints.intValue(), orderInfo.getId().toString(), StrUtil.format("消费¥{}送{}积分", payAmount, awardPoints.intValue()));
}
}

View File

@@ -0,0 +1,85 @@
package com.czg.service.market.service.impl;
import cn.hutool.core.util.NumberUtil;
import com.czg.account.entity.ShopUser;
import com.czg.account.service.ShopInfoService;
import com.czg.account.service.ShopUserService;
import com.czg.account.vo.PointsExchangeSummaryVo;
import com.czg.exception.CzgException;
import com.czg.market.dto.MkPointsGoodsRecordDTO;
import com.czg.market.dto.MkPointsGoodsRecordQueryDTO;
import com.czg.sa.StpKit;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.czg.market.entity.MkPointsGoodsRecord;
import com.czg.market.service.MkPointsGoodsRecordService;
import com.czg.service.market.mapper.MkPointsGoodsRecordMapper;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
/**
* 积分兑换记录 服务层实现。
*
* @author ww
* @since 2025-12-08
*/
@Service
public class MkPointsGoodsRecordServiceImpl extends ServiceImpl<MkPointsGoodsRecordMapper, MkPointsGoodsRecord> implements MkPointsGoodsRecordService {
@DubboReference
private ShopInfoService shopInfoService;
@DubboReference
private ShopUserService shopUserService;
@Override
public Page<MkPointsGoodsRecordDTO> getGoodsRecordPage(MkPointsGoodsRecordQueryDTO param) {
Long mainIdByShopId = shopInfoService.getMainIdByShopId(param.getShopId());
Page<MkPointsGoodsRecordDTO> pages = pageAs(
Page.of(param.getPage(), param.getSize())
, query().eq(MkPointsGoodsRecord::getShopId, param.getShopId())
.eq(MkPointsGoodsRecord::getShopUserId, param.getShopUserId())
.eq(MkPointsGoodsRecord::getStatus, param.getStatus())
.like(MkPointsGoodsRecord::getPointsGoodsName, param.getPointsGoodsName())
.like(MkPointsGoodsRecord::getGoodsCategory, param.getGoodsCategory())
, MkPointsGoodsRecordDTO.class);
pages.getRecords().forEach(data -> {
ShopUser shopUser = shopUserService.getOne(query().eq(ShopUser::getId, data.getShopUserId()).eq(ShopUser::getMainShopId, mainIdByShopId));
data.setHeadImg(shopUser.getHeadImg());
data.setNickName(shopUser.getNickName());
data.setPhone(shopUser.getPhone());
});
return pages;
}
@Override
public PointsExchangeSummaryVo total(MkPointsGoodsRecordQueryDTO param) {
QueryWrapper queryWrapper = query().eq(MkPointsGoodsRecord::getShopId, param.getShopId())
.between(MkPointsGoodsRecord::getCreateTime, param.getStartTime(), param.getEndTime())
.eq(MkPointsGoodsRecord::getShopUserId, param.getShopUserId())
.eq(MkPointsGoodsRecord::getStatus, param.getStatus())
.like(MkPointsGoodsRecord::getPointsGoodsName, param.getPointsGoodsName())
.like(MkPointsGoodsRecord::getGoodsCategory, param.getGoodsCategory());
queryWrapper.select("count(*) as count,sum(extra_payment_amount) as totalAmount");
return getOneAs(queryWrapper, PointsExchangeSummaryVo.class);
}
@Override
public boolean checkout(String couponCode, Long shopId) {
MkPointsGoodsRecord record = getOne(query().eq(MkPointsGoodsRecord::getCouponCode, couponCode).eq(MkPointsGoodsRecord::getShopId, shopId));
if (record == null) {
throw new CzgException("未找到该兑换码");
}
if (!record.getStatus().equals("待核销") && !record.getStatus().equals("退款中")) {
throw new CzgException("核销失败,该商品不可核销");
}
MkPointsGoodsRecord upRecord = new MkPointsGoodsRecord();
upRecord.setCheckoutTime(LocalDateTime.now());
upRecord.setStatus("已完成");
return update(upRecord, query().eq(MkPointsGoodsRecord::getId, record.getId()));
}
}

View File

@@ -0,0 +1,105 @@
package com.czg.service.market.service.impl;
import com.czg.BaseQueryParam;
import com.czg.exception.CzgException;
import com.czg.market.entity.MkPointsGoodsRecord;
import com.czg.market.entity.MkPointsUserRecord;
import com.czg.market.service.MkPointsGoodsRecordService;
import com.czg.order.dto.PointGoodsRefundDTO;
import com.czg.utils.PageUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.czg.market.entity.MkPointsGoods;
import com.czg.market.service.MkPointsGoodsService;
import com.czg.service.market.mapper.MkPointsGoodsMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
/**
* 积分商品设置 服务层实现。
*
* @author ww
* @since 2025-12-08
*/
@Service
public class MkPointsGoodsServiceImpl extends ServiceImpl<MkPointsGoodsMapper, MkPointsGoods> implements MkPointsGoodsService {
@Resource
private MkPointsGoodsRecordService goodsRecordService;
@Override
public Page<MkPointsGoods> getPointsGoodsPage(BaseQueryParam param, Long shopId) {
return page(Page.of(param.getPage(), param.getSize()),
query()
.eq(MkPointsGoods::getShopId, shopId)
.eq(MkPointsGoods::getDelFlag, 0)
.orderBy(MkPointsGoods::getSort, false)
.orderBy(MkPointsGoods::getId, false)
);
}
@Override
public Page<MkPointsGoods> getPointsGoodsPageByUser(Integer page, Integer size, Long shopId, Long userId) {
PageHelper.startPage(page, size);
List<MkPointsGoods> list = mapper.getPointsGoodsPageByUser(shopId, userId);
return PageUtil.convert(new PageInfo<>(list));
}
@Override
public Page<MkPointsGoodsRecord> getGoodsRecordPage(Integer page, Integer size, Long shopId, Long userId) {
return goodsRecordService.page(Page.of(page, size), query().eq(MkPointsGoodsRecord::getUserId, userId).eq(MkPointsGoodsRecord::getShopId, shopId));
}
@Override
public boolean applyRefund(PointGoodsRefundDTO param, Long userId) {
MkPointsGoodsRecord record = new MkPointsGoodsRecord();
record.setStatus("退款中");
record.setCancelOrRefundReason(param.getReason());
record.setCancelOrRefundTime(LocalDateTime.now());
return goodsRecordService.update(record, query()
.eq(MkPointsGoodsRecord::getId, param.getRecordId())
.eq(MkPointsGoodsRecord::getOrderNo, param.getOrderNo())
.eq(MkPointsGoodsRecord::getUserId, userId)
);
}
@Override
public boolean cancelRefund(PointGoodsRefundDTO param, Long userId) {
MkPointsGoodsRecord record1 = goodsRecordService.getById(param.getRecordId());
if (record1 == null) {
throw new CzgException("取消失败,订单不存在");
}
if (record1.getStatus().equals("退款中")) {
throw new CzgException("取消失败,订单不处于退款中");
}
MkPointsGoodsRecord record = new MkPointsGoodsRecord();
if (record1.getCheckoutTime() != null) {
record.setStatus("已完成");
}else {
record.setStatus("待核销");
}
record.setCancelOrRefundReason(param.getReason());
record.setCancelOrRefundTime(LocalDateTime.now());
return goodsRecordService.update(record, query()
.eq(MkPointsGoodsRecord::getId, param.getRecordId())
.eq(MkPointsGoodsRecord::getOrderNo, param.getOrderNo())
.eq(MkPointsGoodsRecord::getUserId, userId)
);
}
@Override
public boolean upNumberById(Long id, Integer quantity, Integer totalExchangeCount) {
MkPointsGoods goodUp = new MkPointsGoods();
goodUp.setQuantity(quantity);
goodUp.setTotalExchangeCount(totalExchangeCount);
return updateById(goodUp);
}
}

View File

@@ -0,0 +1,28 @@
package com.czg.service.market.service.impl;
import com.czg.account.vo.PointsShopListVO;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.czg.market.entity.MkPointsUserRecord;
import com.czg.market.service.MkPointsUserRecordService;
import com.czg.service.market.mapper.MkPointsUserRecordMapper;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 会员积分变动记录 服务层实现。
*
* @author ww
* @since 2025-12-08
*/
@Service
public class MkPointsUserRecordServiceImpl extends ServiceImpl<MkPointsUserRecordMapper, MkPointsUserRecord> implements MkPointsUserRecordService {
@Override
public Page<MkPointsUserRecord> pageByPointsUserId(Integer page, Integer size, Long mkPointsUserId) {
return page(Page.of(page, size), query().eq(MkPointsUserRecord::getMkPointsUserId, mkPointsUserId).orderBy(MkPointsUserRecord::getCreateTime, false));
}
}

View File

@@ -0,0 +1,146 @@
package com.czg.service.market.service.impl;
import cn.hutool.core.util.StrUtil;
import com.czg.account.entity.ShopInfo;
import com.czg.account.entity.ShopUser;
import com.czg.account.service.ShopInfoService;
import com.czg.account.service.ShopUserService;
import com.czg.account.vo.PointsShopListVO;
import com.czg.exception.CzgException;
import com.czg.market.dto.MkPointsUserDTO;
import com.czg.market.entity.MkPointsUserRecord;
import com.czg.market.enums.PointsConstant;
import com.czg.market.service.MkPointsUserRecordService;
import com.czg.sa.StpKit;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.czg.market.entity.MkPointsUser;
import com.czg.market.service.MkPointsUserService;
import com.czg.service.market.mapper.MkPointsUserMapper;
import jakarta.annotation.Resource;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;
import org.stringtemplate.v4.ST;
import java.util.List;
/**
* 会员积分账户 服务层实现。
*
* @author ww
* @since 2025-12-08
*/
@Service
public class MkPointsUserServiceImpl extends ServiceImpl<MkPointsUserMapper, MkPointsUser> implements MkPointsUserService {
@DubboReference
private ShopInfoService shopInfoService;
@DubboReference
private ShopUserService shopUserService;
@Resource
private MkPointsUserRecordService pointsUserRecordService;
@Override
public Page<MkPointsUserDTO> getPointsUserPage(String phone, Integer page, Integer size) {
Long shopUserId = null;
Long mainIdByShopId = shopInfoService.getMainIdByShopId(StpKit.USER.getShopId());
if (StrUtil.isBlankIfStr(phone)) {
ShopUser one = shopUserService.getOne(query().eq(ShopUser::getPhone, phone).eq(ShopUser::getMainShopId, mainIdByShopId));
if (one == null) {
return new Page<>();
}
shopUserId = one.getId();
}
Page<MkPointsUserDTO> pages = pageAs(Page.of(page, size), query()
.eq(MkPointsUser::getShopId, StpKit.USER.getShopId())
.eq(MkPointsUser::getShopUserId, shopUserId)
.orderBy(MkPointsUser::getUpdateTime, false),
MkPointsUserDTO.class);
pages.getRecords().forEach(s -> {
ShopUser shopUser = shopUserService.getOne(query().eq(ShopUser::getId, s.getShopUserId()).eq(ShopUser::getMainShopId, mainIdByShopId));
s.setHeadImg(shopUser.getHeadImg());
s.setNickName(shopUser.getNickName());
s.setPhone(shopUser.getPhone());
});
return pages;
}
@Override
public List<PointsShopListVO> pointsShopList(Long userId, String shopName) {
return listAs(query().select(MkPointsUser::getShopId).select(ShopInfo::getShopName, ShopInfo::getLogo, ShopInfo::getCoverImg)
.eq(MkPointsUser::getUserId, userId).leftJoin(ShopInfo.class).on(MkPointsUser::getShopId, ShopInfo::getId),
PointsShopListVO.class);
}
public MkPointsUser getPointsUser(Long shopId, Long shopUserId, Long userId) {
MkPointsUser pointsUser;
if (userId == null && shopUserId == null) {
throw new CzgException("积分操作失败用户ID和店铺用户ID不能同时为空");
}
if (shopUserId != null) {
pointsUser = getOne(query().eq(MkPointsUser::getShopUserId, shopUserId).eq(MkPointsUser::getShopId, shopId));
if (pointsUser == null) {
ShopUser shopUser = shopUserService.getById(shopUserId);
if (shopUser == null) {
throw new CzgException("积分操作失败,店铺用户不存在");
}
pointsUser = MkPointsUser.builder()
.userId(userId)
.shopId(shopId)
.shopUserId(shopUserId)
.pointBalance(0L)
.build();
}
} else {
pointsUser = getOne(query().eq(MkPointsUser::getUserId, userId).eq(MkPointsUser::getShopId, shopId));
if (pointsUser == null) {
ShopUser shopUser = shopUserService.getUserInfo(shopId, userId);
if (shopUser == null) {
throw new CzgException("积分操作失败,店铺用户不存在");
}
pointsUser = MkPointsUser.builder()
.userId(userId)
.shopId(shopId)
.shopUserId(shopUser.getId())
.pointBalance(0L)
.build();
}
}
return pointsUser;
}
@Override
public Long alterPoints(Long userId, Long shopUserId, Long shopId, PointsConstant floatType, Integer points, String sourceId, String reason) {
MkPointsUser pointsUser = getPointsUser(shopId, shopUserId, userId);
if (floatType == null) {
throw new CzgException("积分变动类型不能为空");
}
switch (floatType) {
case ADD:
pointsUser.setPointBalance(pointsUser.getPointBalance() + points);
break;
case SUB:
pointsUser.setPointBalance(pointsUser.getPointBalance() - points);
if (pointsUser.getPointBalance() < 0) {
throw new CzgException("积分操作失败,积分不足");
}
points = -points;
break;
default:
throw new CzgException("不支持的积分变动类型:" + floatType);
}
saveOrUpdate(pointsUser);
MkPointsUserRecord record = MkPointsUserRecord.builder()
.mkPointsUserId(pointsUser.getId())
.floatType(floatType.getValue())
.floatPoints(points)
.sourceId(sourceId)
.content(reason)
.build();
boolean save = pointsUserRecordService.save(record);
if (!save) {
throw new CzgException("积分操作失败,积分记录保存失败");
}
return record.getId();
}
}

View File

@@ -4,10 +4,8 @@ import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.czg.account.dto.shopuser.ShopUserMoneyEditDTO;
import com.czg.account.entity.ShopConfig;
import com.czg.account.entity.ShopInfo;
import com.czg.account.entity.ShopUser;
import com.czg.account.service.MemberPointsService;
import com.czg.account.service.ShopConfigService;
import com.czg.account.service.ShopInfoService;
import com.czg.account.service.ShopUserService;
@@ -16,29 +14,22 @@ import com.czg.exception.CzgException;
import com.czg.market.dto.CouponInfoDTO;
import com.czg.market.dto.MkRewardCouponDTO;
import com.czg.market.dto.MkShopRechargeDTO;
import com.czg.market.dto.MkShopRechargeDetailDTO;
import com.czg.market.entity.MkShopRechargeDetail;
import com.czg.market.entity.ShopCoupon;
import com.czg.market.service.MkShopCouponRecordService;
import com.czg.market.service.MkShopRechargeDetailService;
import com.czg.market.service.ShopCouponService;
import com.czg.market.enums.PointsConstant;
import com.czg.market.service.*;
import com.czg.market.vo.*;
import com.czg.order.entity.OrderInfo;
import com.czg.service.market.enums.OrderStatusEnums;
import com.czg.utils.AssertUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.czg.market.entity.MkShopRecharge;
import com.czg.market.service.MkShopRechargeService;
import com.czg.service.market.mapper.MkShopRechargeMapper;
import jakarta.annotation.Resource;
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 java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;
@@ -57,8 +48,8 @@ public class MkShopRechargeServiceImpl extends ServiceImpl<MkShopRechargeMapper,
private ShopCouponService shopCouponService;
@DubboReference
private ShopUserService shopUserService;
@DubboReference
private MemberPointsService memberPointsService;
@Resource
private MkPointsUserService pointsUserService;
@Resource
private MkShopCouponRecordService shopCouponRecordService;
@DubboReference
@@ -190,11 +181,11 @@ public class MkShopRechargeServiceImpl extends ServiceImpl<MkShopRechargeMapper,
.setBizEnum(ShopUserFlowBizEnum.AWARD_IN)
.setRelationId(paymentId);
shopUserMoneyEditRewardDTO.setMoney(rechargeDetail.getRewardAmount());
shopUserService.updateMoney(shopUserMoneyEditRewardDTO);
Long editId = shopUserService.updateMoney(shopUserMoneyEditRewardDTO);
// 发放积分
if (rechargeDetail.getRewardPoints() != null) {
memberPointsService.addPoints(shopUserId, rechargeDetail.getRewardPoints(), "会员消费送积分", null);
pointsUserService.alterPoints(null, shopUserId, shopUser.getMainShopId(), PointsConstant.ADD, rechargeDetail.getRewardPoints(), editId.toString(), "会员充值送积分");
}
// 发放优惠券
@@ -205,7 +196,7 @@ public class MkShopRechargeServiceImpl extends ServiceImpl<MkShopRechargeMapper,
.setNum(item.getNum())
.setUserId(shopUser.getUserId())
.setShopId(shopId), "充值赠券");
}catch (Exception e) {
} catch (Exception e) {
log.warn("发放优惠券失败", e);
}
});
@@ -254,7 +245,7 @@ public class MkShopRechargeServiceImpl extends ServiceImpl<MkShopRechargeMapper,
});
});
shopIdList = null;
}else {
} else {
shopIdList.add(shopId);
queryWrapper.in(ShopInfo::getId, shopIdList);
}

View File

@@ -12,6 +12,7 @@ import com.czg.market.dto.*;
import com.czg.account.entity.*;
import com.czg.account.service.*;
import com.czg.market.entity.*;
import com.czg.market.enums.PointsConstant;
import com.czg.market.service.*;
import com.czg.market.vo.*;
import com.czg.order.entity.OrderInfo;
@@ -55,8 +56,8 @@ public class TbMemberConfigServiceImpl extends ServiceImpl<TbMemberConfigMapper,
private OrderInfoService orderInfoService;
@DubboReference
private OrderPaymentService paymentService;
@DubboReference
private MemberPointsService memberPointsService;
@Resource
private MkPointsUserService pointsUserService;
@Resource
private MemberOrderService memberOrderService;
@Resource
@@ -188,7 +189,7 @@ public class TbMemberConfigServiceImpl extends ServiceImpl<TbMemberConfigMapper,
memberConfig.setMemberPriceShopIdList(JSONObject.toJSONString(memberDTO.getMemberPriceShopIdList()));
}
shopConfigService.editStatusByShopIdList(memberConfig.getShopId(), memberConfig.getIsOpen().intValue() == 1 && memberConfig.getIsMemberPrice() == 1 ? 1 : 0,false, "is_member_price",
shopConfigService.editStatusByShopIdList(memberConfig.getShopId(), memberConfig.getIsOpen().intValue() == 1 && memberConfig.getIsMemberPrice() == 1 ? 1 : 0, false, "is_member_price",
"ALL".equals(memberConfig.getMemberPriceShopType()) ? "all" : "part",
StrUtil.isNotBlank(memberConfig.getMemberPriceShopIdList()) ? JSONArray.parseArray(memberConfig.getMemberPriceShopIdList()).toList(Long.class) : new ArrayList<>());
@@ -278,20 +279,6 @@ public class TbMemberConfigServiceImpl extends ServiceImpl<TbMemberConfigMapper,
return memberLevelVOS;
}
static void main() {
Integer expVal = 10;
TableValueConstant.MemberExpFlow.Type type = TableValueConstant.MemberExpFlow.Type.PAY;
long exp = 0;
switch (type) {
case PAY -> {
exp = expVal;
}
}
System.out.println(exp);
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deliver(ShopUser shopUser, TableValueConstant.MemberExpFlow.Type type, BigDecimal money, Integer expVal, Long sourceId) {
@@ -331,7 +318,8 @@ public class TbMemberConfigServiceImpl extends ServiceImpl<TbMemberConfigMapper,
int points = (int) (money.floatValue() / levelVO.getCostRewardPoints());
log.info("消费送积分: {}", points);
if (points > 0) {
memberPointsService.addPoints(shopUser.getId(), points, "会员消费送积分", null);
pointsUserService.alterPoints(null, shopUser.getId(), shopUser.getMainShopId(), PointsConstant.ADD, points, sourceId.toString(), "会员消费送积分");
}
}
}
@@ -345,7 +333,7 @@ public class TbMemberConfigServiceImpl extends ServiceImpl<TbMemberConfigMapper,
if (levelVO.getIsCycleReward() == 1) {
if (levelVO.getCycleRewardPoints() != null) {
int points = levelVO.getCycleRewardPoints();
memberPointsService.addPoints(shopUser.getId(), points, "会员任务奖励", null);
pointsUserService.alterPoints(null, shopUser.getId(), shopUser.getMainShopId(), PointsConstant.ADD, points, sourceId.toString(), "会员奖励");
}
if (levelVO.getCycleRewardCouponList() != null && !levelVO.getCycleRewardCouponList().isEmpty()) {

View File

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

View File

@@ -0,0 +1,17 @@
<?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.market.mapper.MkPointsGoodsMapper">
<select id="getPointsGoodsPageByUser" resultType="com.czg.market.entity.MkPointsGoods">
select goods.*, count(record.id) as boughtCount
from mk_points_goods goods
left join mk_points_user_record record
on record.mk_points_goods_id = goods.id and record.user_id = #{userId} and record.status != '已退款'
where goods.shop_id = #{shopId}
and goods.del_flag = 0
and goods.status = 1
order by goods.sort desc, goods.id desc
</select>
</mapper>

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,8 @@ package com.czg.service.order.service;
import com.czg.entity.resp.CzgBaseResp;
import com.czg.entity.resp.CzgRefundResp;
import com.czg.order.dto.OrderInfoRefundDTO;
import com.czg.order.dto.PointGoodsExchangeDTO;
import com.czg.order.entity.OrderPayment;
import com.czg.resp.CzgResult;
import com.czg.service.order.dto.*;
import lombok.NonNull;
@@ -17,6 +19,8 @@ import java.util.Map;
* @author ww
*/
public interface PayService {
//-----------------------------------------------------------------订单支付--------------------------------------------------------------------
/**
* 挂账
*/
@@ -28,15 +32,10 @@ public interface PayService {
CzgResult<Object> cashPayOrder(OrderPayParamDTO payParam);
/**
* 会员支付
* 余额支付
*/
CzgResult<Object> vipPayOrder(OrderPayParamDTO payParam);
/**
* 充值并付款
*/
CzgResult<Map<String, Object>> rechargePayOrder(String clintIp, OrderPayParamDTO payParam);
/**
* h5支付
*/
@@ -47,6 +46,9 @@ public interface PayService {
*/
CzgResult<Map<String, Object>> jsPayOrder(String clintIp, OrderPayParamDTO payParam);
/**
* 空订单支付
*/
CzgResult<Map<String, Object>> js2PayOrder(@NonNull String clintIp, OrderPayParamDTO payParam);
/**
@@ -63,7 +65,13 @@ public interface PayService {
* 聚合反扫
*/
CzgResult<Map<String, Object>> microPayOrder(OrderPayParamDTO payParam);
//-----------------------------------------------------------------订单+会员 一起支付-----------------------------------------------------------
/**
* 充值并付款
*/
CzgResult<Map<String, Object>> rechargePayOrder(String clintIp, OrderPayParamDTO payParam);
//-----------------------------------------------------------------会员支付--------------------------------------------------------------------
/**
* 现金充值
@@ -89,6 +97,18 @@ public interface PayService {
* 聚合反扫
*/
CzgResult<Map<String, Object>> microPayVip(VipPayParamDTO payParam);
//-----------------------------------------------------------------会员开通购买 ----------------------------------------------------------
CzgResult<Map<String, Object>> ltPayMember(String clientIP, VipMemberPayParamDTO payParam);
CzgResult<Map<String, Object>> recharge(String clientIP, VipPayParamDTO rechargeDTO, Long userId);
//-----------------------------------------------------------------积分商品 付款 ----------------------------------------------------------
CzgResult<Map<String, Object>> ltPayPointsGoods(String ip, PointGoodsExchangeDTO param, Long recordId);
//-----------------------------------------------------------------退款-----------------------------------------------------------------
/**
* 退款前校验
@@ -107,6 +127,21 @@ public interface PayService {
*/
void refundOrder(Long shopId, Long orderId, Long payOrderId, String refPayOrderNo, String refundReason, BigDecimal refundAmount);
/**
* 统一退款接口
*
* @param refPayOrderNo 自定义退单号
*/
void unifyRefund(Long shopId, Long sourceId, Long payOrderId, String refPayOrderNo, String refundReason, BigDecimal refundAmount);
/**
* 退款补偿使用
*/
void unifyRefund(OrderPayment payment, String refPayOrderNo);
//-----------------------------------------------------------------支付查询 ----------------------------------------------------------
/**
* 支付查询
*
@@ -122,8 +157,4 @@ public interface PayService {
* @param refundOrderId 平台退款订单号 二选一
*/
CzgResult<CzgRefundResp> queryRefund(Long shopId, String mchRefundNo, String refundOrderId);
CzgResult<Map<String, Object>> ltPayMember(String clientIP, VipMemberPayParamDTO payParam);
CzgResult<Map<String, Object>> recharge(String clientIP, VipPayParamDTO rechargeDTO, Long userId);
}

View File

@@ -35,7 +35,7 @@ import java.util.Map;
public class DistributionPayServiceImpl implements DistributionPayService {
private final BigDecimal MONEY_RATE = new BigDecimal("100");
@DubboReference
@Resource
private OrderPaymentService orderPaymentService;
@Resource
private MkDistributionConfigService configService;

View File

@@ -25,8 +25,10 @@ import com.czg.exception.OrderCancelException;
import com.czg.exception.OrderValidateException;
import com.czg.market.dto.MkDiscountActivityDTO;
import com.czg.market.entity.MkDiscountThreshold;
import com.czg.market.entity.MkPointsConfig;
import com.czg.market.entity.MkShopCouponRecord;
import com.czg.market.entity.ShopCoupon;
import com.czg.market.enums.PointsConstant;
import com.czg.market.service.*;
import com.czg.order.dto.*;
import com.czg.order.entity.CashierCart;
@@ -34,10 +36,7 @@ import com.czg.order.entity.OrderDetail;
import com.czg.order.entity.OrderInfo;
import com.czg.order.entity.OrderPayment;
import com.czg.order.enums.PayEnums;
import com.czg.order.service.CashierCartService;
import com.czg.order.service.OrderDetailService;
import com.czg.order.service.OrderInfoCustomService;
import com.czg.order.service.OrderPaymentService;
import com.czg.order.service.*;
import com.czg.order.vo.*;
import com.czg.product.entity.Product;
import com.czg.product.service.ProductRpcService;
@@ -104,8 +103,6 @@ public class OrderInfoCustomServiceImpl implements OrderInfoCustomService {
@Resource
private MkDistributionUserService distributionUserService;
@DubboReference
private MemberPointsService pointsService;
@DubboReference
private ProductService productService;
@DubboReference
private ProductRpcService productRpcService;
@@ -119,11 +116,15 @@ public class OrderInfoCustomServiceImpl implements OrderInfoCustomService {
private ShopUserFlowService flowService;
@DubboReference
private ShopTableService shopTableService;
@DubboReference
private PointsBasicSettingService pointsBasicService;
@Resource
private MkShopConsumerCouponService consumerCouponService;
@Resource
private MkPointsUserService pointsUserService;
@Resource
private MkPointsConfigService pointsConfigService;
@Resource
private PointsGoodPayService goodPayService;
@Resource
private ShopCouponService couponService;
@Resource
private MkShopCouponRecordService couponRecordService;
@@ -132,8 +133,6 @@ public class OrderInfoCustomServiceImpl implements OrderInfoCustomService {
@Resource
private MkShopConsumeDiscountRecordService newConsumerDiscountRecordService;
@Resource
private MemberOrderService memberOrderService;
@Resource
private MkDiscountActivityService discountActService;
@Resource
private MkShopRechargeService shopRechargeService;
@@ -376,11 +375,10 @@ public class OrderInfoCustomServiceImpl implements OrderInfoCustomService {
param.getPointsDiscountAmount().compareTo(BigDecimal.ZERO) > 0);
}
// 积分校验
Long mainShopId = shopInfoService.getMainIdByShopId(shopId);
PointsBasicSetting pointSetting = pointsBasicService.getById(mainShopId);
MkPointsConfig pointSetting = pointsConfigService.getOne(QueryWrapper.create().eq(MkPointsConfig::getShopId, shopId));
boolean usePointsDeduction = param.getPointsNum() > 0 || param.getPointsDiscountAmount().compareTo(BigDecimal.ZERO) > 0;
if (usePointsDeduction) {
if (pointSetting == null || !pointSetting.getEnableDeduction().equals(1)) {
if (pointSetting == null || !pointSetting.getEnableRewards().equals(1)) {
throw new OrderValidateException("生成支付订单失败,该店铺未开启积分抵扣");
}
if (param.getUserId() == null) {
@@ -396,24 +394,11 @@ public class OrderInfoCustomServiceImpl implements OrderInfoCustomService {
}
}
if (param.getPointsNum() > 0 && (!param.isFreeDine() || param.isWithPoints())) {
if (pointSetting == null || !pointSetting.getEnableDeduction().equals(1)) {
throw new OrderValidateException("生成支付订单失败,该店铺未开启积分抵扣");
}
if (param.getUserId() == null) {
throw new OrderValidateException("生成支付订单失败,请选择用户后再使用积分抵扣");
}
} else if (param.getPointsDiscountAmount().compareTo(BigDecimal.ZERO) > 0) {
throw new OrderValidateException("生成支付订单失败,已使用积分抵扣,积分数量不正确");
}
ShopUser shopUser = null;
if (param.getUserId() != null) {
UserInfo userInfo = userInfoService.getById(param.getUserId());
AssertUtil.isNull(userInfo, "生成支付订单失败,用户信息不存在");
shopUser = shopUserService.getShopUserInfo(shopId, param.getUserId());
if (param.getPointsNum() > 0 && !shopUser.getIsVip().equals(1) && pointSetting.getDeductionGroup().contains("vip")) {
throw new OrderValidateException("生成支付订单失败,该店铺仅会员可使用积分抵扣");
}
orderInfo.setUserId(userInfo.getId());
}
//会员价校验
@@ -1075,7 +1060,8 @@ public class OrderInfoCustomServiceImpl implements OrderInfoCustomService {
distributionUserService.costUpgradeLevelBefore(orderInfo.getUserId(), orderInfo.getShopId());
// 分销奖励
distributionUserService.distribute(orderInfo.getId(), orderInfo.getOrderNo(), payment.getAmount(), orderInfo.getUserId(), orderInfo.getShopId(), "order");
} else if ("memberIn".equals(payment.getPayType()) || "free".equals(payment.getPayType())) {
}
else if ("memberIn".equals(payment.getPayType()) || "free".equals(payment.getPayType())) {
boolean isFree = "free".equals(payment.getPayType());
ShopUser shopUser = shopUserService.getById(payment.getSourceId());
OrderInfo orderInfo = null;
@@ -1175,7 +1161,8 @@ public class OrderInfoCustomServiceImpl implements OrderInfoCustomService {
// if (orderInfo != null) {
// distributionUserService.distribute(payment.getId(), payment.getOrderNo(), payment.getAmount(), orderInfo.getUserId(), payment.getShopId(), "recharge");
// }
} else if ("memberPay".equals(payment.getPayType())) {
}
else if ("memberPay".equals(payment.getPayType())) {
//购买会员
ShopUser shopUser = shopUserService.getById(payment.getSourceId());
memberConfigService.joinMember(payment.getShopId(), shopUser.getUserId(), payment.getRelatedId());
@@ -1183,8 +1170,11 @@ public class OrderInfoCustomServiceImpl implements OrderInfoCustomService {
// memberConfigService.deliver(shopUser.getMainShopId(), shopUser.getUserId(), TableValueConstant.MemberExpFlow.Type.COST, payment.getAmount(), null, payment.getId());
// 分销员开通
} else if ("distribution".equals(payment.getPayType())) {
}
else if ("distribution".equals(payment.getPayType())) {
distributionUserService.open(payment.getSourceId(), payment.getAmount(), payment.getShopId(), payment.getId());
}else if("point".equals(payment.getPayType())){
goodPayService.payCallBack(payment.getSourceId());
}
}
paymentService.updateById(payment);
@@ -1274,7 +1264,9 @@ public class OrderInfoCustomServiceImpl implements OrderInfoCustomService {
}
//积分使用
if (orderInfo.getPointsNum() != null && orderInfo.getPointsNum() > 0) {
pointsService.deductPoints(shopUser.getId(), orderInfo.getPointsNum(), "积分抵扣账单", orderInfo);
pointsUserService.alterPoints(null, shopUser.getId(), orderInfo.getShopId(), PointsConstant.SUB,
orderInfo.getPointsNum(), orderInfo.getId().toString(), "积分抵扣账单");
}
//更新优惠券信息
if (StrUtil.isNotBlank(orderInfo.getCouponInfoList()) && !"null".equals(orderInfo.getCouponInfoList())) {
@@ -1296,7 +1288,10 @@ public class OrderInfoCustomServiceImpl implements OrderInfoCustomService {
if ((orderInfo.getPayType() != null && !ArrayUtil.contains(payTypes, orderInfo.getPayType()))
|| (payType != null && !ArrayUtil.contains(payTypes, payType))) {
//下单赠送积分
pointsService.consumeAwardPoints(shopUser.getId(), orderInfo);
pointsConfigService.consumeAwardPoints(shopUser.getId(), orderInfo);
// pointsUserService.alterPoints(null, shopUser.getId(), orderInfo.getShopId(), PointsConstant.ADD,
// rechargeDetail.getRewardPoints(), orderInfo.getId(), "下单赠送积分");
//消费返现
if (!orderInfo.getPayType().equals(PayEnums.CASH_PAY.getValue())) {
consumeCashbackService.cashback(orderInfo.getShopId(), shopUser.getUserId(), orderInfo.getPayAmount(), orderInfo.getId(), orderInfo.getOrderNo());
@@ -1509,7 +1504,8 @@ public class OrderInfoCustomServiceImpl implements OrderInfoCustomService {
}
//积分使用
if (orderInfo.getPointsNum() != null && orderInfo.getPointsNum() > 0) {
pointsService.deductPoints(shopUser.getId(), orderInfo.getPointsNum(), "积分抵扣账单", orderInfo);
pointsUserService.alterPoints(null, shopUser.getId(), orderInfo.getShopId(), PointsConstant.ADD,
orderInfo.getPointsNum(), orderInfo.getId().toString(), "积分抵扣账单");
}
//更新优惠券信息
if (StrUtil.isNotBlank(orderInfo.getCouponInfoList()) && !"null".equals(orderInfo.getCouponInfoList())) {

View File

@@ -25,13 +25,13 @@ import com.czg.market.service.*;
import com.czg.market.vo.MkShopRechargeVO;
import com.czg.order.dto.CheckOrderPay;
import com.czg.order.dto.OrderInfoRefundDTO;
import com.czg.order.dto.PointGoodsExchangeDTO;
import com.czg.order.entity.OrderDetail;
import com.czg.order.entity.OrderInfo;
import com.czg.order.entity.OrderPayment;
import com.czg.order.enums.PayEnums;
import com.czg.order.service.CreditBuyerOrderService;
import com.czg.order.service.OrderDetailService;
import com.czg.market.service.OrderInfoService;
import com.czg.order.service.OrderInfoCustomService;
import com.czg.order.service.OrderPaymentService;
import com.czg.resp.CzgRespCode;
@@ -88,8 +88,6 @@ public class PayServiceImpl implements PayService {
@Resource
private MkShopCouponRecordService recordService;
@DubboReference
private MemberPointsService pointsService;
@DubboReference
private FreeDineConfigService freeConfigService;
@Resource
private CzgPayService czgPayService;
@@ -227,7 +225,7 @@ public class PayServiceImpl implements PayService {
boolean exists = shopInfoService.exists(QueryWrapper.create()
.eq(ShopInfo::getMainId, shopInfo.getMainId())
.eq(ShopInfo::getId, orderInfo.getShopId()));
if(!exists){
if (!exists) {
return CzgResult.failure("违规操作,请确认店铺后重试");
}
}
@@ -253,7 +251,6 @@ public class PayServiceImpl implements PayService {
LocalDateTime.now(), flowId, PayEnums.VIP_PAY);
return CzgResult.success();
}
@@ -437,16 +434,6 @@ public class PayServiceImpl implements PayService {
shopUserService.updateById(updateInfo);
}
shopRechargeService.recharge(shopUser.getMainShopId(), shopUser.getId(), payParam.getRechargeDetailId(), payParam.getAmount(), null, "cash", ShopUserFlowBizEnum.CASH_IN);
// ShopUserMoneyEditDTO shopUserMoneyEditDTO = new ShopUserMoneyEditDTO()
// .setId(shopUser.getId())
// .setMoney(payParam.getAmount())
// .setType(1)
// .setRemark("现金充值")
// .setBizEnum(ShopUserFlowBizEnum.CASH_IN);
// //更新会员余额 并生成流水
// Long flowId = shopUserService.updateMoney(shopUserMoneyEditDTO);
//TODO 以前的会员活动
//shopActivateService.giveActivate(shopUser, payParam.getAmount(), payParam.getActivateId(), flowId);
return CzgResult.success();
}
@@ -531,7 +518,8 @@ public class PayServiceImpl implements PayService {
initOrderPayment(new OrderPayment(rechargeDTO.getShopId(), shopUser.getId(), isFree ? "free" : "memberIn", payOrderNo,
"", amount, isFree ? rechargeDTO.getOrderId() : rechargeDTO.getRechargeDetailId()));
return ltPay(rechargeDTO.getShopId(), rechargeDTO.getPayType(), new CzgLtPayReq(payOrderNo, amount.multiply(MONEY_RATE).longValue(),
rechargeDTO.getPayType(), "会员充值", "wechatPay".equals(rechargeDTO.getPayType()) ? userInfo.getWechatOpenId() : userInfo.getAlipayOpenId(), clientIP, rechargeDTO.getReturnUrl(), rechargeDTO.getBuyerRemark(), ""));
rechargeDTO.getPayType(), "会员充值", "wechatPay".equals(rechargeDTO.getPayType()) ? userInfo.getWechatOpenId() : userInfo.getAlipayOpenId(),
clientIP, rechargeDTO.getReturnUrl(), rechargeDTO.getBuyerRemark(), ""));
}
@Override
@@ -563,6 +551,19 @@ public class PayServiceImpl implements PayService {
return mapCzgResult;
}
@Override
@Transactional
public CzgResult<Map<String, Object>> ltPayPointsGoods(String ip, PointGoodsExchangeDTO param, Long mkPointsGoodsRecordId) {
AssertUtil.isBlank(param.getOpenId(), "用户小程序ID不能为空");
AssertUtil.isBlank(param.getPayType(), "支付方式不能为空");
String payOrderNo = "DH" + IdUtil.getSnowflakeNextId();
initOrderPayment(new OrderPayment(param.getShopId(), mkPointsGoodsRecordId, "point", payOrderNo,
"", param.getPrice(), null));
return ltPay(param.getShopId(), param.getPayType(), new CzgLtPayReq(payOrderNo, param.getPrice().multiply(MONEY_RATE).longValue(),
param.getPayType(), "积分商品购买", param.getOpenId(), ip, "", "", ""));
}
@Override
public CzgResult<Map<String, BigDecimal>> refundVipBefore(VipRefundDTO payParam) {
Map<String, BigDecimal> resultMap = new HashMap<>(5);
@@ -645,7 +646,7 @@ public class PayServiceImpl implements PayService {
.setMoney(refPayParam.getRefAmount())
.setType(0)
.setRemark("退款")
.setBizEnum(refPayParam.isCashRefund() ? ShopUserFlowBizEnum.RECHARGE_CASH_REFUND : ShopUserFlowBizEnum.RECHARGE_REFUND )
.setBizEnum(refPayParam.isCashRefund() ? ShopUserFlowBizEnum.RECHARGE_CASH_REFUND : ShopUserFlowBizEnum.RECHARGE_REFUND)
.setRelationId(refPaymentId)
.setRechargeId(inFlow.getId());
//更新会员余额 并生成流水
@@ -820,7 +821,7 @@ public class PayServiceImpl implements PayService {
@NonNull String refundReason, @NonNull BigDecimal refundAmount) {
OrderPayment payment = paymentService.getById(payOrderId);
AssertUtil.isNull(payment, "退款失败支付记录不存在");
Long refundId = initOrderPayment(new OrderPayment(shopId, orderId, "refund", refPayOrderNo, null, refundAmount));
Long refundId = initOrderPayment(new OrderPayment(shopId, orderId, "refund", refPayOrderNo, null, refundAmount, payment.getId()));
CzgResult<CzgRefundResp> refund = refund(shopId, new CzgRefundReq(refPayOrderNo, refundReason, refundAmount.multiply(MONEY_RATE).longValue(),
payment.getOrderNo(), ""));
if (refund.getCode() != 200 || refund.getData() == null || !"SUCCESS".equals(refund.getData().getState())) {
@@ -838,6 +839,59 @@ public class PayServiceImpl implements PayService {
}
}
@Override
@Transactional
public void unifyRefund(@NonNull Long shopId, @NonNull Long sourceId, @NonNull Long payOrderId, @NonNull String refPayOrderNo,
@NonNull String refundReason, @NonNull BigDecimal refundAmount) {
OrderPayment payment = paymentService.getById(payOrderId);
AssertUtil.isNull(payment, "退款失败,支付记录不存在");
Long refundId = initOrderPayment(new OrderPayment(shopId, sourceId, "refund", refPayOrderNo, null, refundAmount, payment.getId()));
CzgResult<CzgRefundResp> refund = refund(shopId, new CzgRefundReq(refPayOrderNo, refundReason, refundAmount.multiply(MONEY_RATE).longValue(),
payment.getOrderNo(), ""));
if (refund.getCode() != 200 || refund.getData() == null || !"SUCCESS".equals(refund.getData().getState())) {
paymentService.updateChain()
.eq(OrderPayment::getId, refundId)
.set(OrderPayment::getTradeNumber, refund.getData().getRefundOrderId())
.set(OrderPayment::getRespJson, JSONObject.toJSONString(refund.getData()))
.set(OrderPayment::getPayStatus, "fail")
.update();
} else {
paymentService.updateChain()
.eq(OrderPayment::getId, refundId)
.set(OrderPayment::getPayTime, refund.getData().getRefundTime())
.set(OrderPayment::getTradeNumber, refund.getData().getRefundOrderId())
.set(OrderPayment::getRespJson, JSONObject.toJSONString(refund.getData()))
.set(OrderPayment::getPayStatus, "success")
.update();
}
}
@Override
@Transactional
public void unifyRefund(OrderPayment refundPayment, String refPayOrderNo) {
OrderPayment payment = paymentService.getById(refundPayment.getRelatedId());
AssertUtil.isNull(payment, "退款失败,支付记录不存在");
Long refundId = refundPayment.getId();
CzgResult<CzgRefundResp> refund = refund(payment.getShopId(), new CzgRefundReq(refPayOrderNo, "退款补偿", refundPayment.getAmount().multiply(MONEY_RATE).longValue(),
payment.getOrderNo(), ""));
if (refund.getCode() != 200 || refund.getData() == null || !"SUCCESS".equals(refund.getData().getState())) {
paymentService.updateChain()
.eq(OrderPayment::getId, refundId)
.set(OrderPayment::getTradeNumber, refund.getData().getRefundOrderId())
.set(OrderPayment::getRespJson, JSONObject.toJSONString(refund.getData()))
.set(OrderPayment::getPayStatus, "fail")
.update();
} else {
paymentService.updateChain()
.eq(OrderPayment::getId, refundId)
.set(OrderPayment::getPayTime, refund.getData().getRefundTime())
.set(OrderPayment::getTradeNumber, refund.getData().getRefundOrderId())
.set(OrderPayment::getRespJson, JSONObject.toJSONString(refund.getData()))
.set(OrderPayment::getPayStatus, "success")
.update();
}
}
@Override
@Transactional
public CzgResult<CzgBaseResp> queryPayOrder(@NonNull Long shopId, String payOrderId, String mchOrderNo) {

View File

@@ -0,0 +1,172 @@
package com.czg.service.order.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import com.czg.account.entity.ShopUser;
import com.czg.account.service.ShopUserService;
import com.czg.exception.CzgException;
import com.czg.market.dto.MkShopCouponGiftDTO;
import com.czg.market.entity.MkPointsGoods;
import com.czg.market.entity.MkPointsGoodsRecord;
import com.czg.market.entity.MkPointsUser;
import com.czg.market.enums.PointsConstant;
import com.czg.market.service.MkPointsGoodsRecordService;
import com.czg.market.service.MkPointsGoodsService;
import com.czg.market.service.MkPointsUserService;
import com.czg.market.service.MkShopCouponRecordService;
import com.czg.order.dto.PointGoodsExchangeDTO;
import com.czg.order.dto.PointGoodsRefundDTO;
import com.czg.order.service.PointsGoodPayService;
import com.czg.resp.CzgResult;
import com.czg.service.order.service.PayService;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Map;
@Slf4j
@Service
public class PointsGoodPayServiceImpl implements PointsGoodPayService {
@Resource
private ShopUserService shopUserService;
@Resource
private MkPointsUserService pointsUserService;
@Resource
private MkPointsGoodsService goodsService;
@Resource
private MkPointsGoodsRecordService goodsRecordService;
@Resource
private MkShopCouponRecordService couponRecordService;
@Resource
@Lazy
private PayService payService;
@Override
@Transactional
public CzgResult<Map<String, Object>> exchange(String ip, PointGoodsExchangeDTO param) {
MkPointsGoods goods = goodsService.getById(param.getPointsGoodsId());
if (goods == null) {
throw new CzgException("兑换失败,商品不存在");
}
if (goods.getStatus() != 1) {
throw new CzgException("兑换失败,商品已下架");
}
Integer boughtCount = goodsRecordService.getOneAs(QueryWrapper.create().select("sum(number)")
.eq(MkPointsGoodsRecord::getPointsGoodsId, param.getPointsGoodsId())
.ne(MkPointsGoodsRecord::getStatus, "已退款")
.eq(MkPointsGoodsRecord::getUserId, param.getUserId()), Integer.class);
if (goods.getLimitQuota() > 0 && boughtCount != null && boughtCount >= goods.getLimitQuota()) {
throw new CzgException("兑换失败,该商品每人限制兑换" + goods.getLimitQuota() + "");
}
MkPointsUser pointsUser = pointsUserService.getPointsUser(param.getShopId(), null, param.getUserId());
if (pointsUser == null) {
throw new CzgException("兑换失败,用户积分不足");
}
if (goods.getRequiredPoints() > 0 && pointsUser.getPointBalance() < (long) goods.getRequiredPoints() * param.getNumber()) {
throw new CzgException("兑换失败,积分不足");
}
ShopUser shopUser = shopUserService.getUserInfo(param.getShopId(), param.getUserId());
if (shopUser == null) {
throw new CzgException("兑换失败,店铺用户不存在");
}
MkPointsGoodsRecord record = new MkPointsGoodsRecord();
record.setOrderNo("DH" + IdUtil.getSnowflakeNextId());
record.setShopId(param.getShopId());
record.setPointsGoodsId(param.getPointsGoodsId());
record.setPointsGoodsName(goods.getGoodsName());
record.setGoodsImageUrl(goods.getGoodsImageUrl());
record.setGoodsCategory(goods.getGoodsCategory());
record.setReceiveType(goods.getReceiveType());
record.setShopUserId(shopUser.getId());
record.setUserId(param.getUserId());
record.setNumber(param.getNumber());
record.setSpendPoints(goods.getRequiredPoints() * param.getNumber());
record.setExtraPaymentAmount(goods.getExtraPrice() != null ? goods.getExtraPrice().multiply(new BigDecimal(param.getNumber())) : BigDecimal.ZERO);
if (goods.getExtraPrice() != null && goods.getExtraPrice().compareTo(BigDecimal.ZERO) > 0) {
param.setPrice(record.getExtraPaymentAmount());
record.setStatus("待支付");
record.setIsDel(1);
goodsRecordService.save(record);
return payService.ltPayPointsGoods(ip, param, record.getId());
}
//正常处理
exchangeAfter(record, goods);
return CzgResult.success(BeanUtil.beanToMap(record));
}
@Override
public boolean applyRefund(PointGoodsRefundDTO param, Long shopId) {
MkPointsGoodsRecord record = goodsRecordService.getOne(QueryWrapper.create()
.eq(MkPointsGoodsRecord::getId, param.getRecordId())
.eq(MkPointsGoodsRecord::getShopId, shopId));
if (record == null) {
throw new CzgException("操作失败,记录不存在");
}
//退积分
if (record.getSpendPoints() != null && record.getSpendPoints() > 0) {
//回增积分
pointsUserService.alterPoints(record.getUserId(), record.getShopUserId(), record.getShopId(), PointsConstant.ADD,
record.getSpendPoints(), record.getId().toString(), "积分商品兑换退款");
}
//退钱
if (record.getExtraPaymentAmount() != null && record.getExtraPaymentAmount().compareTo(BigDecimal.ZERO) > 0) {
String refPayOrderNo = "REP" + IdUtil.getSnowflakeNextId();
payService.unifyRefund(shopId, record.getId(), record.getPayOrderId(), refPayOrderNo, param.getReason(), record.getExtraPaymentAmount());
}
MkPointsGoodsRecord upRecord = new MkPointsGoodsRecord();
upRecord.setId(record.getId());
upRecord.setStatus("已退款");
upRecord.setCancelOrRefundReason(param.getReason());
upRecord.setCancelOrRefundTime(LocalDateTime.now());
return goodsRecordService.updateById(upRecord);
}
@Override
@Transactional
public void payCallBack(Long recordId) {
MkPointsGoodsRecord record = goodsRecordService.getById(recordId);
if (record == null) {
log.error("积分兑换商品发放失败,记录不存在");
}
exchangeAfter(record, null);
}
private MkPointsGoodsRecord exchangeAfter(MkPointsGoodsRecord record, MkPointsGoods goods) {
if (goods == null) {
goods = goodsService.getById(record.getPointsGoodsId());
}
//扣除积分
pointsUserService.alterPoints(record.getUserId(), null, record.getShopId(), PointsConstant.SUB,
record.getSpendPoints(), record.getId().toString(), "积分商品兑换");
record.setIsDel(0);
if (goods.getGoodsCategory().equals("优惠券")) {
record.setStatus("已完成");
record.setCheckoutTime(LocalDateTime.now());
goodsRecordService.save(record);
MkShopCouponGiftDTO giftDTO = new MkShopCouponGiftDTO()
.setCouponId(goods.getCouponId())
.setShopId(record.getShopId())
.setSourceId(record.getId())
.setShopUserId(record.getId())
.setSource("积分兑换");
couponRecordService.receiveCoupon(giftDTO, record.getNumber(), false);
return record;
}else {
record.setStatus("待核销");
record.setCouponCode(RandomUtil.randomNumbers(12));
}
goodsService.upNumberById(goods.getId(), goods.getQuantity() - record.getNumber(), goods.getTotalExchangeCount() + record.getNumber());
goodsRecordService.save(record);
return record;
}
}