diff --git a/cash-api/account-server/src/main/java/com/czg/controller/admin/MemberController.java b/cash-api/account-server/src/main/java/com/czg/controller/admin/MemberController.java new file mode 100644 index 00000000..9ea8f3c4 --- /dev/null +++ b/cash-api/account-server/src/main/java/com/czg/controller/admin/MemberController.java @@ -0,0 +1,103 @@ +package com.czg.controller.admin; + +import com.czg.account.dto.MemberConfigDTO; +import com.czg.account.dto.MemberLevelDTO; +import com.czg.account.entity.MemberLevelConfig; +import com.czg.account.service.TbMemberConfigService; +import com.czg.account.vo.MemberConfigVO; +import com.czg.account.vo.MemberLevelVO; +import com.czg.resp.CzgResult; +import com.czg.sa.StpKit; +import com.czg.validator.group.UpdateGroup; +import com.mybatisflex.core.query.QueryWrapper; +import jakarta.annotation.Resource; +import jakarta.validation.groups.Default; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; + +/** + * 会员配置管理 + */ + +@RestController +@RequestMapping("/admin/member") +public class MemberController { + @Resource + private TbMemberConfigService memberConfigService; + + /** + * 配置信息获取 + * 权限标识: activate:list + */ +// @SaAdminCheckPermission(value = "member:detail", name = "会员配置列表") + @GetMapping + public CzgResult detail() { + return CzgResult.success(memberConfigService.detail(StpKit.USER.getShopId())); + } + + /** + * 配置信息修改 + * @return 是否成功 + */ +// @SaAdminCheckPermission(value = "member:edit", name = "会员配置列表") + @PostMapping + public CzgResult edit(@Validated @RequestBody MemberConfigDTO memberDTO) { + return CzgResult.success(memberConfigService.edit(StpKit.USER.getShopId(), memberDTO)); + } + + + /** + * 会员等级添加 + * @return 是否成功 + */ +// @SaAdminCheckPermission(value = "member:edit", name = "会员配置列表") + @PostMapping("/level") + public CzgResult addLevel(@Validated @RequestBody MemberLevelDTO levelDTO) { + return CzgResult.success(memberConfigService.addLevel(StpKit.USER.getShopId(), levelDTO)); + } + + /** + * 会员等级修改 + * @return 是否成功 + */ +// @SaAdminCheckPermission(value = "member:edit", name = "会员配置列表") + @PutMapping("/level") + public CzgResult editLevel(@Validated({UpdateGroup.class, Default.class}) @RequestBody MemberLevelDTO levelDTO) { + return CzgResult.success(memberConfigService.editLevel(StpKit.USER.getShopId(), levelDTO)); + } + + /** + * 会员等级删除 + * @return 是否成功 + */ +// @SaAdminCheckPermission(value = "member:edit", name = "会员配置列表") + @DeleteMapping("/level/{id}") + public CzgResult deleteLevel(@PathVariable Long id) { + return CzgResult.success(memberConfigService.remove(new QueryWrapper().eq(MemberLevelDTO::getId, id).eq(MemberLevelConfig::getShopId, StpKit.USER.getShopId()))); + } + + /** + * 会员等级列表 + * @return 是否成功 + */ +// @SaAdminCheckPermission(value = "member:edit", name = "会员配置列表") + @GetMapping("/level/list") + public CzgResult> levelList() { + return CzgResult.success(memberConfigService.listLevel(StpKit.USER.getShopId())); + } + + + /** + * 会员等级详情 + * @return 是否成功 + */ +// @SaAdminCheckPermission(value = "member:edit", name = "会员配置列表") + @PutMapping("/level/detail") + public CzgResult> levelList(@RequestParam Integer id) { + return CzgResult.success(memberConfigService.listLevel(StpKit.USER.getShopId())); + } + + +} diff --git a/cash-api/order-server/src/main/java/com/czg/controller/VipPayController.java b/cash-api/order-server/src/main/java/com/czg/controller/VipPayController.java index 1dee3eb0..1020f76a 100644 --- a/cash-api/order-server/src/main/java/com/czg/controller/VipPayController.java +++ b/cash-api/order-server/src/main/java/com/czg/controller/VipPayController.java @@ -5,6 +5,7 @@ import com.czg.annotation.SaStaffCheckPermission; import com.czg.entity.resp.CzgBaseResp; import com.czg.order.entity.OrderInfo; import com.czg.resp.CzgResult; +import com.czg.service.order.dto.VipMemberPayParamDTO; import com.czg.service.order.dto.VipPayParamDTO; import com.czg.service.order.dto.VipRefundDTO; import com.czg.service.order.service.PayService; @@ -69,6 +70,21 @@ public class VipPayController { return payService.ltPayVip(ServletUtil.getClientIP(request), payParam); } + /** + * 会员购买支付 + * @param request + * @param payParam + * @return + */ + @PostMapping("/ltPayMember") + @Debounce(value = "#payParam.memberOrderId") + public CzgResult> ltPayMember(HttpServletRequest request, @Validated @RequestBody VipMemberPayParamDTO payParam) { + AssertUtil.isNull(payParam.getShopUserId(), "购买失败 未指定店铺用户Id"); + payParam.setPlatformType(ServletUtil.getHeaderIgnoreCase(ServletUtil.getRequest(), "platformType")); + return payService.ltPayMember(ServletUtil.getClientIP(request), payParam); + } + + /** * 正扫 */ diff --git a/cash-api/order-server/src/main/java/com/czg/controller/user/UserOrderController.java b/cash-api/order-server/src/main/java/com/czg/controller/user/UserOrderController.java index 127892e8..048bfb01 100644 --- a/cash-api/order-server/src/main/java/com/czg/controller/user/UserOrderController.java +++ b/cash-api/order-server/src/main/java/com/czg/controller/user/UserOrderController.java @@ -2,10 +2,13 @@ package com.czg.controller.user; import com.czg.annotation.Debounce; import com.czg.exception.CzgException; +import com.czg.order.dto.MemberOrderDTO; import com.czg.order.dto.OrderCannelDTO; import com.czg.order.dto.OrderInfoAddDTO; import com.czg.order.dto.OrderInfoQueryDTO; +import com.czg.order.entity.MemberOrder; import com.czg.order.entity.OrderInfo; +import com.czg.order.service.MemberOrderService; import com.czg.order.service.OrderInfoService; import com.czg.order.vo.HistoryOrderVo; import com.czg.order.vo.OrderInfoVo; @@ -35,6 +38,8 @@ public class UserOrderController { @Resource private OrderInfoService orderInfoService; + @Resource + private MemberOrderService memberOrderService; /** * 订单列表 */ @@ -70,6 +75,21 @@ public class UserOrderController { return CzgResult.success(orderInfoService.createOrder(addDto)); } + /** + * 会员购买 + * @param orderDTO 充值信息 + * @return + */ + @PostMapping("/createMemberOrder") + public CzgResult createMemberOrder(@Validated @RequestBody MemberOrderDTO orderDTO) { + orderDTO.setPlatformType(ServletUtil.getHeaderIgnoreCase(ServletUtil.getRequest(), "platformType")); + orderDTO.setUserId(StpKit.USER.getLoginIdAsLong()); + orderDTO.setShopId(StpKit.USER.getShopId()); + orderDTO.setOrderType("miniapp"); + return CzgResult.success(memberOrderService.createMemberOrder(orderDTO)); + } + + @PutMapping("/{id}") public CzgResult upOrderIsDel(@PathVariable("id") Long id) { //效验数据 diff --git a/cash-common/cash-common-service/src/main/java/com/czg/account/dto/MemberConfigDTO.java b/cash-common/cash-common-service/src/main/java/com/czg/account/dto/MemberConfigDTO.java new file mode 100644 index 00000000..363affd9 --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/account/dto/MemberConfigDTO.java @@ -0,0 +1,101 @@ +package com.czg.account.dto; + +import com.czg.account.entity.ShopCoupon; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.List; + +/** + * @author Administrator + */ +@Data +public class MemberConfigDTO { + @Data + public static class ConfigList { + @NotBlank(message = "名称不为空") + private String name; + @NotNull(message = "价格不能为空") + @DecimalMin(value = "0.01", message = "价格不能小于0.01") + private BigDecimal price; + @Min(value = 0, message = "赠送成长值不能小于0") + private Integer reward; + private List couponList; + @NotBlank(message = "会员周期不为空") + private String circleTime; + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class condition { + private String code; + private String value; + } + + /** + * id + */ + @NotNull(message = "id不能为空") + private Long id; + + /** + * 提交生日/姓名 + */ + @NotNull(message = "提交生日/姓名不能为空") + private Integer isSubmitInfo; + + + /** + * 方案列表 + */ + @Valid + private List configList; + + /** + * 条件开通条件项 + */ + @Valid + private List conditionList; + + /** + * 购买开通金额 + */ + @DecimalMin(value = "0.01", message = "金额不能小于0.01") + private BigDecimal openAmount; + + /** + * 参与会员价门店 + */ + private List memberPriceShopIdList; + + /** + * 是否享受会员价 + */ + @NotNull(message = "是否享受会员价不能为空") + private Integer isMemberPrice; + + /** + * 每消费一元经验值 + */ + @Min(value = 0, message = "消费增成长值最小为0") + private Float costReward; + + /** + * 每充值一元经验值 + */ + @Min(value = 0, message = "充值增成长值最小为0") + private Float rechargeReward; + + /** + * 规则说明 + */ + @NotBlank(message = "规则说明不能为空") + private String remark; + + +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/account/dto/MemberLevelDTO.java b/cash-common/cash-common-service/src/main/java/com/czg/account/dto/MemberLevelDTO.java new file mode 100644 index 00000000..146fcf13 --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/account/dto/MemberLevelDTO.java @@ -0,0 +1,81 @@ +package com.czg.account.dto; + +import com.czg.account.entity.ShopCoupon; +import com.czg.validator.group.UpdateGroup; +import com.czg.validator.group.member.MemberLevelCycleRewardGroup; +import jakarta.validation.constraints.*; +import lombok.Data; + +import java.util.List; + +@Data +public class MemberLevelDTO { + /** + * ID + */ + @NotNull(message = "id不为空", groups = UpdateGroup.class) + private Long id; + + /** + * 说明 + */ + @Size(max = 200, message = "最大长度为200") + private String remark; + + /** + * 会员名称 + */ + @NotBlank(message = "会员名称不为空") + @Size(max = 30, message = "最大长度为30") + private String name; + + /** + * 所需成长值 + */ + @Min(value = 0, message = "成长值不能小于0") + @NotNull(message = "成长值不为空") + private Long experienceValue; + + /** + * 会员折扣 + */ + @NotNull(message = "会员折扣不为空") + @Min(value = 0, message = "会员折扣不能小于0") + private Integer discount; + + /** + * logo + */ + private String logo; + + /** + * 消费送积分,消费n元送1积分, 0为禁用 + */ + @DecimalMin(value = "0.01", message = "消费送积分不能小于0.01") + private Float costRewardPoints; + + /** + * 周期奖励状态 0禁用 1启用 + */ + @NotNull(message = "周期奖励状态不为空") + private Integer isCycleReward; + + /** + * 周期时间包含周 月 年 日 + */ + @NotBlank(message = "周期时间不为空", groups = MemberLevelCycleRewardGroup.class) + private String cycleTime; + + /** + * 赠送积分 + */ + @DecimalMin(value = "0.01", message = "赠送积分不能小于0.01") + private Float cycleRewardPoints; + + /** + * 优惠券列表 + */ + private List cycleRewardCouponList; + + +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/account/dto/TbMemberConfigDTO.java b/cash-common/cash-common-service/src/main/java/com/czg/account/dto/TbMemberConfigDTO.java new file mode 100644 index 00000000..85a4adde --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/account/dto/TbMemberConfigDTO.java @@ -0,0 +1,99 @@ + +package com.czg.account.dto; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import com.alibaba.fastjson2.annotation.JSONField; +import java.io.Serial; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 会员基础配置 实体类。 + * + * @author zs + * @since 2025-09-10 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TbMemberConfigDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + private Long id; + + /** + * 提交生日/姓名 + */ + private Integer submitInfo; + + /** + * 购买开通PAY 条件开通CONDITION + */ + private String openType; + + /** + * 方案列表 + */ + private String configList; + + /** + * 条件开通条件项 + */ + private String conditionInfo; + + /** + * 购买开通金额 + */ + private BigDecimal amount; + + /** + * 参与会员价门店 + */ + private String memberPriceShopIdList; + + /** + * 是否享受会员价 + */ + private Integer isMemberPrice; + + /** + * 每消费一元经验值 + */ + private Float costReward; + + /** + * 每充值一元经验值 + */ + private Float rechargeReward; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 规则说明 + */ + private String remark; + + /** + * 创建时间 + */ + @JSONField(format = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime createTime; + + /** + * 修改时间 + */ + @JSONField(format = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime updateTime; + +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/account/dto/TbMemberLevelConfigDTO.java b/cash-common/cash-common-service/src/main/java/com/czg/account/dto/TbMemberLevelConfigDTO.java new file mode 100644 index 00000000..96ded205 --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/account/dto/TbMemberLevelConfigDTO.java @@ -0,0 +1,93 @@ + +package com.czg.account.dto; + +import java.io.Serializable; +import java.time.LocalDateTime; +import com.alibaba.fastjson2.annotation.JSONField; +import java.io.Serial; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 会员等级配置 实体类。 + * + * @author zs + * @since 2025-09-10 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TbMemberLevelConfigDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + private Long id; + + /** + * 会员名称 + */ + private String name; + + /** + * 所需成长值 + */ + private Long experienceValue; + + /** + * 会员折扣 + */ + private Float discunt; + + /** + * logo + */ + private String logo; + + /** + * 消费送积分,消费n元送1积分, 0为禁用 + */ + private Float costRewardPoints; + + /** + * 周期奖励状态 0禁用 1启用 + */ + private Integer isCycleReward; + + /** + * 周期时间包含周 月 年 日 + */ + private String cycleTime; + + /** + * 赠送积分 + */ + private String cycleRewardPoints; + + /** + * 优惠券列表 + */ + private String cycleRewardCouponList; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 创建时间 + */ + @JSONField(format = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime createTime; + + /** + * 修改时间 + */ + @JSONField(format = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime updateTime; + +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/account/entity/MemberLevelConfig.java b/cash-common/cash-common-service/src/main/java/com/czg/account/entity/MemberLevelConfig.java new file mode 100644 index 00000000..ef816819 --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/account/entity/MemberLevelConfig.java @@ -0,0 +1,103 @@ +package com.czg.account.entity; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.KeyType; +import com.mybatisflex.annotation.Table; +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 zs + * @since 2025-09-10 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Table("tb_member_level_config") +public class MemberLevelConfig implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private String remark; + + /** + * ID + */ + @Id(keyType = KeyType.Auto) + private Long id; + + /** + * 会员名称 + */ + private String name; + + /** + * 所需成长值 + */ + private Long experienceValue; + + /** + * 会员折扣 + */ + private Integer discount; + + /** + * logo + */ + private String logo; + + /** + * 消费送积分,消费n元送1积分, 0为禁用 + */ + private Float costRewardPoints; + + /** + * 周期奖励状态 0禁用 1启用 + */ + private Integer isCycleReward; + + /** + * 周期时间包含周 月 年 日 + */ + private String cycleTime; + + /** + * 赠送积分 + */ + private Float cycleRewardPoints; + + /** + * 优惠券列表 + */ + private String cycleRewardCouponList; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 创建时间 + */ + @Column(onInsertValue = "now()") + private LocalDateTime createTime; + + /** + * 修改时间 + */ + @Column(onInsertValue = "now()", onUpdateValue = "now()") + private LocalDateTime updateTime; + +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/account/entity/ShopUser.java b/cash-common/cash-common-service/src/main/java/com/czg/account/entity/ShopUser.java index 1ca16bc4..4324657b 100644 --- a/cash-common/cash-common-service/src/main/java/com/czg/account/entity/ShopUser.java +++ b/cash-common/cash-common-service/src/main/java/com/czg/account/entity/ShopUser.java @@ -129,4 +129,8 @@ public class ShopUser implements Serializable { * 已经合并过来的用户信息,jsonArray格式,[{"id":1,"shopId":2,...},{"id":1,"shopId":2,...}] */ private String mergedUsers; + + private Long memberLevelId; + + private Long experience; } diff --git a/cash-common/cash-common-service/src/main/java/com/czg/account/entity/TbMemberConfig.java b/cash-common/cash-common-service/src/main/java/com/czg/account/entity/TbMemberConfig.java new file mode 100644 index 00000000..8417836a --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/account/entity/TbMemberConfig.java @@ -0,0 +1,102 @@ +package com.czg.account.entity; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.KeyType; +import com.mybatisflex.annotation.Table; +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 zs + * @since 2025-09-10 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Table("tb_member_config") +public class TbMemberConfig implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id(keyType = KeyType.Auto) + private Long id; + + /** + * 提交生日/姓名 + */ + private Integer isSubmitInfo; + + /** + * 方案列表 + */ + private String configList; + + /** + * 条件开通条件项 + */ + private String conditionList; + + /** + * 购买开通金额 + */ + private BigDecimal openAmount; + + /** + * 参与会员价门店 + */ + private String memberPriceShopIdList; + + /** + * 是否享受会员价 + */ + private Integer isMemberPrice; + + /** + * 每消费一元经验值 + */ + private Long costReward; + + /** + * 每充值一元经验值 + */ + private Long rechargeReward; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 规则说明 + */ + private String remark; + + /** + * 创建时间 + */ + @Column(onInsertValue = "now()") + private LocalDateTime createTime; + + /** + * 修改时间 + */ + @Column(onInsertValue = "now()", onUpdateValue = "now()") + private LocalDateTime updateTime; + +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/account/service/MemberLevelConfigService.java b/cash-common/cash-common-service/src/main/java/com/czg/account/service/MemberLevelConfigService.java new file mode 100644 index 00000000..8df02836 --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/account/service/MemberLevelConfigService.java @@ -0,0 +1,14 @@ +package com.czg.account.service; + +import com.mybatisflex.core.service.IService; +import com.czg.account.entity.MemberLevelConfig; + +/** + * 会员等级配置 服务层。 + * + * @author zs + * @since 2025-09-10 + */ +public interface MemberLevelConfigService extends IService { + +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/account/service/TbMemberConfigService.java b/cash-common/cash-common-service/src/main/java/com/czg/account/service/TbMemberConfigService.java new file mode 100644 index 00000000..8cadd6da --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/account/service/TbMemberConfigService.java @@ -0,0 +1,45 @@ +package com.czg.account.service; + +import com.czg.account.dto.MemberConfigDTO; +import com.czg.account.dto.MemberLevelDTO; +import com.czg.account.vo.MemberConfigVO; +import com.czg.account.vo.MemberLevelVO; +import com.mybatisflex.core.service.IService; +import com.czg.account.entity.TbMemberConfig; + +import java.math.BigDecimal; +import java.util.ArrayList; + +/** + * 会员基础配置 服务层。 + * + * @author zs + * @since 2025-09-10 + */ +public interface TbMemberConfigService extends IService { + + MemberConfigVO detail(Long shopId); + + Boolean edit(Long shopId, MemberConfigDTO memberDTO); + + Boolean addLevel(Long shopId, MemberLevelDTO levelDTO); + + Boolean editLevel(Long shopId, MemberLevelDTO levelDTO); + + ArrayList listLevel(Long shopId); + + /** + * 根据传入的用户Id,校验是否符合条件,符合加入会员 + * @param shopId 店铺id + * @param userId 用户id + * @return 是否加入成功 + */ + boolean joinMember(Long shopId, Long userId); + + /** + * 发放会员奖励 + * @param isCost 是否是消费 true 消费 false 充值 + */ + boolean deliver(Long shopId, Long userId, BigDecimal money, boolean isCost); + +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/account/vo/MemberConfigVO.java b/cash-common/cash-common-service/src/main/java/com/czg/account/vo/MemberConfigVO.java new file mode 100644 index 00000000..d2ba2aac --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/account/vo/MemberConfigVO.java @@ -0,0 +1,84 @@ +package com.czg.account.vo; + +import com.czg.account.dto.MemberConfigDTO; +import com.mybatisflex.annotation.Column; +import jakarta.validation.Valid; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Data +public class MemberConfigVO { + /** + * id + */ + @NotNull(message = "id不能为空") + private Long id; + + /** + * 提交生日/姓名 + */ + @NotNull(message = "提交生日/姓名不能为空") + private Integer isSubmitInfo; + + + /** + * 方案列表 + */ + @Valid + private List configList; + + /** + * 条件开通条件项 + */ + @Valid + private List conditionList; + + /** + * 购买开通金额 + */ + @DecimalMin(value = "0.01", message = "金额不能小于0.01") + private BigDecimal openAmount; + + /** + * 参与会员价门店 + */ + private List memberPriceShopIdList; + + /** + * 是否享受会员价 + */ + @NotNull(message = "是否享受会员价不能为空") + private Integer isMemberPrice; + + /** + * 每消费一元经验值 + */ + @Min(value = 0, message = "消费增成长值最小为0") + private Long costReward; + + /** + * 每充值一元经验值 + */ + @Min(value = 0, message = "充值增成长值最小为0") + private Long rechargeReward; + + /** + * 规则说明 + */ + @NotBlank(message = "规则说明不能为空") + private String remark; + + private LocalDateTime createTime; + + /** + * 修改时间 + */ + private LocalDateTime updateTime; +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/account/vo/MemberLevelVO.java b/cash-common/cash-common-service/src/main/java/com/czg/account/vo/MemberLevelVO.java new file mode 100644 index 00000000..a21369e8 --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/account/vo/MemberLevelVO.java @@ -0,0 +1,85 @@ +package com.czg.account.vo; + +import com.czg.account.dto.MemberConfigDTO; +import com.czg.account.entity.ShopCoupon; +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.KeyType; +import jakarta.validation.Valid; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Data +public class MemberLevelVO { + /** + * ID + */ + private Long id; + + /** + * 会员名称 + */ + private String name; + + /** + * 所需成长值 + */ + private Long experienceValue; + + /** + * 会员折扣 + */ + private Integer discount; + + /** + * logo + */ + private String logo; + + /** + * 消费送积分,消费n元送1积分, 0为禁用 + */ + private Float costRewardPoints; + + /** + * 周期奖励状态 0禁用 1启用 + */ + private Integer isCycleReward; + + /** + * 周期时间包含周 月 年 日 + */ + private String cycleTime; + + /** + * 赠送积分 + */ + private Float cycleRewardPoints; + + /** + * 优惠券列表 + */ + private List cycleRewardCouponList; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 修改时间 + */ + private LocalDateTime updateTime; +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/order/dto/MemberOrderDTO.java b/cash-common/cash-common-service/src/main/java/com/czg/order/dto/MemberOrderDTO.java new file mode 100644 index 00000000..2602a4a6 --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/order/dto/MemberOrderDTO.java @@ -0,0 +1,54 @@ + +package com.czg.order.dto; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import com.alibaba.fastjson2.annotation.JSONField; +import java.io.Serial; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 会员充值订单 实体类。 + * + * @author zs + * @since 2025-09-11 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MemberOrderDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + + /** + * 店铺id + */ + @NotNull(message = "店铺id不能为空") + private Long shopId; + + + /** + * 名称 + */ + @NotBlank(message = "方案名称不为空") + private String name; + + + /** + * 数量 + */ + @NotNull(message = "数量不为空") + private Integer num; + private String platformType; + private Long userId; + private String orderType; + +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/order/entity/MemberOrder.java b/cash-common/cash-common-service/src/main/java/com/czg/order/entity/MemberOrder.java new file mode 100644 index 00000000..48ed1e08 --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/order/entity/MemberOrder.java @@ -0,0 +1,110 @@ +package com.czg.order.entity; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.KeyType; +import com.mybatisflex.annotation.Table; +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 zs + * @since 2025-09-11 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Table("tb_member_order") +public class MemberOrder implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Id(keyType = KeyType.Auto) + private Long id; + + /** + * 订单编号 + */ + private String orderNo; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 金额 + */ + private BigDecimal amount; + private BigDecimal price; + + /** + * 名称 + */ + private String name; + + /** + * 用户id + */ + private Long userId; + + /** + * 实际支付金额 + */ + private BigDecimal payAmount; + + /** + * 成长值 + */ + private Integer reward; + + /** + * 会员周期 + */ + private String circleTime; + + /** + * 优惠券信息 + */ + private String couponList; + + /** + * 状态 + */ + private String status; + + /** + * 创建时间 + */ + @Column(onInsertValue = "now()") + private LocalDateTime createTime; + + /** + * 修改时间 + */ + @Column(onInsertValue = "now()", onUpdateValue = "now()") + private LocalDateTime updateTime; + + /** + * 支付时间 + */ + private LocalDateTime payTime; + + /** + * 数量 + */ + private Integer num; + +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/order/service/MemberOrderService.java b/cash-common/cash-common-service/src/main/java/com/czg/order/service/MemberOrderService.java new file mode 100644 index 00000000..5d82c355 --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/order/service/MemberOrderService.java @@ -0,0 +1,16 @@ +package com.czg.order.service; + +import com.czg.order.dto.MemberOrderDTO; +import com.mybatisflex.core.service.IService; +import com.czg.order.entity.MemberOrder; + +/** + * 会员充值订单 服务层。 + * + * @author zs + * @since 2025-09-11 + */ +public interface MemberOrderService extends IService { + + MemberOrder createMemberOrder(MemberOrderDTO orderDTO); +} diff --git a/cash-common/cash-common-tools/src/main/java/com/czg/validator/group/member/MemberLevelCycleRewardGroup.java b/cash-common/cash-common-tools/src/main/java/com/czg/validator/group/member/MemberLevelCycleRewardGroup.java new file mode 100644 index 00000000..af442690 --- /dev/null +++ b/cash-common/cash-common-tools/src/main/java/com/czg/validator/group/member/MemberLevelCycleRewardGroup.java @@ -0,0 +1,11 @@ + + +package com.czg.validator.group.member; + +/** + * @author admin admin@cashier.com + * @since 1.0.0 + */ +public interface MemberLevelCycleRewardGroup { + +} diff --git a/cash-service/account-service/src/main/java/com/czg/service/account/mapper/TbMemberConfigMapper.java b/cash-service/account-service/src/main/java/com/czg/service/account/mapper/TbMemberConfigMapper.java new file mode 100644 index 00000000..872ec7a8 --- /dev/null +++ b/cash-service/account-service/src/main/java/com/czg/service/account/mapper/TbMemberConfigMapper.java @@ -0,0 +1,14 @@ +package com.czg.service.account.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.czg.account.entity.TbMemberConfig; + +/** + * 会员基础配置 映射层。 + * + * @author zs + * @since 2025-09-10 + */ +public interface TbMemberConfigMapper extends BaseMapper { + +} diff --git a/cash-service/account-service/src/main/java/com/czg/service/account/mapper/TbMemberLevelConfigMapper.java b/cash-service/account-service/src/main/java/com/czg/service/account/mapper/TbMemberLevelConfigMapper.java new file mode 100644 index 00000000..f2f224a2 --- /dev/null +++ b/cash-service/account-service/src/main/java/com/czg/service/account/mapper/TbMemberLevelConfigMapper.java @@ -0,0 +1,14 @@ +package com.czg.service.account.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.czg.account.entity.MemberLevelConfig; + +/** + * 会员等级配置 映射层。 + * + * @author zs + * @since 2025-09-10 + */ +public interface TbMemberLevelConfigMapper extends BaseMapper { + +} diff --git a/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/ShopUserServiceImpl.java b/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/ShopUserServiceImpl.java index 04324e39..2f846285 100644 --- a/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/ShopUserServiceImpl.java +++ b/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/ShopUserServiceImpl.java @@ -71,6 +71,8 @@ public class ShopUserServiceImpl extends ServiceImpl i private FreeDineConfigService freeDineConfigService; @Resource private ShopConfigMapper shopConfigMapper; + @DubboReference + private TbMemberConfigService memberConfigService; private ShopUser getUserInfo(Long shopId, Long shopUserId) { ShopUser shopUser = queryChain().eq(ShopUser::getShopId, shopId).eq(ShopUser::getId, shopUserId).one(); @@ -308,6 +310,8 @@ public class ShopUserServiceImpl extends ServiceImpl i shopUser.setUserId(null); shopUser.setShopId(null); } + + memberConfigService.joinMember(shopId, userId); return saveOrUpdate(shopUser); } diff --git a/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/TbMemberConfigServiceImpl.java b/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/TbMemberConfigServiceImpl.java new file mode 100644 index 00000000..45cdb002 --- /dev/null +++ b/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/TbMemberConfigServiceImpl.java @@ -0,0 +1,279 @@ +package com.czg.service.account.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.czg.account.dto.MemberConfigDTO; +import com.czg.account.dto.MemberLevelDTO; +import com.czg.account.entity.*; +import com.czg.account.service.*; +import com.czg.account.vo.MemberConfigVO; +import com.czg.account.vo.MemberLevelVO; +import com.czg.exception.CzgException; +import com.czg.order.entity.OrderInfo; +import com.czg.order.entity.OrderPayment; +import com.czg.order.service.OrderInfoService; +import com.czg.order.service.OrderPaymentService; +import com.czg.service.order.enums.OrderStatusEnums; +import com.czg.validator.ValidatorUtil; +import com.czg.validator.group.member.MemberLevelCycleRewardGroup; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.spring.service.impl.ServiceImpl; +import com.czg.service.account.mapper.TbMemberConfigMapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.dubbo.config.annotation.DubboReference; +import org.apache.dubbo.config.annotation.DubboService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * 会员基础配置 服务层实现。 + * + * @author zs + * @since 2025-09-10 + */ +@Slf4j +@DubboService +public class TbMemberConfigServiceImpl extends ServiceImpl implements TbMemberConfigService { + private final List conditionMap = CollUtil.toList("BIND_PHONE", "ORDER", "COST_AMOUNT", "RECHARGE_AMOUNT"); + + @Resource + private MemberLevelConfigService levelConfigService; + @Resource + private UserInfoService userInfoService; + @Resource + private ShopUserService shopUserService; + @DubboReference + private OrderInfoService orderInfoService; + @DubboReference + private OrderPaymentService orderPaymentService; + @DubboReference + private MemberPointsService memberPointsService; + + @Override + public MemberConfigVO detail(Long shopId) { + TbMemberConfig memberConfig = getOne(new QueryWrapper().eq(TbMemberConfig::getShopId, shopId)); + if (memberConfig == null) { + memberConfig = new TbMemberConfig(); + memberConfig.setShopId(shopId); + save(memberConfig); + memberConfig = getOne(new QueryWrapper().eq(TbMemberConfig::getShopId, shopId)); + } + + MemberConfigVO memberConfigVO = BeanUtil.copyProperties(memberConfig, MemberConfigVO.class, "configList", "conditionList"); + if (StrUtil.isNotBlank(memberConfig.getConfigList())) { + memberConfigVO.setConfigList(JSONArray.parseArray(memberConfig.getConfigList()).toList(MemberConfigDTO.ConfigList.class)); + } + + if (StrUtil.isNotBlank(memberConfig.getConditionList())) { + memberConfigVO.setConditionList(JSONArray.parseArray(memberConfig.getConditionList()).toList(MemberConfigDTO.condition.class)); + } + + if (StrUtil.isNotBlank(memberConfig.getMemberPriceShopIdList())) { + memberConfigVO.setMemberPriceShopIdList(JSONArray.parseArray(memberConfig.getMemberPriceShopIdList()).toList(Long.class)); + } + + return memberConfigVO; + } + + @Override + public Boolean edit(Long shopId, MemberConfigDTO memberDTO) { + TbMemberConfig memberConfig = getOne(new QueryWrapper().eq(TbMemberConfig::getShopId, shopId)); + BeanUtil.copyProperties(memberDTO, memberConfig); + if ((memberDTO.getConfigList() == null || memberDTO.getConfigList().isEmpty()) && memberDTO.getOpenAmount() == null) { + throw new CzgException("会员开通方式必须选择一个"); + } + + + if (memberDTO.getConfigList() != null && !memberDTO.getConfigList().isEmpty()) { + memberDTO.getConfigList().forEach(item -> { + if (item.getReward() == null && (item.getCouponList() == null || item.getCouponList().isEmpty())) { + throw new CzgException("方案列表中赠送成长值和赠送优惠券不能同时为空"); + } + }); + memberConfig.setConfigList(JSONObject.toJSONString(memberDTO.getConfigList())); + memberConfig.setConditionList(null); + } + + if (memberConfig.getConfigList() != null && !memberConfig.getConfigList().isEmpty() && memberConfig.getOpenAmount() != null) { + throw new CzgException("会员开通方式为单选条件"); + } + + if (memberDTO.getConditionList() != null && !memberDTO.getConditionList().isEmpty()) { +// ArrayList conditionList = CollUtil.newArrayList(conditionMap); + memberDTO.getConditionList().forEach(item -> { + if (!conditionMap.contains(item.getCode())) { + throw new CzgException("条件列表中code值错误"); + } + }); +// conditionList.forEach(item -> { +// memberDTO.getConditionList().add(new MemberDTO.condition(item, null)); +// }); + + memberConfig.setConditionList(JSONObject.toJSONString(memberDTO.getConditionList())); + memberConfig.setOpenAmount(null); + } + + if (memberDTO.getMemberPriceShopIdList() != null && !memberDTO.getMemberPriceShopIdList().isEmpty()) { + memberConfig.setMemberPriceShopIdList(JSONObject.toJSONString(memberDTO.getMemberPriceShopIdList())); + } + return updateById(memberConfig, false); + } + + private void checkLevelDto(Long shopId, MemberLevelDTO levelDTO) { + long count = levelConfigService.count(new QueryWrapper().eq(MemberLevelConfig::getShopId, shopId).eq(MemberLevelConfig::getName, levelDTO.getName()).ne(MemberLevelConfig::getId, levelDTO.getId())); + if (count > 0) { + throw new CzgException("会员等级名称已存在"); + } + + MemberLevelConfig lastConfig = levelConfigService.getOne(new QueryWrapper().eq(MemberLevelConfig::getShopId, shopId) + .lt(MemberLevelConfig::getId, levelDTO.getId()) + .limit(1).orderBy(MemberLevelConfig::getId, false).ne(MemberLevelConfig::getId, levelDTO.getId())); + if (lastConfig == null && levelDTO.getExperienceValue() > 0) { + throw new CzgException("1级时本字段必须为0"); + } else if (lastConfig != null && levelDTO.getExperienceValue() <= lastConfig.getExperienceValue()) { + throw new CzgException("会员等级经验值必须大于上一等级经验值"); + } + + if (levelDTO.getIsCycleReward() == 1) { + if (levelDTO.getCycleRewardPoints() == null && (levelDTO.getCycleRewardCouponList() == null || levelDTO.getCycleRewardCouponList().isEmpty())) { + throw new CzgException("周期奖励成长值和优惠券不能同时为空"); + } + + ValidatorUtil.validateEntity(levelDTO, MemberLevelCycleRewardGroup.class); + } + } + + @Override + public Boolean addLevel(Long shopId, MemberLevelDTO levelDTO) { + checkLevelDto(shopId, levelDTO); + + MemberLevelConfig levelConfig = BeanUtil.copyProperties(levelDTO, MemberLevelConfig.class); + levelConfig.setShopId(shopId); + if (levelDTO.getCycleRewardCouponList() != null && !levelDTO.getCycleRewardCouponList().isEmpty()) { + levelConfig.setCycleRewardCouponList(JSONObject.toJSONString(levelDTO.getCycleRewardCouponList())); + } + + return levelConfigService.save(levelConfig); + } + + @Override + public Boolean editLevel(Long shopId, MemberLevelDTO levelDTO) { + MemberLevelConfig levelConfig = levelConfigService.getOne(new QueryWrapper().eq(MemberLevelConfig::getId, levelDTO.getId()).eq(MemberLevelConfig::getShopId, shopId)); + Optional.ofNullable(levelConfig).orElseThrow(() -> new CzgException("会员等级不存在")); + checkLevelDto(shopId, levelDTO); + BeanUtil.copyProperties(levelDTO, levelConfig); + + if (levelDTO.getCycleRewardCouponList() != null && !levelDTO.getCycleRewardCouponList().isEmpty()) { + levelConfig.setCycleRewardCouponList(JSONObject.toJSONString(levelDTO.getCycleRewardCouponList())); + } else { + levelConfig.setCycleRewardCouponList(null); + } + return levelConfigService.updateById(levelConfig, false); + } + + @Override + public ArrayList listLevel(Long shopId) { + ArrayList memberLevelVOS = new ArrayList<>(); + levelConfigService.list(new QueryWrapper().eq(MemberLevelConfig::getShopId, shopId)).forEach(item -> { + MemberLevelVO memberLevelVO = BeanUtil.copyProperties(item, MemberLevelVO.class, "cycleRewardCouponList"); + if (StrUtil.isNotBlank(item.getCycleRewardCouponList())) { + memberLevelVO.setCycleRewardCouponList(JSONArray.parseArray(item.getCycleRewardCouponList()).toList(ShopCoupon.class)); + } + memberLevelVOS.add(memberLevelVO); + }); + + return memberLevelVOS; + } + + @Override + public boolean deliver(Long shopId, Long userId, BigDecimal money, boolean isCost) { + ShopUser shopUser = shopUserService.getOne(new QueryWrapper().eq(ShopUser::getShopId, shopId).eq(ShopUser::getUserId, userId)); + if (shopUser == null || shopUser.getIsVip() == 0) { + return false; + } + MemberConfigVO memberConfig = detail(shopId); + if (memberConfig == null) { + log.warn("会员配置不存在, 店铺id: {}", shopId); + return false; + } + + if (shopUser.getExperience() == null) { + shopUser.setExperience(0L); + } + + // 消费经验 + if (memberConfig.getCostReward() != null && isCost) { + shopUser.setExperience(money.longValue() * memberConfig.getCostReward() + shopUser.getExperience()); + } + + // 充值经验 + if (memberConfig.getRechargeReward() != null && !isCost) { + shopUser.setExperience(money.longValue() * memberConfig.getRechargeReward() + shopUser.getExperience()); + } + + MemberLevelConfig levelConfig = levelConfigService.getById(shopUser.getMemberLevelId()); + if (levelConfig == null) { + log.warn("会员等级配置不存在, 店铺id: {}, 等级id: {}", shopId, shopUser.getMemberLevelId()); + return false; + } + + // 消费送积分 + if (isCost && levelConfig.getCostRewardPoints() != null) { + int points = (int) (money.floatValue() / levelConfig.getCostRewardPoints()); + memberPointsService.addPoints(shopUser.getId(), points, "会员消费送积分", null); + } + return false; + } + + @Override + public boolean joinMember(Long shopId, Long userId) { + MemberConfigVO memberConfigVO = detail(shopId); + ShopUser shopUser = shopUserService.getOne(new QueryWrapper().eq(ShopUser::getShopId, shopId).eq(ShopUser::getUserId, userId)); + if (shopUser == null) { + log.warn("用户不存在, 店铺id: {}, 用户id: {}", shopId, userId); + return false; + } + boolean canOpen = false; + // 条件开通 + if (memberConfigVO.getOpenAmount() == null) { + for (MemberConfigDTO.condition item : memberConfigVO.getConditionList()) { + canOpen = switch (item.getCode()) { + case "BIND_PHONE" -> StrUtil.isNotBlank(shopUser.getPhone()); + case "ORDER" -> + orderInfoService.count(new QueryWrapper().eq(OrderInfo::getShopId, shopId).eq(OrderInfo::getUserId, userId) + .notIn(OrderInfo::getStatus, OrderStatusEnums.UNPAID.getCode(), OrderStatusEnums.CANCELLED.getCode())) > Integer.parseInt(item.getValue()); + case "COST_AMOUNT" -> + orderInfoService.list(new QueryWrapper().eq(OrderInfo::getShopId, shopId).eq(OrderInfo::getUserId, userId) + .notIn(OrderInfo::getStatus, OrderStatusEnums.UNPAID.getCode(), OrderStatusEnums.CANCELLED.getCode())) + .stream().map(OrderInfo::getPayAmount).reduce(BigDecimal.ZERO, BigDecimal::add).compareTo(new BigDecimal(item.getValue())) > 0; + case "RECHARGE_AMOUNT" -> + orderPaymentService.list(new QueryWrapper().eq(OrderPayment::getShopId, shopId) + .eq(OrderPayment::getSourceId, shopUser.getId()).isNotNull(OrderPayment::getTradeNumber)) + .stream().map(OrderPayment::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add).compareTo(new BigDecimal(item.getValue())) > 0; + default -> throw new CzgException("会员开通条件类型错误"); + }; + + } + + // 购买开通 + } else { + } + + if (canOpen) { + MemberLevelConfig levelConfig = levelConfigService.getOne(new QueryWrapper().eq(MemberLevelConfig::getShopId, shopId).orderBy(MemberLevelConfig::getExperienceValue, true).limit(1)); + shopUser.setMemberLevelId(levelConfig.getId()); + shopUser.setIsVip(1); + shopUserService.updateById(shopUser); + } + + return false; + } +} diff --git a/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/TbMemberLevelConfigServiceImpl.java b/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/TbMemberLevelConfigServiceImpl.java new file mode 100644 index 00000000..c3f7033d --- /dev/null +++ b/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/TbMemberLevelConfigServiceImpl.java @@ -0,0 +1,18 @@ +package com.czg.service.account.service.impl; + +import com.mybatisflex.spring.service.impl.ServiceImpl; +import com.czg.account.entity.MemberLevelConfig; +import com.czg.account.service.MemberLevelConfigService; +import com.czg.service.account.mapper.TbMemberLevelConfigMapper; +import org.springframework.stereotype.Service; + +/** + * 会员等级配置 服务层实现。 + * + * @author zs + * @since 2025-09-10 + */ +@Service +public class TbMemberLevelConfigServiceImpl extends ServiceImpl implements MemberLevelConfigService{ + +} diff --git a/cash-service/account-service/src/main/resources/mapper/TbMemberConfigMapper.xml b/cash-service/account-service/src/main/resources/mapper/TbMemberConfigMapper.xml new file mode 100644 index 00000000..c6dd3527 --- /dev/null +++ b/cash-service/account-service/src/main/resources/mapper/TbMemberConfigMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/cash-service/account-service/src/main/resources/mapper/TbMemberLevelConfigMapper.xml b/cash-service/account-service/src/main/resources/mapper/TbMemberLevelConfigMapper.xml new file mode 100644 index 00000000..e26df246 --- /dev/null +++ b/cash-service/account-service/src/main/resources/mapper/TbMemberLevelConfigMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/cash-service/order-service/src/main/java/com/czg/service/order/dto/VipMemberPayParamDTO.java b/cash-service/order-service/src/main/java/com/czg/service/order/dto/VipMemberPayParamDTO.java new file mode 100644 index 00000000..ddf6fcbd --- /dev/null +++ b/cash-service/order-service/src/main/java/com/czg/service/order/dto/VipMemberPayParamDTO.java @@ -0,0 +1,35 @@ +package com.czg.service.order.dto; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 支付接收参数 实体类 + * + * @author ww + * @description + */ +@Data +public class VipMemberPayParamDTO { + @NotNull(message = "店铺不能为空") + private Long shopId; + @NotNull(message = "用户ID不能为空") + private Long shopUserId; + @NotNull(message = "会员订单id不能为空") + private Long memberOrderId; + /** + * 平台类型 pc 收银机客户端 wechat 微信小程序 alipay 支付宝小程序 admin-pc PC管理端 admin-app APP管理端 + */ + private String platformType; + private String payType; + private String openId; + /** + * 扫码支付 扫描码 + */ + private String authCode; + private String pwd; + private String returnUrl; + private String buyerRemark; +} diff --git a/cash-service/order-service/src/main/java/com/czg/service/order/mapper/MemberOrderMapper.java b/cash-service/order-service/src/main/java/com/czg/service/order/mapper/MemberOrderMapper.java new file mode 100644 index 00000000..35b64ef8 --- /dev/null +++ b/cash-service/order-service/src/main/java/com/czg/service/order/mapper/MemberOrderMapper.java @@ -0,0 +1,14 @@ +package com.czg.service.order.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.czg.order.entity.MemberOrder; + +/** + * 会员充值订单 映射层。 + * + * @author zs + * @since 2025-09-11 + */ +public interface MemberOrderMapper extends BaseMapper { + +} diff --git a/cash-service/order-service/src/main/java/com/czg/service/order/service/PayService.java b/cash-service/order-service/src/main/java/com/czg/service/order/service/PayService.java index be11a211..59ed502b 100644 --- a/cash-service/order-service/src/main/java/com/czg/service/order/service/PayService.java +++ b/cash-service/order-service/src/main/java/com/czg/service/order/service/PayService.java @@ -5,6 +5,7 @@ import com.czg.entity.resp.CzgRefundResp; import com.czg.order.dto.OrderInfoRefundDTO; import com.czg.resp.CzgResult; import com.czg.service.order.dto.OrderPayParamDTO; +import com.czg.service.order.dto.VipMemberPayParamDTO; import com.czg.service.order.dto.VipPayParamDTO; import com.czg.service.order.dto.VipRefundDTO; import lombok.NonNull; @@ -119,4 +120,6 @@ public interface PayService { * @param refundOrderId 平台退款订单号 二选一 */ CzgResult queryRefund(Long shopId, String mchRefundNo, String refundOrderId); + + CzgResult> ltPayMember(String clientIP, VipMemberPayParamDTO payParam); } diff --git a/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/MemberOrderServiceImpl.java b/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/MemberOrderServiceImpl.java new file mode 100644 index 00000000..f622ac3c --- /dev/null +++ b/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/MemberOrderServiceImpl.java @@ -0,0 +1,84 @@ +package com.czg.service.order.service.impl; + +import cn.hutool.core.util.IdUtil; +import com.alibaba.fastjson2.JSONObject; +import com.czg.account.dto.MemberConfigDTO; +import com.czg.account.entity.ShopInfo; +import com.czg.account.entity.UserInfo; +import com.czg.account.service.ShopInfoService; +import com.czg.account.service.ShopUserService; +import com.czg.account.service.TbMemberConfigService; +import com.czg.account.service.UserInfoService; +import com.czg.account.vo.MemberConfigVO; +import com.czg.exception.CzgException; +import com.czg.order.dto.MemberOrderDTO; +import com.czg.service.order.enums.OrderStatusEnums; +import com.czg.utils.AssertUtil; +import com.mybatisflex.spring.service.impl.ServiceImpl; +import com.czg.order.entity.MemberOrder; +import com.czg.order.service.MemberOrderService; +import com.czg.service.order.mapper.MemberOrderMapper; +import org.apache.dubbo.config.annotation.DubboReference; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; + +/** + * 会员充值订单 服务层实现。 + * + * @author zs + * @since 2025-09-11 + */ +@Service +public class MemberOrderServiceImpl extends ServiceImpl implements MemberOrderService { + @DubboReference + private ShopInfoService shopInfoService; + @DubboReference + private UserInfoService userInfoService; + @DubboReference + private ShopUserService shopUserService; + @DubboReference + private TbMemberConfigService memberConfigService; + + @Override + public MemberOrder createMemberOrder(MemberOrderDTO orderDTO) { + ShopInfo shopInfo = shopInfoService.getById(orderDTO.getShopId()); + AssertUtil.isNull(shopInfo, "生成订单失败,店铺信息不存在"); + + if (orderDTO.getUserId() != null) { + UserInfo userInfo = userInfoService.getById(orderDTO.getUserId()); + AssertUtil.isNull(userInfo, "生成订单失败,用户信息不存在"); + } + + MemberConfigVO memberConfigVO = memberConfigService.detail(shopInfo.getId()); + if(memberConfigVO.getConfigList() == null || memberConfigVO.getConfigList().isEmpty()) { + throw new CzgException("会员开通方案未配置,请联系店铺"); + } + + MemberConfigDTO.ConfigList configItem = memberConfigVO.getConfigList().stream() + .filter(item -> item.getName().equals(orderDTO.getName())) + .findFirst() + .orElseThrow(() -> new CzgException("会员开通方案未配置,请联系店铺")); + + + //生成订单 + MemberOrder orderInfo = new MemberOrder(); + + orderInfo.setOrderNo(orderDTO.getPlatformType() + IdUtil.getSnowflakeNextId()); + orderInfo.setShopId(orderDTO.getShopId()); + orderInfo.setPayAmount(BigDecimal.ZERO); + orderInfo.setStatus(OrderStatusEnums.UNPAID.getCode()); + orderInfo.setAmount(configItem.getPrice().multiply(BigDecimal.valueOf(orderDTO.getNum()))); + orderInfo.setPrice(configItem.getPrice()); + orderInfo.setName(configItem.getName()); + orderInfo.setUserId(orderDTO.getUserId()); + orderInfo.setReward(configItem.getReward()); + orderInfo.setCircleTime(configItem.getCircleTime()); + if (configItem.getCouponList() != null && !configItem.getCouponList().isEmpty()) { + orderInfo.setCouponList(JSONObject.toJSONString(configItem.getCouponList())); + } + orderInfo.setNum(orderDTO.getNum()); + save(orderInfo); + return orderInfo; + } +} diff --git a/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/OrderPaymentServiceImpl.java b/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/OrderPaymentServiceImpl.java index 2bb8e00b..5faa2dee 100644 --- a/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/OrderPaymentServiceImpl.java +++ b/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/OrderPaymentServiceImpl.java @@ -4,6 +4,7 @@ import com.mybatisflex.spring.service.impl.ServiceImpl; import com.czg.order.entity.OrderPayment; import com.czg.order.service.OrderPaymentService; import com.czg.service.order.mapper.OrderPaymentMapper; +import org.apache.dubbo.config.annotation.DubboService; import org.springframework.stereotype.Service; /** @@ -12,7 +13,7 @@ import org.springframework.stereotype.Service; * @author ww * @since 2025-02-15 */ -@Service +@DubboService public class OrderPaymentServiceImpl extends ServiceImpl implements OrderPaymentService{ } diff --git a/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/PayServiceImpl.java b/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/PayServiceImpl.java index 6b8a78e5..13a78808 100644 --- a/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/PayServiceImpl.java +++ b/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/PayServiceImpl.java @@ -20,19 +20,18 @@ import com.czg.exception.PaySuccessException; import com.czg.order.dto.BigDecimalDTO; import com.czg.order.dto.CheckOrderPay; import com.czg.order.dto.OrderInfoRefundDTO; +import com.czg.order.entity.MemberOrder; 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.order.service.OrderInfoService; -import com.czg.order.service.OrderPaymentService; +import com.czg.order.service.*; import com.czg.resp.CzgRespCode; import com.czg.resp.CzgResult; import com.czg.service.CzgPayService; import com.czg.service.RedisService; import com.czg.service.order.dto.OrderPayParamDTO; +import com.czg.service.order.dto.VipMemberPayParamDTO; import com.czg.service.order.dto.VipPayParamDTO; import com.czg.service.order.dto.VipRefundDTO; import com.czg.service.order.enums.OrderStatusEnums; @@ -101,6 +100,8 @@ public class PayServiceImpl implements PayService { private RedisService redisService; @Resource private RabbitPublisher rabbitPublisher; + @Resource + private MemberOrderService memberOrderService; private final BigDecimal MONEY_RATE = new BigDecimal("100"); @@ -390,6 +391,21 @@ public class PayServiceImpl implements PayService { "会员充值", payParam.getOpenId(), clintIp, payParam.getReturnUrl(), payParam.getBuyerRemark(), "")); } + @Override + public CzgResult> ltPayMember(String clientIP, VipMemberPayParamDTO payParam) { + MemberOrder memberOrder = memberOrderService.getOne(new QueryWrapper().eq(MemberOrder::getId, payParam.getMemberOrderId())); + AssertUtil.isNull(memberOrder, "充值会员失败 该会员订单不存在"); + ShopUser shopUser = shopUserService.getById(payParam.getShopUserId()); + AssertUtil.isNull(shopUser, "充值失败 该店铺用户不存在"); + AssertUtil.isBlank(payParam.getOpenId(), "用户小程序ID不能为空"); + AssertUtil.isBlank(payParam.getPayType(), "支付方式不能为空"); + String payOrderNo = payParam.getPlatformType() + IdUtil.getSnowflakeNextId(); + initOrderPayment(new OrderPayment(payParam.getShopId(), shopUser.getId(), "memberPay", payOrderNo, + "", memberOrder.getAmount(), payParam.getMemberOrderId())); + return ltPay(payParam.getShopId(), payParam.getPayType(), new CzgLtPayReq(payOrderNo, memberOrder.getAmount().multiply(MONEY_RATE).longValue(), + payParam.getPayType(), "会员充值", payParam.getOpenId(), clientIP, payParam.getReturnUrl(), payParam.getBuyerRemark(), "")); + } + @Override @Transactional public CzgResult> ltPayVip(String clintIp, VipPayParamDTO payParam) { diff --git a/cash-service/order-service/src/main/resources/mapper/MemberOrderMapper.xml b/cash-service/order-service/src/main/resources/mapper/MemberOrderMapper.xml new file mode 100644 index 00000000..124a957a --- /dev/null +++ b/cash-service/order-service/src/main/resources/mapper/MemberOrderMapper.xml @@ -0,0 +1,7 @@ + + + + +