This commit is contained in:
磷叶 2025-01-17 18:16:02 +08:00
parent 9979f8c0b3
commit 9afb61f2ea
32 changed files with 716 additions and 48 deletions

View File

@ -0,0 +1,18 @@
package com.ruoyi.common.utils;
import java.math.BigDecimal;
/**
* @author wjh
* 2025/1/17
*/
public class MathUtils {
/**
* 判断a和b的值是否一致
*/
public static boolean equals(BigDecimal a, BigDecimal b) {
return a != null && b != null && a.compareTo(b) == 0;
}
}

View File

@ -52,6 +52,9 @@ public class ChannelServiceImpl implements ChannelService
@Override
public ChannelVO selectSmChannelByChannelId(Long channelId)
{
if (channelId == null) {
return null;
}
return channelMapper.selectSmChannelByChannelId(channelId);
}

View File

@ -0,0 +1,19 @@
package com.ruoyi.ss.store.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author wjh
* 2025/1/17
*/
@Data
public class StoreNameVO {
@ApiModelProperty("ID")
private Long storeId;
@ApiModelProperty("名称")
private String name;
}

View File

@ -1,9 +1,6 @@
package com.ruoyi.ss.store.mapper;
import com.ruoyi.ss.store.domain.Store;
import com.ruoyi.ss.store.domain.StoreCountVO;
import com.ruoyi.ss.store.domain.StoreQuery;
import com.ruoyi.ss.store.domain.StoreVo;
import com.ruoyi.ss.store.domain.*;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@ -125,4 +122,9 @@ public interface StoreMapper
* 根据条件更新
*/
int updateByQuery(@Param("data") Store data, @Param("query") StoreQuery query);
List<StoreVo> selectSimpleList(@Param("query") StoreQuery query);
List<StoreNameVO> selectNameList(@Param("query") StoreQuery query);
}

View File

@ -149,6 +149,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</if>
</select>
<select id="selectSimpleList" resultMap="SmStoreResult">
select
ss.*
from sm_store ss
<where>
<include refid="searchCondition"/>
</where>
</select>
<select id="selectNameList" resultType="StoreNameVO">
select
ss.store_id,
ss.name
from sm_store ss
<where>
<include refid="searchCondition"/>
</where>
</select>
<insert id="insertSmStore" parameterType="Store" useGeneratedKeys="true" keyProperty="storeId">
<selectKey keyProperty="storeId" resultType="Long" order="AFTER">
SELECT LAST_INSERT_ID();

View File

@ -1,9 +1,6 @@
package com.ruoyi.ss.store.service;
import com.ruoyi.ss.store.domain.Store;
import com.ruoyi.ss.store.domain.StoreBO;
import com.ruoyi.ss.store.domain.StoreQuery;
import com.ruoyi.ss.store.domain.StoreVo;
import com.ruoyi.ss.store.domain.*;
import java.util.List;
import java.util.Map;
@ -155,4 +152,11 @@ public interface StoreService
* 查询一个员工管理的店铺
*/
StoreVo selectStaffStoreOne(StoreQuery query);
/**
* 查询简单店铺信息
*/
List<StoreVo> selectSimpleByIds(List<Long> storeIds);
List<StoreNameVO> selectNameList(StoreQuery query);
}

View File

