diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/controller/points/TbMemberPointsController.java b/eladmin-system/src/main/java/cn/ysk/cashier/controller/points/TbMemberPointsController.java new file mode 100644 index 00000000..9b9d0d3e --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/controller/points/TbMemberPointsController.java @@ -0,0 +1,88 @@ +package cn.ysk.cashier.controller.points; + +import cn.hutool.core.map.MapProxy; +import cn.ysk.cashier.dto.points.OrderDeductionPointsDTO; +import cn.ysk.cashier.mybatis.entity.TbMemberPoints; +import cn.ysk.cashier.mybatis.service.TbMemberPointsService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.util.Map; + + +/** + * 会员积分 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@RestController +@RequestMapping("/api/points/member-points") +@Api(tags = "会员积分") +public class TbMemberPointsController { + + @Resource + private TbMemberPointsService tbMemberPointsService; + + @GetMapping("page") + @ApiOperation("分页") + public ResponseEntity page(@RequestParam Map params) { + Map page = tbMemberPointsService.page(params); + return ResponseEntity.ok().body(page); + } + + @GetMapping("{memberId}") + @ApiOperation("获取会员积分等信息") + public ResponseEntity getMemberPoints(@PathVariable("memberId") Long memberId) { + TbMemberPoints memberPoints = tbMemberPointsService.getMemberPoints(memberId); + return ResponseEntity.ok().body(memberPoints); + } + + @GetMapping("calc-usable-points") + @ApiOperation("获取订单可用积分及抵扣金额") + public ResponseEntity getMemberUsablePoints(@RequestParam Long memberId, @RequestParam BigDecimal orderAmount) { + OrderDeductionPointsDTO usablePoints = tbMemberPointsService.getMemberUsablePoints(memberId, orderAmount); + return ResponseEntity.ok().body(usablePoints); + } + + @GetMapping("calc-used-points") + @ApiOperation("根据抵扣金额计算所需积分") + public ResponseEntity calcUsedPoints(@RequestParam Long memberId, @RequestParam BigDecimal orderAmount, @RequestParam BigDecimal deductionAmount) { + int points = tbMemberPointsService.calcUsedPoints(memberId, orderAmount, deductionAmount); + return ResponseEntity.ok().body(points); + } + + @GetMapping("calc-deduction-amount") + @ApiOperation("根据积分计算可抵扣金额") + public ResponseEntity calcDeductionAmount(@RequestParam Long memberId, @RequestParam BigDecimal orderAmount, @RequestParam Integer points) { + BigDecimal deductionAmount = tbMemberPointsService.calcDeductionAmount(memberId, orderAmount, points); + return ResponseEntity.ok().body(deductionAmount); + } + + @PostMapping("payed-deduct-points") + @ApiOperation("支付完成扣减积分(支付成功回调中使用)") + public ResponseEntity deductPoints(@RequestBody Map params) { + MapProxy proxy = MapProxy.create(params); + Long memberId = proxy.getLong("memberId"); + Integer points = proxy.getInt("points"); + String content = proxy.getStr("content"); + Long orderId = proxy.getLong("orderId"); + boolean ret = tbMemberPointsService.deductPoints(memberId, points, content, orderId); + return ResponseEntity.ok().body(ret); + } + + @PostMapping("consume-award-points") + @ApiOperation("消费赠送积分(支付成功回调中使用)") + public ResponseEntity consumeAwardPoints(@RequestBody Map params) { + MapProxy proxy = MapProxy.create(params); + Long memberId = proxy.getLong("memberId"); + Long orderId = proxy.getLong("orderId"); + tbMemberPointsService.consumeAwardPoints(memberId, orderId); + return ResponseEntity.ok().build(); + } + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/controller/points/TbMemberPointsLogController.java b/eladmin-system/src/main/java/cn/ysk/cashier/controller/points/TbMemberPointsLogController.java new file mode 100644 index 00000000..b03bc21b --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/controller/points/TbMemberPointsLogController.java @@ -0,0 +1,36 @@ +package cn.ysk.cashier.controller.points; + +import cn.ysk.cashier.mybatis.service.TbMemberPointsLogService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.http.ResponseEntity; +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 javax.annotation.Resource; +import java.util.Map; + + +/** + * 会员积分变动记录 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@RestController +@RequestMapping("/api/points/member-points-log") +@Api(tags = "会员积分变动记录") +public class TbMemberPointsLogController { + + @Resource + private TbMemberPointsLogService tbMemberPointsLogService; + + @GetMapping("page") + @ApiOperation("分页") + public ResponseEntity page(@RequestParam Map params) { + Map page = tbMemberPointsLogService.page(params); + return ResponseEntity.ok().body(page); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/controller/points/TbPointsBasicSettingController.java b/eladmin-system/src/main/java/cn/ysk/cashier/controller/points/TbPointsBasicSettingController.java new file mode 100644 index 00000000..2f5104bb --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/controller/points/TbPointsBasicSettingController.java @@ -0,0 +1,42 @@ +package cn.ysk.cashier.controller.points; + +import cn.ysk.cashier.mybatis.entity.TbPointsBasicSetting; +import cn.ysk.cashier.mybatis.service.TbPointsBasicSettingService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + + +/** + * 积分基本设置 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@RestController +@RequestMapping("/api/points/basic-setting") +@Api(tags = "积分基本设置") +public class TbPointsBasicSettingController { + + @Resource + private TbPointsBasicSettingService tbPointsBasicSettingService; + + @GetMapping("{shopId}") + @ApiOperation("信息") + public ResponseEntity get(@PathVariable("shopId") Long shopId) { + TbPointsBasicSetting data = tbPointsBasicSettingService.getByShopId(shopId); + return ResponseEntity.ok().body(data); + } + + @PostMapping + @ApiOperation("保存") + public ResponseEntity save(@RequestBody TbPointsBasicSetting entity) { + tbPointsBasicSettingService.save(entity); + return ResponseEntity.ok().build(); + } + + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/controller/points/TbPointsExchangeRecordController.java b/eladmin-system/src/main/java/cn/ysk/cashier/controller/points/TbPointsExchangeRecordController.java new file mode 100644 index 00000000..2ce213f2 --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/controller/points/TbPointsExchangeRecordController.java @@ -0,0 +1,62 @@ +package cn.ysk.cashier.controller.points; + +import cn.ysk.cashier.mybatis.entity.TbPointsExchangeRecord; +import cn.ysk.cashier.mybatis.service.TbPointsExchangeRecordService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.Map; + + +/** + * 积分兑换记录 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@RestController +@RequestMapping("/api/points/exchange-record") +@Api(tags = "积分兑换记录") +public class TbPointsExchangeRecordController { + + @Resource + private TbPointsExchangeRecordService tbPointsExchangeRecordService; + + @GetMapping("page") + @ApiOperation("分页") + public ResponseEntity page(@RequestParam Map params) { + Map data = tbPointsExchangeRecordService.page(params); + return ResponseEntity.ok().body(data); + } + + @GetMapping("{id}") + @ApiOperation("信息") + public ResponseEntity get(@PathVariable("id") Long id) { + TbPointsExchangeRecord data = tbPointsExchangeRecordService.get(id); + return ResponseEntity.ok().body(data); + } + + @PostMapping("checkout") + @ApiOperation("核销") + public ResponseEntity checkout(@RequestBody TbPointsExchangeRecord record) { + tbPointsExchangeRecordService.checkout(record.getCouponCode()); + return ResponseEntity.ok().build(); + } + + @PostMapping("exchange") + @ApiOperation("兑换") + public ResponseEntity exchange(@RequestBody TbPointsExchangeRecord record) { + TbPointsExchangeRecord data = tbPointsExchangeRecordService.exchange(record); + return ResponseEntity.ok().body(data); + } + + @GetMapping("total") + @ApiOperation("统计") + public ResponseEntity total(@RequestParam Map params) { + Map data = tbPointsExchangeRecordService.total(params); + return ResponseEntity.ok().body(data); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/controller/points/TbPointsGoodsSettingController.java b/eladmin-system/src/main/java/cn/ysk/cashier/controller/points/TbPointsGoodsSettingController.java new file mode 100644 index 00000000..96017cfb --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/controller/points/TbPointsGoodsSettingController.java @@ -0,0 +1,55 @@ +package cn.ysk.cashier.controller.points; + +import cn.ysk.cashier.mybatis.entity.TbPointsGoodsSetting; +import cn.ysk.cashier.mybatis.service.TbPointsGoodsSettingService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.Map; + + +/** + * 积分商品设置 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@RestController +@RequestMapping("/api/points/goods-setting") +@Api(tags = "积分商品设置") +public class TbPointsGoodsSettingController { + + @Resource + private TbPointsGoodsSettingService tbPointsGoodsSettingService; + + @GetMapping("page") + @ApiOperation("分页") + public ResponseEntity page(@RequestParam Map params) { + Map data = tbPointsGoodsSettingService.page(params); + return ResponseEntity.ok().body(data); + } + + @GetMapping("{id}") + @ApiOperation("信息") + public ResponseEntity get(@PathVariable("id") Integer id) { + TbPointsGoodsSetting data = tbPointsGoodsSettingService.getById(id); + return ResponseEntity.ok().body(data); + } + + @PostMapping + @ApiOperation("保存") + public ResponseEntity save(@RequestBody TbPointsGoodsSetting entity) { + boolean ret = tbPointsGoodsSettingService.save(entity); + return ResponseEntity.ok().body(ret); + } + + @PutMapping + @ApiOperation("修改") + public ResponseEntity update(@RequestBody TbPointsGoodsSetting dto) { + boolean ret = tbPointsGoodsSettingService.update(dto); + return ResponseEntity.ok().body(ret); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/dto/points/OrderDeductionPointsDTO.java b/eladmin-system/src/main/java/cn/ysk/cashier/dto/points/OrderDeductionPointsDTO.java new file mode 100644 index 00000000..6ac6ebda --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/dto/points/OrderDeductionPointsDTO.java @@ -0,0 +1,61 @@ +package cn.ysk.cashier.dto.points; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 获取会员可用积分 + * @author tankaikai + * @since 2024-10-26 11:40 + */ +@Data +public class OrderDeductionPointsDTO { + + /** + * 本单最多可抵扣多少积分 + */ + private Integer maxUsablePoints; + /** + * 根据策略计算出的最少可以抵扣的金额 + */ + private BigDecimal minDeductionAmount; + /** + * 根据策略计算出的最多可以抵扣的金额 + */ + private BigDecimal maxDeductionAmount; + /** + * 下单积分抵扣门槛(每次使用不低于这个值) + */ + private Integer minDeductionPoints; + /** + * 会员账户积分 + */ + private Integer accountPoints; + /** + * 订单金额 (扣除各类折扣) + */ + private BigDecimal orderAmount; + /** + * 使用的积分数量 + */ + private Integer usedPoints; + /** + * 实际抵扣的金额 + */ + private Integer deductionAmount; + /** + * 下单抵扣积分比例 1元=?积分 + */ + private Integer equivalentPoints; + /** + * 不可抵扣原因 + */ + private String unusableReason; + /** + * 是否可用 + */ + private Boolean usable; + + +} diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/entity/TbMemberPoints.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/entity/TbMemberPoints.java new file mode 100644 index 00000000..db2d0286 --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/entity/TbMemberPoints.java @@ -0,0 +1,64 @@ +package cn.ysk.cashier.mybatis.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 会员积分 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("tb_member_points") +public class TbMemberPoints { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 店铺id + */ + private Long shopId; + /** + * 会员id + */ + private Long memberId; + /** + * 会员名称 + */ + private String memberName; + /** + * 会员头像 + */ + private String avatarUrl; + /** + * 手机号码 + */ + private String mobile; + /** + * 账户积分 + */ + private Integer accountPoints; + /** + * 创建时间 + */ + private Date createTime; + /** + * 最近一次积分变动时间 + */ + private Date lastPointsChangeTime; + /** + * 最近一次浮动积分 + */ + private Integer lastFloatPoints; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/entity/TbMemberPointsLog.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/entity/TbMemberPointsLog.java new file mode 100644 index 00000000..ac430835 --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/entity/TbMemberPointsLog.java @@ -0,0 +1,68 @@ +package cn.ysk.cashier.mybatis.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 会员积分变动记录 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("tb_member_points_log") +public class TbMemberPointsLog { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 店铺id + */ + private Long shopId; + /** + * 会员id + */ + private Long memberId; + /** + * 会员名称 + */ + private String memberName; + /** + * 会员头像 + */ + private String avatarUrl; + /** + * 摘要信息(如:兑换某个商品/消费多少钱/充值多少钱/新会员赠送积分等) + */ + private String content; + /** + * 订单编号 + */ + private String orderNo; + /** + * 手机号码 + */ + private String mobile; + /** + * 浮动类型 add-累加 subtract-扣减 + */ + private String floatType; + /** + * 浮动积分(非0正负数) + */ + private Integer floatPoints; + /** + * 创建时间 + */ + private Date createTime; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/entity/TbPointsBasicSetting.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/entity/TbPointsBasicSetting.java new file mode 100644 index 00000000..b9b5432a --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/entity/TbPointsBasicSetting.java @@ -0,0 +1,69 @@ +package cn.ysk.cashier.mybatis.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 积分基本设置 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("tb_points_basic_setting") +public class TbPointsBasicSetting { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 店铺id + */ + private Long shopId; + /** + * 开启消费赠送积分 1-开启 0-关闭 + */ + private Integer enableRewards; + /** + * 每消费xx元赠送1积分 + */ + private BigDecimal consumeAmount; + /** + * 开启下单积分抵扣 1-开启 0-关闭 + */ + private Integer enableDeduction; + /** + * 下单积分抵扣门槛 + */ + private Integer minDeductionPoint; + /** + * 下单最高抵扣比例 + */ + private BigDecimal maxDeductionRatio; + /** + * 下单抵扣积分比例 1元=?积分 + */ + private Integer equivalentPoints; + /** + * 开启积分商城 1-开启 0-关闭 + */ + private Integer enablePointsMall; + /** + * 浏览模式 list-列表 grid-宫格 + */ + private String browseMode; + /** + * 创建时间 + */ + private Date createTime; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/entity/TbPointsExchangeRecord.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/entity/TbPointsExchangeRecord.java new file mode 100644 index 00000000..dfd5fc3a --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/entity/TbPointsExchangeRecord.java @@ -0,0 +1,97 @@ +package cn.ysk.cashier.mybatis.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 积分兑换记录 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("tb_points_exchange_record") +public class TbPointsExchangeRecord { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.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; + /** + * 会员id + */ + private Long memberId; + /** + * 会员名称 + */ + private String memberName; + /** + * 手机号码 + */ + private String mobile; + /** + * 会员头像 + */ + private String avatarUrl; + /** + * 消耗积分 + */ + private Integer spendPoints; + /** + * 额外支付 + */ + private BigDecimal extraPaymentAmount; + /** + * 兑换券券码 + */ + private String couponCode; + /** + * 状态 waiting-待自取 done-已完成 + */ + private String status; + /** + * 创建时间(下单时间) + */ + private Date createTime; + /** + * 更新时间(核销时间) + */ + private Date updateTime; + + @TableField(value = "count(*)", insertStrategy = FieldStrategy.NEVER, updateStrategy = FieldStrategy.NEVER) + private Long count; + + @TableField(value = "sum(extra_payment_amount)", insertStrategy = FieldStrategy.NEVER, updateStrategy = FieldStrategy.NEVER) + private BigDecimal totalAmount; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/entity/TbPointsGoodsSetting.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/entity/TbPointsGoodsSetting.java new file mode 100644 index 00000000..d3292cfe --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/entity/TbPointsGoodsSetting.java @@ -0,0 +1,77 @@ +package cn.ysk.cashier.mybatis.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 积分商品设置 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("tb_points_goods_setting") +public class TbPointsGoodsSetting { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 店铺id + */ + private Long shopId; + /** + * 商品类型 physical-实物 coupon-优惠劵 + */ + private String goodsCategory; + /** + * 商品名称 + */ + private String goodsName; + /** + * 商品图片URL + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS) + private String goodsImageUrl; + /** + * 所需积分 + */ + private Integer requiredPoints; + /** + * 额外价格 + */ + private BigDecimal extraPrice; + /** + * 排序(权重),数字越高,显示约靠前 + */ + private Integer sort; + /** + * 数量 + */ + private Integer quantity; + /** + * 商品详情 + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS) + private String goodsDescription; + /** + * 是否上架 1-是 0-否 + */ + private Integer status; + /** + * 创建时间 + */ + private Date createTime; + /** + * 更新时间 + */ + private Date updateTime; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/mapper/TbMemberPointsLogMapper.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/mapper/TbMemberPointsLogMapper.java new file mode 100644 index 00000000..b8d16e6d --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/mapper/TbMemberPointsLogMapper.java @@ -0,0 +1,16 @@ +package cn.ysk.cashier.mybatis.mapper; + +import cn.ysk.cashier.mybatis.entity.TbMemberPointsLog; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 会员积分变动记录 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@Mapper +public interface TbMemberPointsLogMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/mapper/TbMemberPointsMapper.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/mapper/TbMemberPointsMapper.java new file mode 100644 index 00000000..141b564b --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/mapper/TbMemberPointsMapper.java @@ -0,0 +1,16 @@ +package cn.ysk.cashier.mybatis.mapper; + +import cn.ysk.cashier.mybatis.entity.TbMemberPoints; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 会员积分 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@Mapper +public interface TbMemberPointsMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/mapper/TbPointsBasicSettingMapper.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/mapper/TbPointsBasicSettingMapper.java new file mode 100644 index 00000000..ea144fd2 --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/mapper/TbPointsBasicSettingMapper.java @@ -0,0 +1,16 @@ +package cn.ysk.cashier.mybatis.mapper; + +import cn.ysk.cashier.mybatis.entity.TbPointsBasicSetting; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 积分基本设置 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@Mapper +public interface TbPointsBasicSettingMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/mapper/TbPointsExchangeRecordMapper.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/mapper/TbPointsExchangeRecordMapper.java new file mode 100644 index 00000000..64d627f8 --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/mapper/TbPointsExchangeRecordMapper.java @@ -0,0 +1,16 @@ +package cn.ysk.cashier.mybatis.mapper; + +import cn.ysk.cashier.mybatis.entity.TbPointsExchangeRecord; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 积分兑换记录 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@Mapper +public interface TbPointsExchangeRecordMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/mapper/TbPointsGoodsSettingMapper.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/mapper/TbPointsGoodsSettingMapper.java new file mode 100644 index 00000000..e22a76e7 --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/mapper/TbPointsGoodsSettingMapper.java @@ -0,0 +1,16 @@ +package cn.ysk.cashier.mybatis.mapper; + +import cn.ysk.cashier.mybatis.entity.TbPointsGoodsSetting; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 积分商品设置 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@Mapper +public interface TbPointsGoodsSettingMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/TbMemberPointsLogService.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/TbMemberPointsLogService.java new file mode 100644 index 00000000..abc42438 --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/TbMemberPointsLogService.java @@ -0,0 +1,18 @@ +package cn.ysk.cashier.mybatis.service; + +import cn.ysk.cashier.mybatis.entity.TbMemberPointsLog; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.Map; + +/** + * 会员积分变动记录 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +public interface TbMemberPointsLogService extends IService { + + Map page(Map params); + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/TbMemberPointsService.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/TbMemberPointsService.java new file mode 100644 index 00000000..9ada495c --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/TbMemberPointsService.java @@ -0,0 +1,99 @@ +package cn.ysk.cashier.mybatis.service; + +import cn.ysk.cashier.dto.points.OrderDeductionPointsDTO; +import cn.ysk.cashier.mybatis.entity.TbMemberPoints; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.math.BigDecimal; +import java.util.Map; + +/** + * 会员积分 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +public interface TbMemberPointsService extends IService { + + /** + * 会员积分分页 + * + * @param params + * @return + */ + Map page(Map params); + + /** + * 获取会员积分等信息 + * + * @param memberId 会员id + * @return + */ + TbMemberPoints getMemberPoints(Long memberId); + + /** + * 初始化会员积分 + * + * @param memberId 会员id + * @return + */ + TbMemberPoints initMemberPoints(Long memberId); + + /** + * 根据会员id及订单金额计算可抵扣积分及可抵扣金额 + * + * @param memberId 会员id + * @param orderAmount 订单金额 + * @return + */ + OrderDeductionPointsDTO getMemberUsablePoints(Long memberId, BigDecimal orderAmount); + + /** + * 根据抵扣金额计算抵扣积分 + * + * @param memberId 会员id + * @param orderAmount 订单金额 + * @param deductionAmount 抵扣金额 + */ + int calcUsedPoints(Long memberId, BigDecimal orderAmount, BigDecimal deductionAmount); + + /** + * 根据抵扣积分计算抵扣金额 + * + * @param memberId 会员id + * @param orderAmount 订单金额 + * @param points 抵扣积分 + */ + BigDecimal calcDeductionAmount(Long memberId, BigDecimal orderAmount, int points); + + /** + * 扣除积分 + * + * @param memberId 会员id + * @param points 积分 + * @param content 摘要信息(如:兑换积分商品/积分抵扣账单/消费赠送积分/新会员送积分/储值赠送积分) + * @param orderId 订单id,可以为空 + * @throws Exception + */ + boolean deductPoints(Long memberId, int points, String content, Long orderId); + + /** + * 追加积分 + * + * @param memberId 会员id + * @param points 积分 + * @param content 摘要信息(如:兑换积分商品/积分抵扣账单/消费赠送积分/新会员送积分/储值赠送积分) + * @param orderId 订单id,可以为空 + * @throws Exception + */ + boolean addPoints(Long memberId, int points, String content, Long orderId); + + /** + * 消费赠送积分 + * + * @param memberId 会员id + * @param orderId 订单id + * @throws Exception + */ + void consumeAwardPoints(Long memberId, Long orderId); +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/TbPointsBasicSettingService.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/TbPointsBasicSettingService.java new file mode 100644 index 00000000..66acb836 --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/TbPointsBasicSettingService.java @@ -0,0 +1,16 @@ +package cn.ysk.cashier.mybatis.service; + +import cn.ysk.cashier.mybatis.entity.TbPointsBasicSetting; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 积分基本设置 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +public interface TbPointsBasicSettingService extends IService { + boolean save(TbPointsBasicSetting entity); + + TbPointsBasicSetting getByShopId(Long shopId); +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/TbPointsExchangeRecordService.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/TbPointsExchangeRecordService.java new file mode 100644 index 00000000..af21f22e --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/TbPointsExchangeRecordService.java @@ -0,0 +1,25 @@ +package cn.ysk.cashier.mybatis.service; + +import cn.ysk.cashier.mybatis.entity.TbPointsExchangeRecord; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.Map; + +/** + * 积分兑换记录 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +public interface TbPointsExchangeRecordService extends IService { + + Map page(Map params); + + TbPointsExchangeRecord get(Long id); + + void checkout(String couponCode); + + TbPointsExchangeRecord exchange(TbPointsExchangeRecord record); + + Map total(Map params); +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/TbPointsGoodsSettingService.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/TbPointsGoodsSettingService.java new file mode 100644 index 00000000..d0422283 --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/TbPointsGoodsSettingService.java @@ -0,0 +1,22 @@ +package cn.ysk.cashier.mybatis.service; + +import cn.ysk.cashier.mybatis.entity.TbPointsGoodsSetting; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.Map; + +/** + * 积分商品设置 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +public interface TbPointsGoodsSettingService extends IService { + + Map page(Map params); + + boolean save(TbPointsGoodsSetting entity); + + boolean update(TbPointsGoodsSetting dto); + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/impl/TbMemberPointsLogServiceImpl.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/impl/TbMemberPointsLogServiceImpl.java new file mode 100644 index 00000000..0c9009d9 --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/impl/TbMemberPointsLogServiceImpl.java @@ -0,0 +1,60 @@ +package cn.ysk.cashier.mybatis.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.map.MapProxy; +import cn.hutool.core.util.StrUtil; +import cn.ysk.cashier.mybatis.entity.TbMemberPointsLog; +import cn.ysk.cashier.mybatis.mapper.TbMemberPointsLogMapper; +import cn.ysk.cashier.mybatis.service.TbMemberPointsLogService; +import cn.ysk.cashier.utils.PageUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * 会员积分变动记录 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@Service +public class TbMemberPointsLogServiceImpl extends ServiceImpl implements TbMemberPointsLogService { + + private LambdaQueryWrapper getWrapper(Map params) { + MapProxy mapProxy = MapProxy.create(params); + String beginDate = mapProxy.getStr("beginDate"); + String endDate = mapProxy.getStr("endDate"); + TbMemberPointsLog param = BeanUtil.toBean(params, TbMemberPointsLog.class); + + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(TbMemberPointsLog::getShopId, param.getShopId()); + wrapper.like(StrUtil.isNotEmpty(param.getMemberName()), TbMemberPointsLog::getMemberName, param.getMemberName()); + wrapper.like(StrUtil.isNotEmpty(param.getMobile()), TbMemberPointsLog::getMobile, param.getMobile()); + wrapper.like(StrUtil.isNotEmpty(param.getContent()), TbMemberPointsLog::getContent, param.getContent()); + wrapper.eq(StrUtil.isNotEmpty(param.getFloatType()), TbMemberPointsLog::getFloatType, param.getFloatType()); + wrapper.eq(StrUtil.isNotEmpty(param.getOrderNo()), TbMemberPointsLog::getOrderNo, param.getOrderNo()); + if (StrUtil.isNotEmpty(beginDate)) { + wrapper.apply("create_time >= str_to_date({0}, '%Y-%m-%d %H:%i:%s')", beginDate + " 00:00:00"); + } + if (StrUtil.isNotEmpty(endDate)) { + wrapper.apply("create_time <= str_to_date({0}, '%Y-%m-%d %H:%i:%s')", endDate + " 23:59:59"); + } + wrapper.orderByDesc(TbMemberPointsLog::getId); + return wrapper; + } + + @Override + public Map page(Map params) { + MapProxy mapProxy = MapProxy.create(params); + int pageNum = mapProxy.getInt("page", 1); + int pageSize = mapProxy.getInt("size", 10); + LambdaQueryWrapper wrapper = getWrapper(params); + Page page = super.page(new Page<>(pageNum, pageSize), wrapper); + return PageUtil.toPlusPage(page.getRecords(), Convert.toInt(page.getTotal())); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/impl/TbMemberPointsServiceImpl.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/impl/TbMemberPointsServiceImpl.java new file mode 100644 index 00000000..7b33ca11 --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/impl/TbMemberPointsServiceImpl.java @@ -0,0 +1,292 @@ +package cn.ysk.cashier.mybatis.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.map.MapProxy; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.StrUtil; +import cn.ysk.cashier.dto.points.OrderDeductionPointsDTO; +import cn.ysk.cashier.exception.BadRequestException; +import cn.ysk.cashier.mybatis.entity.TbMemberPoints; +import cn.ysk.cashier.mybatis.entity.TbMemberPointsLog; +import cn.ysk.cashier.mybatis.entity.TbPointsBasicSetting; +import cn.ysk.cashier.mybatis.mapper.*; +import cn.ysk.cashier.mybatis.service.TbMemberPointsService; +import cn.ysk.cashier.mybatis.service.TbPointsBasicSettingService; +import cn.ysk.cashier.pojo.order.TbOrderInfo; +import cn.ysk.cashier.pojo.shop.TbShopUser; +import cn.ysk.cashier.utils.PageUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Date; +import java.util.Map; + +/** + * 会员积分 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@Service +public class TbMemberPointsServiceImpl extends ServiceImpl implements TbMemberPointsService { + + @Resource + private TbMemberPointsLogMapper tbMemberPointsLogMapper; + @Resource + private TbPointsBasicSettingMapper tbPointsBasicSettingMapper; + @Resource + private TbShopUserMapper tbShopUserMapper; + @Resource + private TbOrderInfoMapper tbOrderInfoMapper; + @Resource + private TbPointsBasicSettingService tbPointsBasicSettingService; + + private LambdaQueryWrapper getWrapper(Map params) { + //MapProxy mapProxy = MapProxy.create(params); + TbMemberPoints param = BeanUtil.toBean(params, TbMemberPoints.class); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(TbMemberPoints::getShopId, param.getShopId()); + wrapper.like(StrUtil.isNotEmpty(param.getMemberName()), TbMemberPoints::getMemberName, param.getMemberName()); + wrapper.like(StrUtil.isNotEmpty(param.getMobile()), TbMemberPoints::getMobile, param.getMobile()); + + wrapper.orderByDesc(TbMemberPoints::getId); + return wrapper; + } + + @Override + public Map page(Map params) { + MapProxy mapProxy = MapProxy.create(params); + int pageNum = mapProxy.getInt("page", 1); + int pageSize = mapProxy.getInt("size", 10); + LambdaQueryWrapper wrapper = getWrapper(params); + Page page = super.page(new Page<>(pageNum, pageSize), wrapper); + return PageUtil.toPlusPage(page.getRecords(), Convert.toInt(page.getTotal())); + } + + @Override + public TbMemberPoints getMemberPoints(Long memberId) { + return initMemberPoints(memberId); + } + + @Override + public TbMemberPoints initMemberPoints(Long memberId) { + TbShopUser shopUser = tbShopUserMapper.selectById(memberId); + if (shopUser == null) { + throw new BadRequestException("会员信息不存在"); + } + TbMemberPoints entity = super.getOne(Wrappers.lambdaQuery().eq(TbMemberPoints::getMemberId, memberId)); + if (entity == null) { + entity = new TbMemberPoints(); + entity.setCreateTime(new Date()); + entity.setAccountPoints(0); + } + entity.setShopId(Long.valueOf(shopUser.getShopId())); + entity.setMemberId(Long.valueOf(shopUser.getId())); + entity.setMemberName(shopUser.getName()); + entity.setAvatarUrl(shopUser.getHeadImg()); + entity.setMobile(shopUser.getTelephone()); + super.saveOrUpdate(entity); + return entity; + } + + @Override + public OrderDeductionPointsDTO getMemberUsablePoints(Long memberId, BigDecimal orderAmount) { + TbMemberPoints entity = initMemberPoints(memberId); + Long shopId = entity.getShopId(); + Integer accountPoints = entity.getAccountPoints(); + OrderDeductionPointsDTO dto = new OrderDeductionPointsDTO(); + dto.setAccountPoints(accountPoints); + dto.setUsable(false); + dto.setMaxDeductionAmount(BigDecimal.ZERO); + dto.setMinDeductionAmount(BigDecimal.ZERO); + TbPointsBasicSetting basic = tbPointsBasicSettingMapper.selectOne(Wrappers.lambdaQuery().eq(TbPointsBasicSetting::getShopId, shopId)); + if (basic == null) { + dto.setUnusableReason("商家未启用积分抵扣功能"); + return dto; + } + if (basic.getEnableDeduction() == 0) { + dto.setUnusableReason("商家未启用积分抵扣功能"); + return dto; + } + dto.setMinDeductionPoints(basic.getMinDeductionPoint()); + if (accountPoints == 0 || accountPoints < basic.getMinDeductionPoint()) { + dto.setUnusableReason("积分不足或小于最低使用门槛" + basic.getMinDeductionPoint()); + return dto; + } + // 下单抵扣积分比例 1元=?积分 + Integer equivalentPoints = basic.getEquivalentPoints(); + // 计算账户积分=?元 + BigDecimal accountYuan = NumberUtil.div(accountPoints, equivalentPoints); + // 下单最高抵扣比例 + BigDecimal maxDeductionRatio = basic.getMaxDeductionRatio(); + // 计算订单最多可以抵扣多少元 + BigDecimal orderYuan = NumberUtil.mul(orderAmount, NumberUtil.div(maxDeductionRatio, new BigDecimal("100"))); + // 积分余额足够 + if (NumberUtil.isGreaterOrEqual(accountYuan, orderYuan)) { + dto.setMaxDeductionAmount(orderYuan); + } else { + dto.setMaxDeductionAmount(NumberUtil.roundDown(accountYuan, 2)); + } + if (NumberUtil.isLess(dto.getMaxDeductionAmount(), new BigDecimal("0.01"))) { + dto.setUnusableReason("积分不足0.01元,无法进行抵扣"); + return dto; + } + // 计算抵扣门槛=?元 + BigDecimal minYuan = NumberUtil.div(basic.getMinDeductionPoint(), equivalentPoints); + if (NumberUtil.isLess(minYuan, new BigDecimal("0.01"))) { + dto.setMinDeductionAmount(new BigDecimal("0.01")); + } else { + dto.setMinDeductionAmount(NumberUtil.roundDown(minYuan, 2)); + } + dto.setEquivalentPoints(equivalentPoints); + dto.setOrderAmount(orderAmount); + 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 memberId, BigDecimal orderAmount, BigDecimal deductionAmount) { + OrderDeductionPointsDTO core = getMemberUsablePoints(memberId, orderAmount); + if (!core.getUsable()) { + throw new BadRequestException(core.getUnusableReason()); + } + if (NumberUtil.isGreater(deductionAmount, core.getMaxDeductionAmount())) { + throw new BadRequestException(StrUtil.format("抵扣金额不能超过最大抵扣金额{}元", core.getMaxDeductionAmount())); + } + if (NumberUtil.isGreater(deductionAmount, orderAmount)) { + throw new BadRequestException(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 memberId, BigDecimal orderAmount, int points) { + OrderDeductionPointsDTO core = getMemberUsablePoints(memberId, orderAmount); + if (!core.getUsable()) { + throw new BadRequestException(core.getUnusableReason()); + } + if (points < core.getMinDeductionPoints()) { + throw new BadRequestException(StrUtil.format("使用积分不能低于使用门槛(每次最少使用{}积分)", core.getMinDeductionPoints())); + } + if (points > core.getMaxUsablePoints()) { + throw new BadRequestException(StrUtil.format("使用积分不能超过最大使用限制{}", core.getMaxUsablePoints())); + } + BigDecimal mul = NumberUtil.mul(new BigDecimal("0.01"), core.getEquivalentPoints()); + int minPoints = NumberUtil.round(mul, 0, RoundingMode.CEILING).intValue(); + if (points < minPoints) { + throw new BadRequestException(StrUtil.format("使用积分不能低于{}(0.01元)", minPoints)); + } + BigDecimal money = NumberUtil.mul(points, NumberUtil.div(BigDecimal.ONE, core.getEquivalentPoints())); + return NumberUtil.roundDown(money, 2); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deductPoints(Long memberId, int points, String content, Long orderId) { + TbMemberPoints entity = initMemberPoints(memberId); + // 扣除账户积分 + entity.setAccountPoints(entity.getAccountPoints() - points); + entity.setLastPointsChangeTime(new Date()); + entity.setLastFloatPoints(-points); + + // 记录积分变动记录 + TbMemberPointsLog log = new TbMemberPointsLog(); + log.setShopId(entity.getShopId()); + log.setMemberId(entity.getMemberId()); + log.setMemberName(entity.getMemberName()); + log.setAvatarUrl(entity.getAvatarUrl()); + log.setMobile(entity.getMobile()); + log.setContent(content); + log.setFloatType("subtract"); + log.setFloatPoints(-points); + log.setCreateTime(new Date()); + // 有关联订单的需要回置订单表的相关积分使用字段 + if (orderId != null) { + TbOrderInfo orderInfo = tbOrderInfoMapper.selectById(orderId); + if (orderInfo != null) { + log.setOrderNo(orderInfo.getOrderNo()); + // TODO 是否需要回执“使用的积分数量(points_num)”和“积分抵扣金额(points_discount_amount)”,目前不清楚是创建订单的时候置入还是支付完成后回调时置入需要商议后决定 + } + } + super.updateById(entity); + tbMemberPointsLogMapper.insert(log); + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean addPoints(Long memberId, int points, String content, Long orderId) { + TbMemberPoints entity = initMemberPoints(memberId); + // 增加账户积分 + entity.setAccountPoints(entity.getAccountPoints() + points); + entity.setLastPointsChangeTime(new Date()); + entity.setLastFloatPoints(points); + // 记录积分变动记录 + TbMemberPointsLog log = new TbMemberPointsLog(); + log.setShopId(entity.getShopId()); + log.setMemberId(entity.getMemberId()); + log.setMemberName(entity.getMemberName()); + log.setAvatarUrl(entity.getAvatarUrl()); + log.setMobile(entity.getMobile()); + log.setContent(content); + log.setFloatType("add"); + log.setFloatPoints(points); + log.setCreateTime(new Date()); + // 有关联订单的需要回置订单表的相关积分使用字段 + if (orderId != null) { + TbOrderInfo orderInfo = tbOrderInfoMapper.selectById(orderId); + if (orderInfo != null) { + log.setOrderNo(orderInfo.getOrderNo()); + } + } + super.updateById(entity); + tbMemberPointsLogMapper.insert(log); + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void consumeAwardPoints(Long memberId, Long orderId) { + TbOrderInfo orderInfo = tbOrderInfoMapper.selectById(orderId); + if (orderInfo == null) { + throw new BadRequestException("订单不存在"); + } + BigDecimal payAmount = orderInfo.getPayAmount(); + if (NumberUtil.isLessOrEqual(payAmount, BigDecimal.ZERO)) { + return; + } + TbPointsBasicSetting basicSetting = tbPointsBasicSettingService.getByShopId(Convert.toLong(orderInfo.getShopId())); + if (basicSetting == null) { + return; + } + Integer enableRewards = basicSetting.getEnableRewards(); + if (enableRewards == 0) { + return; + } + BigDecimal consumeAmount = basicSetting.getConsumeAmount(); + if (consumeAmount == null) { + return; + } + if (NumberUtil.isLessOrEqual(consumeAmount, BigDecimal.ZERO)) { + return; + } + BigDecimal awardPoints = NumberUtil.roundDown(NumberUtil.div(payAmount, consumeAmount), 0); + addPoints(memberId, awardPoints.intValue(), StrUtil.format("消费¥{}送{}积分", payAmount, awardPoints.intValue()), orderId); + } + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/impl/TbPointsBasicSettingServiceImpl.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/impl/TbPointsBasicSettingServiceImpl.java new file mode 100644 index 00000000..072bf653 --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/impl/TbPointsBasicSettingServiceImpl.java @@ -0,0 +1,51 @@ +package cn.ysk.cashier.mybatis.service.impl; + +import cn.hutool.core.lang.Assert; +import cn.ysk.cashier.exception.BadRequestException; +import cn.ysk.cashier.mybatis.entity.TbPointsBasicSetting; +import cn.ysk.cashier.mybatis.mapper.TbPointsBasicSettingMapper; +import cn.ysk.cashier.mybatis.service.TbPointsBasicSettingService; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; + +/** + * 积分基本设置 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@Service +public class TbPointsBasicSettingServiceImpl extends ServiceImpl implements TbPointsBasicSettingService { + + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean save(TbPointsBasicSetting entity) { + try { + Assert.notNull(entity.getShopId(), "{}({})不能为空", "店铺id","shopId"); + Assert.notNull(entity.getEnableRewards(), "{}({})不能为空", "开启消费赠送积分","enableRewards"); + Assert.notNull(entity.getConsumeAmount(), "{}({})不能为空", "每消费xx元赠送1积分","consumeAmount"); + Assert.notNull(entity.getEnableDeduction(), "{}({})不能为空", "开启下单积分抵扣","enableDeduction"); + Assert.notNull(entity.getMinDeductionPoint(), "{}({})不能为空", "下单积分抵扣门槛","minDeductionPoint"); + Assert.notNull(entity.getMaxDeductionRatio(), "{}({})不能为空", "下单最高抵扣比例","maxDeductionRatio"); + Assert.notNull(entity.getEquivalentPoints(), "{}({})不能为空", "下单抵扣积分比例","equivalentPoints"); + Assert.notNull(entity.getEnablePointsMall(), "{}({})不能为空", "开启积分商城","enablePointsMall"); + Assert.notEmpty(entity.getBrowseMode(), "{}({})不能为空", "浏览模式","browseMode"); + } catch (IllegalArgumentException e) { + throw new BadRequestException(e.getMessage()); + } + entity.setCreateTime(new Date()); + super.remove(Wrappers.lambdaQuery().eq(TbPointsBasicSetting::getShopId, entity.getShopId())); + super.save(entity); + return true; + } + + @Override + public TbPointsBasicSetting getByShopId(Long shopId) { + return super.getOne(Wrappers.lambdaQuery().eq(TbPointsBasicSetting::getShopId, shopId)); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/impl/TbPointsExchangeRecordServiceImpl.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/impl/TbPointsExchangeRecordServiceImpl.java new file mode 100644 index 00000000..532f9e90 --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/impl/TbPointsExchangeRecordServiceImpl.java @@ -0,0 +1,195 @@ +package cn.ysk.cashier.mybatis.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.Snowflake; +import cn.hutool.core.map.MapProxy; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.StrUtil; +import cn.ysk.cashier.exception.BadRequestException; +import cn.ysk.cashier.mybatis.entity.*; +import cn.ysk.cashier.mybatis.mapper.*; +import cn.ysk.cashier.mybatis.service.TbPointsExchangeRecordService; +import cn.ysk.cashier.utils.PageUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 积分兑换记录 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@Service +public class TbPointsExchangeRecordServiceImpl extends ServiceImpl implements TbPointsExchangeRecordService { + + @Resource + private TbPointsBasicSettingMapper tbPointsBasicSettingMapper; + + @Resource + private TbPointsGoodsSettingMapper tbPointsGoodsSettingMapper; + + @Resource + private TbMemberPointsMapper tbMemberPointsMapper; + + @Resource + private TbMemberPointsLogMapper tbMemberPointsLogMapper; + + private LambdaQueryWrapper getWrapper(Map params) { + MapProxy mapProxy = MapProxy.create(params); + String keywords = mapProxy.getStr("keywords"); + String beginDate = mapProxy.getStr("beginDate"); + String endDate = mapProxy.getStr("endDate"); + TbPointsExchangeRecord param = BeanUtil.toBean(params, TbPointsExchangeRecord.class); + + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(TbPointsExchangeRecord::getShopId, param.getShopId()); + wrapper.eq(StrUtil.isNotEmpty(param.getPickupMethod()), TbPointsExchangeRecord::getPickupMethod, param.getPickupMethod()); + wrapper.eq(StrUtil.isNotEmpty(param.getStatus()), TbPointsExchangeRecord::getStatus, param.getStatus()); + wrapper.eq(param.getMemberId() != null, TbPointsExchangeRecord::getMemberId, param.getMemberId()); + if (StrUtil.isNotEmpty(keywords)) { + wrapper.nested(i -> i.like(TbPointsExchangeRecord::getOrderNo, keywords).or().like(TbPointsExchangeRecord::getCouponCode, keywords)); + } + if (StrUtil.isNotEmpty(beginDate)) { + wrapper.apply("create_time >= str_to_date({0}, '%Y-%m-%d %H:%i:%s')", beginDate + " 00:00:00"); + } + if (StrUtil.isNotEmpty(endDate)) { + wrapper.apply("create_time <= str_to_date({0}, '%Y-%m-%d %H:%i:%s')", endDate + " 23:59:59"); + } + wrapper.orderByDesc(TbPointsExchangeRecord::getId); + return wrapper; + } + + @Override + public Map page(Map params) { + MapProxy mapProxy = MapProxy.create(params); + int pageNum = mapProxy.getInt("page", 1); + int pageSize = mapProxy.getInt("size", 10); + LambdaQueryWrapper wrapper = getWrapper(params); + Page page = super.page(new Page<>(pageNum, pageSize), wrapper); + return PageUtil.toPlusPage(page.getRecords(), Convert.toInt(page.getTotal())); + } + + @Override + public TbPointsExchangeRecord get(Long id) { + return super.getById(id); + } + + @Override + public void checkout(String couponCode) { + if (StrUtil.isBlank(couponCode)) { + throw new BadRequestException("兑换券券码不能为空"); + } + TbPointsExchangeRecord entity = super.getOne(Wrappers.lambdaQuery().eq(TbPointsExchangeRecord::getCouponCode, couponCode)); + if (entity == null) { + throw new BadRequestException("兑换券券码无效"); + } + entity.setStatus("done"); + entity.setUpdateTime(new Date()); + super.updateById(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public TbPointsExchangeRecord exchange(TbPointsExchangeRecord record) { + try { + Assert.notNull(record.getShopId(), "{}({})不能为空", "店铺id", "shopId"); + Assert.notNull(record.getPointsGoodsId(), "{}({})不能为空", "积分商品id", "pointsGoodsId"); + Assert.notEmpty(record.getPickupMethod(), "{}({})不能为空", "领取方式", "pickupMethod"); + Assert.notNull(record.getMemberId(), "{}({})不能为空", "会员id", "memberId"); + Assert.notEmpty(record.getMemberName(), "{}({})不能为空", "会员名称", "memberName"); + Assert.notEmpty(record.getMobile(), "{}({})不能为空", "手机号码", "mobile"); + } catch (IllegalArgumentException e) { + throw new BadRequestException(e.getMessage()); + } + TbPointsBasicSetting basic = tbPointsBasicSettingMapper.selectOne(Wrappers.lambdaQuery().eq(TbPointsBasicSetting::getShopId, record.getShopId())); + if (basic == null) { + throw new BadRequestException("未配置积分锁客基本设置"); + } + if (basic.getEnablePointsMall() != 1) { + throw new BadRequestException("积分商城未开启"); + } + TbPointsGoodsSetting goods = tbPointsGoodsSettingMapper.selectById(record.getPointsGoodsId()); + if (goods == null) { + throw new BadRequestException("兑换的商品信息不存在"); + } + record.setPointsGoodsName(goods.getGoodsName()); + record.setGoodsImageUrl(goods.getGoodsImageUrl()); + + Integer status = goods.getStatus(); + if (status != 1) { + throw new BadRequestException("兑换的商品已下架"); + } + Integer quantity = goods.getQuantity(); + if (quantity <= 0) { + throw new BadRequestException("兑换的商品库存不足"); + } + TbMemberPoints memberPoints = tbMemberPointsMapper.selectOne(Wrappers.lambdaQuery().eq(TbMemberPoints::getMobile, record.getMobile())); + if (memberPoints == null) { + throw new BadRequestException("该会员积分不足无法兑换这个商品"); + } + Integer accountPoints = memberPoints.getAccountPoints(); + Integer requiredPoints = goods.getRequiredPoints(); + if (accountPoints < requiredPoints) { + throw new BadRequestException("该会员积分不足无法兑换这个商品"); + } + BigDecimal extraPrice = goods.getExtraPrice(); + record.setExtraPaymentAmount(extraPrice); + record.setSpendPoints(requiredPoints); + Snowflake seqNo = IdUtil.getSnowflake(0, 0); + String orderNo = DateUtil.format(new Date(), "yyyyMMddHH") + StrUtil.subSuf(seqNo.nextIdStr(), -12); + record.setOrderNo(orderNo); + record.setCouponCode(IdUtil.getSnowflakeNextIdStr()); + record.setStatus("waiting"); + record.setCreateTime(new Date()); + // 生成订单 + super.save(record); + // 扣减积分 + memberPoints.setAccountPoints(accountPoints - requiredPoints); + memberPoints.setLastPointsChangeTime(new Date()); + memberPoints.setLastFloatPoints(-requiredPoints); + tbMemberPointsMapper.updateById(memberPoints); + // 扣减库存 + goods.setQuantity(quantity - 1); + goods.setUpdateTime(new Date()); + tbPointsGoodsSettingMapper.updateById(goods); + // 记录积分浮动流水 + TbMemberPointsLog log = new TbMemberPointsLog(); + log.setShopId(record.getShopId()); + log.setMemberId(record.getMemberId()); + log.setMemberName(record.getMemberName()); + log.setMobile(record.getMobile()); + log.setAvatarUrl(record.getAvatarUrl()); + log.setContent(StrUtil.format("兑换商品:{} * {}", record.getPointsGoodsName(), "1")); + log.setFloatType("subtract"); + log.setFloatPoints(-requiredPoints); + log.setCreateTime(new Date()); + tbMemberPointsLogMapper.insert(log); + return record; + } + + @Override + public Map total(Map params) { + LambdaQueryWrapper wrapper = getWrapper(params); + wrapper.select(TbPointsExchangeRecord::getCount, TbPointsExchangeRecord::getTotalAmount); + TbPointsExchangeRecord summary = baseMapper.selectOne(wrapper); + Map result = new HashMap<>(2); + result.put("count", summary.getCount()); + result.put("totalAmount", NumberUtil.null2Zero(summary.getTotalAmount())); + return result; + } + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/impl/TbPointsGoodsSettingServiceImpl.java b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/impl/TbPointsGoodsSettingServiceImpl.java new file mode 100644 index 00000000..65833485 --- /dev/null +++ b/eladmin-system/src/main/java/cn/ysk/cashier/mybatis/service/impl/TbPointsGoodsSettingServiceImpl.java @@ -0,0 +1,94 @@ +package cn.ysk.cashier.mybatis.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapProxy; +import cn.hutool.core.util.StrUtil; +import cn.ysk.cashier.exception.BadRequestException; +import cn.ysk.cashier.mybatis.entity.TbPointsGoodsSetting; +import cn.ysk.cashier.mybatis.mapper.TbPointsGoodsSettingMapper; +import cn.ysk.cashier.mybatis.service.TbPointsGoodsSettingService; +import cn.ysk.cashier.utils.PageUtil; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.Map; + +/** + * 积分商品设置 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 2.0 2024-10-25 + */ +@Service +public class TbPointsGoodsSettingServiceImpl extends ServiceImpl implements TbPointsGoodsSettingService { + + + @Override + public Map page(Map params) { + MapProxy mapProxy = MapProxy.create(params); + int pageNum = mapProxy.getInt("page", 1); + int pageSize = mapProxy.getInt("size", 10); + TbPointsGoodsSetting param = BeanUtil.toBean(params, TbPointsGoodsSetting.class); + Page pg = new Page<>(pageNum, pageSize); + pg.addOrder(OrderItem.desc("sort")); + Page page = baseMapper.selectPage(pg, + Wrappers.lambdaQuery() + .eq(TbPointsGoodsSetting::getShopId, param.getShopId()) + .like(StrUtil.isNotEmpty(param.getGoodsName()), TbPointsGoodsSetting::getGoodsName, param.getGoodsName()) + .like(StrUtil.isNotEmpty(param.getGoodsCategory()), TbPointsGoodsSetting::getGoodsCategory, param.getGoodsCategory()) + .eq(param.getStatus() != null, TbPointsGoodsSetting::getStatus, param.getStatus()) + .orderByDesc(TbPointsGoodsSetting::getId) + ); + return PageUtil.toPlusPage(page.getRecords(), Convert.toInt(page.getTotal())); + } + + @Override + public boolean save(TbPointsGoodsSetting entity) { + try { + Assert.notNull(entity.getShopId(), "{}({})不能为空", "店铺id", "shopId"); + Assert.notEmpty(entity.getGoodsCategory(), "{}({})不能为空", "商品类型", "goodsCategory"); + Assert.notEmpty(entity.getGoodsName(), "{}({})不能为空", "商品名称", "goodsName"); + //Assert.notNull(entity.getGoodsImageUrl(), "{}({})不能为空", "商品图片URL","goodsImageUrl"); + Assert.notNull(entity.getRequiredPoints(), "{}({})不能为空", "所需积分", "requiredPoints"); + Assert.notNull(entity.getExtraPrice(), "{}({})不能为空", "额外价格", "extraPrice"); + Assert.notNull(entity.getSort(), "{}({})不能为空", "排序(权重)", "sort"); + Assert.notNull(entity.getQuantity(), "{}({})不能为空", "数量", "quantity"); + //Assert.notEmpty(entity.getGoodsDescription(), "{}({})不能为空", "商品详情","goodsDescription"); + Assert.notNull(entity.getStatus(), "{}({})不能为空", "是否上架", "status"); + } catch (IllegalArgumentException e) { + throw new BadRequestException(e.getMessage()); + } + entity.setCreateTime(new Date()); + return super.save(entity); + } + + @Override + public boolean update(TbPointsGoodsSetting dto) { + try { + Assert.notNull(dto.getId(), "{}不能为空", "商品id"); + Assert.notNull(dto.getShopId(), "{}({})不能为空", "店铺id", "shopId"); + Assert.notEmpty(dto.getGoodsCategory(), "{}({})不能为空", "商品类型", "goodsCategory"); + Assert.notEmpty(dto.getGoodsName(), "{}({})不能为空", "商品名称", "goodsName"); + //Assert.notNull(entity.getGoodsImageUrl(), "{}({})不能为空", "商品图片URL","goodsImageUrl"); + Assert.notNull(dto.getRequiredPoints(), "{}({})不能为空", "所需积分", "requiredPoints"); + Assert.notNull(dto.getExtraPrice(), "{}({})不能为空", "额外价格", "extraPrice"); + Assert.notNull(dto.getSort(), "{}({})不能为空", "排序(权重)", "sort"); + Assert.notNull(dto.getQuantity(), "{}({})不能为空", "数量", "quantity"); + //Assert.notEmpty(entity.getGoodsDescription(), "{}({})不能为空", "商品详情","goodsDescription"); + Assert.notNull(dto.getStatus(), "{}({})不能为空", "是否上架", "status"); + } catch (IllegalArgumentException e) { + throw new BadRequestException(e.getMessage()); + } + TbPointsGoodsSetting entity = super.getById(dto.getId()); + BeanUtil.copyProperties(dto, entity, CopyOptions.create().setIgnoreNullValue(false).setIgnoreProperties("createTime")); + entity.setUpdateTime(new Date()); + return super.updateById(entity); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/resources/mapper/plus/TbMemberPointsDao.xml b/eladmin-system/src/main/resources/mapper/plus/TbMemberPointsDao.xml new file mode 100644 index 00000000..2d0c97c1 --- /dev/null +++ b/eladmin-system/src/main/resources/mapper/plus/TbMemberPointsDao.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/eladmin-system/src/main/resources/mapper/plus/TbMemberPointsLogDao.xml b/eladmin-system/src/main/resources/mapper/plus/TbMemberPointsLogDao.xml new file mode 100644 index 00000000..e491c1f3 --- /dev/null +++ b/eladmin-system/src/main/resources/mapper/plus/TbMemberPointsLogDao.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/eladmin-system/src/main/resources/mapper/plus/TbPointsBasicSettingDao.xml b/eladmin-system/src/main/resources/mapper/plus/TbPointsBasicSettingDao.xml new file mode 100644 index 00000000..e9449ec2 --- /dev/null +++ b/eladmin-system/src/main/resources/mapper/plus/TbPointsBasicSettingDao.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/eladmin-system/src/main/resources/mapper/plus/TbPointsExchangeRecordDao.xml b/eladmin-system/src/main/resources/mapper/plus/TbPointsExchangeRecordDao.xml new file mode 100644 index 00000000..c9e6387b --- /dev/null +++ b/eladmin-system/src/main/resources/mapper/plus/TbPointsExchangeRecordDao.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/eladmin-system/src/main/resources/mapper/plus/TbPointsGoodsSettingDao.xml b/eladmin-system/src/main/resources/mapper/plus/TbPointsGoodsSettingDao.xml new file mode 100644 index 00000000..120e2743 --- /dev/null +++ b/eladmin-system/src/main/resources/mapper/plus/TbPointsGoodsSettingDao.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file