@ -401,6 +401,21 @@ public class StoreServiceImpl implements StoreService
return CollectionUtils.getFirst(list);
}
@Override
public List<StoreVo> selectSimpleByIds(List<Long> storeIds) {
if (CollectionUtils.isEmptyElement(storeIds)) {
return Collections.emptyList();
}
StoreQuery query = new StoreQuery();
query.setStoreIds(storeIds);
return storeMapper.selectSimpleList(query);
}
@Override
public List<StoreNameVO> selectNameList(StoreQuery query) {
return storeMapper.selectNameList(query);
}
/**
* 通用查询数量
*

View File

@ -41,9 +41,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="query.storeIds != null and query.storeIds != ''"> and sv.store_ids = #{query.storeIds}</if>
<if test="query.userName != null and query.userName != ''">and if(su.is_real, su.real_name, su.user_name) like concat('%',#{query.userName},'%')</if>
<if test="query.vipLevelName != null and query.vipLevelName != ''">and svl.name like concat('%',#{query.vipLevelName},'%')</if>
<if test="query.nextResetTime != null "> and next_reset_time = #{query.nextResetTime}</if>
<if test="query.totalCount != null "> and total_count = #{query.totalCount}</if>
<if test="query.roundCount != null "> and round_count = #{query.roundCount}</if>
<if test="query.limitType != null and query.limitType != ''"> and limit_type = #{query.limitType}</if>
<if test="query.limitCount != null "> and limit_count = #{query.limitCount}</if>
<if test="query.excludeId != null">and sv.id != #{query.excludeId}</if>
@ -85,6 +82,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="endTime != null">end_time,</if>
<if test="discount != null">discount,</if>
<if test="storeIds != null and storeIds.size() > 0">store_ids,</if>
<if test="nextResetTime != null">next_reset_time,</if>
<if test="totalCount != null">total_count,</if>
<if test="roundCount != null">round_count,</if>
<if test="limitType != null and limitType != ''">limit_type,</if>
<if test="limitCount != null">limit_count,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null">#{userId},</if>
@ -94,6 +96,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="endTime != null">#{endTime},</if>
<if test="discount != null">#{discount},</if>
<if test="storeIds != null and storeIds.size() > 0">#{storeIds, typeHandler=com.ruoyi.system.mapper.typehandler.LongSplitListTypeHandler},</if>
<if test="nextResetTime != null">#{nextResetTime},</if>
<if test="totalCount != null">#{totalCount},</if>
<if test="roundCount != null">#{roundCount},</if>
<if test="limitType != null and limitType != ''">#{limitType},</if>
<if test="limitCount != null">#{limitCount},</if>
</trim>
</insert>
@ -113,6 +120,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="data.endTime != null">end_time = #{data.endTime},</if>
<if test="data.discount != null">discount = #{data.discount},</if>
<if test="data.storeIds != null and data.storeIds.size() > 0">store_ids = #{data.storeIds, typeHandler=com.ruoyi.system.mapper.typehandler.LongSplitListTypeHandler},</if>
<if test="data.nextResetTime != null">next_reset_time = #{data.nextResetTime},</if>
<if test="data.totalCount != null">total_count = #{data.totalCount},</if>
<if test="data.roundCount != null">round_count = #{data.roundCount},</if>
<if test="data.limitType != null and data.limitType != ''">limit_type = #{data.limitType},</if>
<if test="data.limitCount != null">limit_count = #{data.limitCount},</if>
</sql>
<delete id="deleteVipById" parameterType="Long">

View File

@ -14,4 +14,7 @@ public class VipLevelQuery extends VipLevelVO {
@ApiModelProperty("ID列表")
private List<Long> ids;
@ApiModelProperty("店铺ID")
private Long storeId;
}

View File

@ -1,6 +1,6 @@
package com.ruoyi.ss.vipLevel.domain;
import com.ruoyi.ss.store.domain.StoreVo;
import com.ruoyi.ss.store.domain.StoreNameVO;
import com.ruoyi.ss.vipLevelSku.domain.VipLevelSkuVO;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ -20,7 +20,7 @@ public class VipLevelVO extends VipLevel{
private String mchName;
@ApiModelProperty("店铺列表")
private List<StoreVo> storeList;
private List<StoreNameVO> storeList;
@ApiModelProperty("SKU列表")
@Size(min = 1, message = "SKU列表不能为空")

View File

@ -0,0 +1,18 @@
package com.ruoyi.ss.vipLevel.domain.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author wjh
* 2025/1/17
*/
@Getter
@AllArgsConstructor
public enum VipLevelSolution {
SAME("1", "相同续期");
private final String code;
private final String msg;
}

View File

@ -30,6 +30,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="query.mchName != null and query.mchName != ''">
and if (mch.is_real, mch.real_name, mch.user_name) like concat('%', #{query.mchName}, '%')
</if>
<if test="query.storeId != null">
and find_in_set(#{query.storeId}, svl.store_ids)
</if>
<if test="query.ids != null and query.ids.size() > 0">
and id in
<foreach collection="query.ids" item="item" open="(" separator="," close=")">

View File

@ -9,4 +9,9 @@ public interface VipLevelAssembler {
void assembleStoreList(List<VipLevelVO> list);
void assembleSkuList(List<VipLevelVO> list);
/**
* 拼接在售的会员等级列表
*/
void assembleSaleSkuList(List<VipLevelVO> list);
}

View File

@ -1,19 +1,19 @@
package com.ruoyi.ss.vipLevel.service.impl;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.ss.store.domain.StoreVo;
import com.ruoyi.ss.store.domain.StoreNameVO;
import com.ruoyi.ss.store.domain.StoreQuery;
import com.ruoyi.ss.store.service.StoreService;
import com.ruoyi.ss.vipLevel.domain.VipLevelVO;
import com.ruoyi.ss.vipLevel.service.VipLevelAssembler;
import com.ruoyi.ss.vipLevelSku.domain.VipLevelSkuQuery;
import com.ruoyi.ss.vipLevelSku.domain.VipLevelSkuVO;
import com.ruoyi.ss.vipLevelSku.domain.enums.VipLevelSkuStatus;
import com.ruoyi.ss.vipLevelSku.service.VipLevelSkuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
@Service
@ -32,10 +32,15 @@ public class VipLevelAssemblerImpl implements VipLevelAssembler {
}
List<Long> storeIds = list.stream().map(VipLevelVO::getStoreIds).flatMap(Collection::stream).collect(Collectors.toList());
List<StoreVo> storeList = storeService.selectStoreByIds(storeIds);
List<StoreNameVO> storeList = new ArrayList<>();
if (CollectionUtils.isNotEmptyElement(storeIds)) {
StoreQuery query = new StoreQuery();
query.setStoreIds(storeIds);
storeList = storeService.selectNameList(query);
}
for (VipLevelVO vipLevel : list) {
List<StoreVo> vipLevelStoreList = storeList.stream()
List<StoreNameVO> vipLevelStoreList = storeList.stream()
.filter(store -> vipLevel.getStoreIds().contains(store.getStoreId()))
.collect(Collectors.toList());
vipLevel.setStoreList(vipLevelStoreList);
@ -53,7 +58,6 @@ public class VipLevelAssemblerImpl implements VipLevelAssembler {
Map<Long, List<VipLevelSkuVO>> group = vipLevelSkuService.selectByLevelIds(levelIds)
.stream().collect(Collectors.groupingBy(VipLevelSkuVO::getLevelId));
for (VipLevelVO vipLevel : list) {
List<VipLevelSkuVO> vipLevelSkuList = group.get(vipLevel.getId());
if (vipLevelSkuList == null) {
@ -62,4 +66,28 @@ public class VipLevelAssemblerImpl implements VipLevelAssembler {
vipLevel.setSkuList(vipLevelSkuList);
}
}
@Override
public void assembleSaleSkuList(List<VipLevelVO> list) {
if (CollectionUtils.isEmptyElement(list)) {
return;
}
List<Long> levelIds = list.stream().map(VipLevelVO::getId).collect(Collectors.toList());
VipLevelSkuQuery query = new VipLevelSkuQuery();
query.setLevelIds(levelIds);
query.setStatus(VipLevelSkuStatus.ON_SALE.getStatus());
Map<Long, List<VipLevelSkuVO>> group = vipLevelSkuService.selectVipLevelSkuList(query)
.stream().collect(Collectors.groupingBy(VipLevelSkuVO::getLevelId));
for (VipLevelVO vipLevel : list) {
List<VipLevelSkuVO> vipLevelSkuList = group.get(vipLevel.getId());
if (vipLevelSkuList == null) {
vipLevelSkuList = Collections.emptyList();
}
vipLevel.setSkuList(vipLevelSkuList);
}
}
}

View File

@ -56,4 +56,13 @@ public class VipLevelSku extends BaseEntity
@DictValid(type = DictTypeConstants.VIP_LEVEL_SKU_STATUS, message = "非法的状态")
private String status;
@Excel(name = "定价名称")
@ApiModelProperty("定价名称")
@NotBlank(message = "定价名称不允许为空", groups = {ValidGroup.Create.class})
private String name;
@Excel(name = "原价")
@ApiModelProperty("原价")
private BigDecimal originalPrice;
}

View File

@ -8,4 +8,5 @@ import lombok.Data;
*/
@Data
public class VipLevelSkuVO extends VipLevelSku {
}

View File

@ -0,0 +1,19 @@
package com.ruoyi.ss.vipLevelSku.domain.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author wjh
* 2025/1/17
*/
@Getter
@AllArgsConstructor
public enum VipLevelSkuStatus {
ON_SALE("1", "上架"),
OFF_SALE("0", "下架");
private final String status;
private final String msg;
}

View File

@ -17,7 +17,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
svls.description,
svls.limit_type,
svls.limit_count,
svls.status
svls.status,
svls.name,
svls.original_price
from ss_vip_level_sku svls
</sql>
@ -28,6 +30,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="query.limitType != null and query.limitType != ''"> and svls.limit_type = #{query.limitType}</if>
<if test="query.limitCount != null "> and svls.limit_count = #{query.limitCount}</if>
<if test="query.status != null and query.status != '' "> and svls.status = #{query.status}</if>
<if test="query.name != null and query.name != ''"> and svls.name like concat('%', #{query.name}, '%')</if>
<if test="query.levelIds != null and query.levelIds.size() > 0">
and svls.level_id in
<foreach collection="query.levelIds" item="item" separator="," open="(" close=")">
@ -62,6 +65,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="limitType != null and limitType != ''">limit_type,</if>
<if test="limitCount != null">limit_count,</if>
<if test="status != null and status != ''">`status`,</if>
<if test="name != null and name != ''">`name`,</if>
<if test="originalPrice != null">original_price,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if>
@ -74,6 +79,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="limitType != null and limitType != ''">#{limitType},</if>
<if test="limitCount != null">#{limitCount},</if>
<if test="status != null and status != ''">#{status},</if>
<if test="name != null and name != ''">#{name},</if>
<if test="originalPrice != null">#{originalPrice},</if>
</trim>
</insert>
@ -89,6 +96,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
limit_type,
limit_count,
`status`,
`name`,
original_price,
</trim>
values
<foreach collection="list" item="i" separator=",">
@ -111,6 +120,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="i.limitCount == null ">default,</if>
<if test="i.status != null and i.status != ''">#{i.status},</if>
<if test="i.status == null or i.status == ''">default,</if>
<if test="i.name != null and i.name != ''">#{i.name},</if>
<if test="i.name == null or i.name == ''">default,</if>
<if test="i.originalPrice != null ">#{i.originalPrice},</if>
<if test="i.originalPrice == null ">default,</if>
</trim>
</foreach>
</insert>
@ -208,6 +221,26 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</otherwise>
</choose>
</foreach>
<foreach open="name = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.name != null and item.name != ''">
WHEN #{item.id} THEN #{item.name}
</when>
<otherwise>
WHEN #{item.id} THEN `name`
</otherwise>
</choose>
</foreach>
<foreach open="original_price = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.originalPrice != null ">
WHEN #{item.id} THEN #{item.originalPrice}
</when>
<otherwise>
WHEN #{item.id} THEN `original_price`
</otherwise>
</choose>
</foreach>
</trim>
where id in
<foreach item="item" collection="list" open="(" separator="," close=")">
@ -233,6 +266,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="data.limitType != null and data.limitType != ''">limit_type = #{data.limitType},</if>
<if test="data.limitCount != null">limit_count = #{data.limitCount},</if>
<if test="data.status != null and data.status != ''">`status` = #{data.status},</if>
<if test="data.name != null and data.name != ''">`name` = #{data.name},</if>
<if test="data.originalPrice != null">original_price = #{data.originalPrice},</if>
</sql>
<delete id="deleteVipLevelSkuById" parameterType="Long">

View File

@ -1,22 +1,19 @@
package com.ruoyi.ss.vipOrder.domain;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import org.springframework.format.annotation.DateTimeFormat;
/**
* 会员订单对象 ss_vip_order
*
* @author ruoyi
* @date 2025-01-16
* @date 2025-01-17
*/
@Data
public class VipOrder extends BaseEntity
@ -53,16 +50,28 @@ public class VipOrder extends BaseEntity
@ApiModelProperty("下单时的可用店铺列表")
private String levelStoreIds;
@Excel(name = "VIP定价ID")
@ApiModelProperty("VIP定价ID")
@Excel(name = "sku ID")
@ApiModelProperty("sku ID")
private Long skuId;
@Excel(name = "下单时的购买时长(天)")
@ApiModelProperty("下单时的购买时长(天)")
private Long skuTime;
@Excel(name = "sku购买时长")
@ApiModelProperty("sku购买时长")
private Integer skuTime;
@Excel(name = "订单金额")
@ApiModelProperty("订单金额")
@Excel(name = "sku价格")
@ApiModelProperty("sku价格")
private BigDecimal skuPrice;
@Excel(name = "sku限制类型")
@ApiModelProperty("sku限制类型")
private String skuLimitType;
@Excel(name = "sku限制次数")
@ApiModelProperty("sku限制次数")
private Integer skuLimitCount;
@Excel(name = "实付金额")
@ApiModelProperty("实付金额")
private BigDecimal amount;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")

View File

@ -0,0 +1,35 @@
package com.ruoyi.ss.vipOrder.domain.bo;
import com.ruoyi.ss.channel.domain.ChannelVO;
import com.ruoyi.ss.user.domain.SmUserVO;
import com.ruoyi.ss.vipLevel.domain.VipLevelVO;
import com.ruoyi.ss.vipLevelSku.domain.VipLevelSkuVO;
import com.ruoyi.ss.vipOrder.domain.dto.AddPayVipOrderDTO;
import lombok.Data;
/**
* @author wjh
* 2025/1/17
*/
@Data
public class AddPayVipOrderBO {
// DTO
private AddPayVipOrderDTO dto;
// 支付渠道
private ChannelVO channel;
// sku
private VipLevelSkuVO sku;
// 等级
private VipLevelVO level;
// 下单用户
private SmUserVO user;
// 商户
private SmUserVO mch;
}

View File

@ -0,0 +1,58 @@
package com.ruoyi.ss.vipOrder.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.List;
/**
* 会员下单DTO
* @author wjh
* 2025/1/17
*/
@Data
public class AddPayVipOrderDTO {
@ApiModelProperty("实付金额")
@NotNull(message = "实付金额不能为空")
@Min(value = 0, message = "实付金额不能小于0")
private BigDecimal amount;
@ApiModelProperty("支付渠道ID")
@NotNull(message = "支付渠道不能为空")
private Long channelId;
@ApiModelProperty("会员等级skuId")
@NotNull(message = "会员等级定价不能为空")
private Long skuId;
@ApiModelProperty("sku购买时长")
@NotNull(message = "sku购买时长不能为空")
private Integer skuTime;
@ApiModelProperty("sku价格")
@NotNull(message = "sku价格不能为空")
private BigDecimal skuPrice;
@ApiModelProperty("sku限制类型")
@NotNull(message = "sku限制类型不能为空")
private String skuLimitType;
@ApiModelProperty("sku限制次数")
@NotNull(message = "sku限制次数不能为空")
private Integer skuLimitCount;
@ApiModelProperty("VIP等级折扣")
@NotNull(message = "VIP等级折扣不能为空")
private BigDecimal levelDiscount;
@ApiModelProperty("VIP可用店铺列表")
@NotNull(message = "VIP可用店铺列表不能为空")
private List<Long> levelStoreIds;
@ApiModelProperty("出现不兼容时的解决方案")
private String solution;
}

View File

@ -0,0 +1,11 @@
package com.ruoyi.ss.vipOrder.domain.vo;
import lombok.Data;
/**
* @author wjh
* 2025/1/17
*/
@Data
public class AddAndPayVO {
}

View File

@ -18,6 +18,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
svo.level_store_ids,
svo.sku_id,
svo.sku_time,
svo.sku_price,
svo.sku_limit_type,
svo.sku_limit_count,
svo.amount,
svo.expire_time,
svo.pay_id,
@ -70,10 +73,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="levelStoreIds != null and levelStoreIds != ''">level_store_ids,</if>
<if test="skuId != null">sku_id,</if>
<if test="skuTime != null">sku_time,</if>
<if test="skuPrice != null">sku_price,</if>
<if test="skuLimitType != null and skuLimitType != ''">sku_limit_type,</if>
<if test="skuLimitCount != null">sku_limit_count,</if>
<if test="amount != null">amount,</if>
<if test="expireTime != null">expire_time,</if>
<if test="payId != null">pay_id,</if>
<if test="status != null and status != ''">`status`,</if>
<if test="status != null and status != ''">status,</if>
<if test="cancelTime != null">cancel_time,</if>
<if test="cancelReason != null">cancel_reason,</if>
<if test="createTime != null">create_time,</if>
@ -88,6 +94,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="levelStoreIds != null and levelStoreIds != ''">#{levelStoreIds},</if>
<if test="skuId != null">#{skuId},</if>
<if test="skuTime != null">#{skuTime},</if>
<if test="skuPrice != null">#{skuPrice},</if>
<if test="skuLimitType != null and skuLimitType != ''">#{skuLimitType},</if>
<if test="skuLimitCount != null">#{skuLimitCount},</if>
<if test="amount != null">#{amount},</if>
<if test="expireTime != null">#{expireTime},</if>
<if test="payId != null">#{payId},</if>
@ -116,6 +125,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="data.levelStoreIds != null and data.levelStoreIds != ''">level_store_ids = #{data.levelStoreIds},</if>
<if test="data.skuId != null">sku_id = #{data.skuId},</if>
<if test="data.skuTime != null">sku_time = #{data.skuTime},</if>
<if test="data.skuPrice != null">sku_price = #{data.skuPrice},</if>
<if test="data.skuLimitType != null and data.skuLimitType != ''">sku_limit_type = #{data.skuLimitType},</if>
<if test="data.skuLimitCount != null">sku_limit_count = #{data.skuLimitCount},</if>
<if test="data.amount != null">amount = #{data.amount},</if>
<if test="data.expireTime != null">expire_time = #{data.expireTime},</if>
<if test="data.payId != null">pay_id = #{data.payId},</if>

View File

@ -0,0 +1,17 @@
package com.ruoyi.ss.vipOrder.service;
import com.ruoyi.ss.vipOrder.domain.bo.AddPayVipOrderBO;
import com.ruoyi.ss.vipOrder.domain.dto.AddPayVipOrderDTO;
/**
* @author wjh
* 2025/1/17
*/
public interface VipOrderConverter {
/**
* 下单 DTO 转为 BO
*/
AddPayVipOrderBO toBO(AddPayVipOrderDTO dto);
}

View File

@ -1,9 +1,12 @@
package com.ruoyi.ss.vipOrder.service;
import java.util.List;
import com.ruoyi.ss.vipOrder.domain.VipOrder;
import com.ruoyi.ss.vipOrder.domain.VipOrderVO;
import com.ruoyi.ss.vipOrder.domain.VipOrderQuery;
import com.ruoyi.ss.vipOrder.domain.VipOrderVO;
import com.ruoyi.ss.vipOrder.domain.bo.AddPayVipOrderBO;
import com.ruoyi.ss.vipOrder.domain.vo.AddAndPayVO;
import java.util.List;
/**
* 会员订单Service接口
@ -60,4 +63,9 @@ public interface VipOrderService
* @return 结果
*/
public int deleteVipOrderById(Long id);
/**
* 下单并支付
*/
AddAndPayVO addAndPay(AddPayVipOrderBO bo);
}

View File

@ -0,0 +1,15 @@
package com.ruoyi.ss.vipOrder.service;
import com.ruoyi.ss.vipOrder.domain.bo.AddPayVipOrderBO;
/**
* @author wjh
* 2025/1/17
*/
public interface VipOrderValidator {
/**
* 下单并支付校验
*/
void preAddAndPay(AddPayVipOrderBO bo);
}

View File

@ -0,0 +1,65 @@
package com.ruoyi.ss.vipOrder.service.impl;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.ss.channel.service.ChannelService;
import com.ruoyi.ss.user.service.UserService;
import com.ruoyi.ss.vipLevel.domain.VipLevelVO;
import com.ruoyi.ss.vipLevel.service.VipLevelService;
import com.ruoyi.ss.vipLevelSku.domain.VipLevelSkuVO;
import com.ruoyi.ss.vipLevelSku.service.VipLevelSkuService;
import com.ruoyi.ss.vipOrder.domain.bo.AddPayVipOrderBO;
import com.ruoyi.ss.vipOrder.domain.dto.AddPayVipOrderDTO;
import com.ruoyi.ss.vipOrder.service.VipOrderConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author wjh
* 2025/1/17
*/
@Service
public class VipOrderConverterImpl implements VipOrderConverter {
@Autowired
private ChannelService channelService;
@Autowired
private VipLevelSkuService vipLevelSkuService;
@Autowired
private VipLevelService vipLevelService;
@Autowired
private UserService userService;
@Override
public AddPayVipOrderBO toBO(AddPayVipOrderDTO dto) {
if (dto == null) {
return null;
}
AddPayVipOrderBO bo = new AddPayVipOrderBO();
bo.setDto(dto);
// 支付渠道
bo.setChannel(channelService.selectSmChannelByChannelId(dto.getChannelId()));
// sku
VipLevelSkuVO sku = vipLevelSkuService.selectVipLevelSkuById(dto.getSkuId());
bo.setSku(sku);
if (sku != null && sku.getLevelId() != null) {
// 会员等级
VipLevelVO level = vipLevelService.selectVipLevelById(sku.getLevelId());
bo.setLevel(level);
if (level != null && level.getMchId() != null) {
// 商户
bo.setMch(userService.selectSmUserByUserId(level.getMchId()));
}
}
// 下单用户
bo.setUser(userService.selectSmUserByUserId(SecurityUtils.getUserId()));
return bo;
}
}

View File

@ -1,14 +1,18 @@
package com.ruoyi.ss.vipOrder.service.impl;
import java.util.List;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.ss.vipOrder.domain.VipOrder;
import com.ruoyi.ss.vipOrder.domain.VipOrderQuery;
import com.ruoyi.ss.vipOrder.domain.VipOrderVO;
import com.ruoyi.ss.vipOrder.domain.bo.AddPayVipOrderBO;
import com.ruoyi.ss.vipOrder.domain.vo.AddAndPayVO;
import com.ruoyi.ss.vipOrder.mapper.VipOrderMapper;
import com.ruoyi.ss.vipOrder.service.VipOrderService;
import com.ruoyi.ss.vipOrder.service.VipOrderValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.ss.vipOrder.mapper.VipOrderMapper;
import com.ruoyi.ss.vipOrder.domain.VipOrder;
import com.ruoyi.ss.vipOrder.domain.VipOrderVO;
import com.ruoyi.ss.vipOrder.domain.VipOrderQuery;
import com.ruoyi.ss.vipOrder.service.VipOrderService;
import java.util.List;
/**
* 会员订单Service业务层处理
@ -22,6 +26,9 @@ public class VipOrderServiceImpl implements VipOrderService
@Autowired
private VipOrderMapper vipOrderMapper;
@Autowired
private VipOrderValidator vipOrderValidator;
/**
* 查询会员订单
*
@ -94,4 +101,20 @@ public class VipOrderServiceImpl implements VipOrderService
{
return vipOrderMapper.deleteVipOrderById(id);
}
@Override
public AddAndPayVO addAndPay(AddPayVipOrderBO bo) {
// 校验
vipOrderValidator.preAddAndPay(bo);
AddAndPayVO vo = new AddAndPayVO();
// 创建订单
// 创建支付单
// 调起支付
return vo;
}
}

View File

@ -0,0 +1,113 @@
package com.ruoyi.ss.vipOrder.service.impl;
import com.ruoyi.common.utils.MathUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.ss.channel.domain.ChannelVO;
import com.ruoyi.ss.user.domain.SmUserVO;
import com.ruoyi.ss.vip.domain.VipQuery;
import com.ruoyi.ss.vip.domain.VipVO;
import com.ruoyi.ss.vip.service.VipService;
import com.ruoyi.ss.vipLevel.domain.VipLevelVO;
import com.ruoyi.ss.vipLevelSku.domain.VipLevelSkuVO;
import com.ruoyi.ss.vipLevelSku.domain.enums.VipLevelSkuStatus;
import com.ruoyi.ss.vipOrder.domain.bo.AddPayVipOrderBO;
import com.ruoyi.ss.vipOrder.domain.dto.AddPayVipOrderDTO;
import com.ruoyi.ss.vipOrder.service.VipOrderValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
/**
* @author wjh
* 2025/1/17
*/
@Service
public class VipOrderValidatorImpl implements VipOrderValidator {
@Autowired
private VipService vipService;
@Override
public void preAddAndPay(AddPayVipOrderBO bo) {
ServiceUtil.assertion(bo == null, "参数错误");
AddPayVipOrderDTO dto = bo.getDto();
VipLevelVO level = bo.getLevel();
VipLevelSkuVO sku = bo.getSku();
SmUserVO mch = bo.getMch();
ChannelVO channel = bo.getChannel();
SmUserVO user = bo.getUser();
// 校验用户前端查看到的数据和后台数据是否一致
ServiceUtil.assertion(sku == null, "ID为%sVIP等级价格不存在", dto.getSkuId());
ServiceUtil.assertion(level == null, "的VIP等级不存在");
ServiceUtil.assertion(!VipLevelSkuStatus.ON_SALE.getStatus().equals(sku.getStatus()), "ID为%s的SKU目前不可购买", sku.getId());
ServiceUtil.assertion(!Objects.equals(dto.getSkuLimitType(), sku.getLimitType()), "当前使用限制已发生变化,请重新下单");
ServiceUtil.assertion(!Objects.equals(dto.getSkuLimitCount(), sku.getLimitCount()), "当前限制次数已发生变化,请重新下单");
ServiceUtil.assertion(!Objects.equals(dto.getSkuPrice(), sku.getPrice()), "当前价格已发生变化,请重新下单");
ServiceUtil.assertion(!Objects.equals(dto.getSkuTime(), sku.getTime()), "当前可购买的时长已发生变化,请重新下单");
ServiceUtil.assertion(!MathUtils.equals(dto.getLevelDiscount(), level.getDiscount()), "当前折扣已发生变化,请重新下单");
ServiceUtil.assertion(!CollectionUtils.equals(dto.getLevelStoreIds(), level.getStoreIds()), "当前可用店铺已发生变化,请重新下单");
// 商户是否存在
ServiceUtil.assertion(mch == null, "商户不存在");
// 用户是否存在
ServiceUtil.assertion(user == null, "下单用户不存在");
// 校验渠道是否可用
ServiceUtil.assertion(channel == null, "ID为%s的支付渠道不存在", dto.getChannelId());
ServiceUtil.assertion(channel.getEnabled() == null || !channel.getEnabled(), "ID为%s的支付渠道已禁用", channel.getChannelId());
// 校验用户VIP兼容性若不兼容则校验是否有选择方案
this.isCompatibility(sku, level, user);
}
/**
* 判断用户目前拥有的VIP与当前购买SKU是否兼容
* @param sku sku
* @param level 等级
* @param user 用户
*/
private boolean isCompatibility(VipLevelSkuVO sku, VipLevelVO level, SmUserVO user) {
if (sku == null || level == null || user == null) {
return false;
}
// 查询用户所拥有的当前等级的VIP
VipQuery query = new VipQuery();
query.setUserId(user.getUserId());
query.setLevelId(level.getId());
List<VipVO> vipList = vipService.selectVipList(query);
if (CollectionUtils.isEmpty(vipList)) {
return true;
}
// 逐一校验是否一致
for (VipVO vip : vipList) {
if (vip == null) {
continue;
}
// 折扣
if (!MathUtils.equals(vip.getDiscount(), level.getDiscount())) {
return false;
}
// 可用店铺
if (!CollectionUtils.equals(vip.getStoreIds(), level.getStoreIds())) {
return false;
}
// 限制类型
if (!Objects.equals(sku.getLimitType(), vip.getLimitType())) {
return false;
}
// 限制次数
if (!Objects.equals(sku.getLimitCount(), vip.getLimitCount())) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,41 @@
package com.ruoyi.web.controller.app;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.ss.vipLevel.domain.VipLevelQuery;
import com.ruoyi.ss.vipLevel.domain.VipLevelVO;
import com.ruoyi.ss.vipLevel.service.VipLevelAssembler;
import com.ruoyi.ss.vipLevel.service.VipLevelService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author wjh
* 2025/1/17
*/
@RestController
@RequestMapping("/app/vipLevel")
public class AppVipLevelController extends BaseController {
@Autowired
private VipLevelService vipLevelService;
@Autowired
private VipLevelAssembler vipLevelAssembler;
@ApiOperation("查询在售的会员等级列表")
@GetMapping("/list")
public TableDataInfo list(VipLevelQuery query) {
startPage();
List<VipLevelVO> list = vipLevelService.selectVipLevelList(query);
vipLevelAssembler.assembleStoreList(list);
vipLevelAssembler.assembleSaleSkuList(list);
return getDataTable(list);
}
}

View File

@ -0,0 +1,39 @@
package com.ruoyi.web.controller.app;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.ss.vipLevel.domain.enums.VipLevelSolution;
import com.ruoyi.ss.vipOrder.domain.bo.AddPayVipOrderBO;
import com.ruoyi.ss.vipOrder.domain.dto.AddPayVipOrderDTO;
import com.ruoyi.ss.vipOrder.service.VipOrderConverter;
import com.ruoyi.ss.vipOrder.service.VipOrderService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author wjh
* 2025/1/17
*/
@RestController
@RequestMapping("/app/vipOrder")
public class AppVipOrderController extends BaseController {
@Autowired
private VipOrderService vipOrderService;
@Autowired
private VipOrderConverter vipOrderConverter;
@ApiOperation("下单并发起支付")
@PostMapping
public AjaxResult addAndPay(@RequestBody AddPayVipOrderDTO dto) {
dto.setSolution(VipLevelSolution.SAME.getCode());
AddPayVipOrderBO bo = vipOrderConverter.toBO(dto);
return success(vipOrderService.addAndPay(bo));
}
}

View File

@ -6,6 +6,7 @@ import com.ruoyi.common.core.domain.ValidGroup;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.ss.vipLevel.domain.VipLevelQuery;
import com.ruoyi.ss.vipLevel.domain.VipLevelVO;
import com.ruoyi.ss.vipLevel.service.VipLevelAssembler;
import com.ruoyi.ss.vipLevel.service.VipLevelService;
import com.ruoyi.ss.vipLevel.service.VipLevelValidator;
import io.swagger.annotations.ApiModelProperty;
@ -14,6 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
/**
@ -30,12 +32,17 @@ public class MchVipLevelController extends BaseController {
@Autowired
private VipLevelValidator vipLevelValidator;
@Autowired
private VipLevelAssembler vipLevelAssembler;
@ApiOperation("商户获取VIP等级列表")
@GetMapping("/list")
public TableDataInfo list(VipLevelQuery query) {
startPage();
query.setMchId(getUserId());
List<VipLevelVO> list = vipLevelService.selectVipLevelList(query);
vipLevelAssembler.assembleStoreList(list);
vipLevelAssembler.assembleSkuList(list);
return getDataTable(list);
}
@ -46,6 +53,9 @@ public class MchVipLevelController extends BaseController {
if (!vipLevelValidator.canView(vo, getUserId())) {
return error("您无权查看该信息");
}
List<VipLevelVO> list = Collections.singletonList(vo);
vipLevelAssembler.assembleStoreList(list);
vipLevelAssembler.assembleSkuList(list);
return success(vo);
}