|
@@ -1532,28 +1532,27 @@ public Result savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
|
|
|
|
|
|
- 抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
|
|
|
|
|
|
-- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
|
|
|
+- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供**具体**的算法实现或行为。
|
|
|
|
|
|
- 环境(Context)类:用来操作策略的上下文环境,屏蔽高层模块(客户端)对策略、算法的直接访问,封装可能存在的变化。
|
|
|
|
|
|
**策略接口**
|
|
|
|
|
|
```java
|
|
|
-package com.atguigu.tingshu.user.strategy;
|
|
|
+package com.atguigu.tingshu.user.pattern;
|
|
|
|
|
|
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
|
|
|
|
|
|
/**
|
|
|
- * 处理不同类型商品购买记录的策略接口
|
|
|
+ * 抽象”发货“策略接口
|
|
|
*/
|
|
|
-public interface PaidRecordStrategy {
|
|
|
-
|
|
|
+public interface DeliveryStrategy {
|
|
|
|
|
|
/**
|
|
|
- * 抽象出公用处理不同购买项目类型接口方法
|
|
|
+ * 虚拟物品"发货"抽象方法
|
|
|
* @param userPaidRecordVo
|
|
|
*/
|
|
|
- public void handlerPaidRecord(UserPaidRecordVo userPaidRecordVo);
|
|
|
+ void delivery(UserPaidRecordVo userPaidRecordVo);
|
|
|
|
|
|
}
|
|
|
```
|
|
@@ -1561,17 +1560,18 @@ public interface PaidRecordStrategy {
|
|
|
**VIP购买项处理策略实现类**:其中Bean对象ID要跟前端提交项目类型一致
|
|
|
|
|
|
```java
|
|
|
-package com.atguigu.tingshu.user.strategy.impl;
|
|
|
+package com.atguigu.tingshu.user.pattern.impl;
|
|
|
|
|
|
+import cn.hutool.core.date.DateTime;
|
|
|
import cn.hutool.core.date.DateUtil;
|
|
|
+import com.atguigu.tingshu.common.constant.SystemConstant;
|
|
|
import com.atguigu.tingshu.model.user.UserInfo;
|
|
|
import com.atguigu.tingshu.model.user.UserVipService;
|
|
|
import com.atguigu.tingshu.model.user.VipServiceConfig;
|
|
|
import com.atguigu.tingshu.user.mapper.UserInfoMapper;
|
|
|
import com.atguigu.tingshu.user.mapper.UserVipServiceMapper;
|
|
|
import com.atguigu.tingshu.user.mapper.VipServiceConfigMapper;
|
|
|
-import com.atguigu.tingshu.user.strategy.PaidRecordStrategy;
|
|
|
-import com.atguigu.tingshu.vo.user.UserInfoVo;
|
|
|
+import com.atguigu.tingshu.user.pattern.DeliveryStrategy;
|
|
|
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
@@ -1582,11 +1582,11 @@ import java.util.Date;
|
|
|
|
|
|
/**
|
|
|
* @author: atguigu
|
|
|
- * @create: 2024-08-23 10:32
|
|
|
+ * @create: 2025-06-16 09:36
|
|
|
*/
|
|
|
@Slf4j
|
|
|
-@Component("1003")
|
|
|
-public class VIPPaidRecordStrategy implements PaidRecordStrategy {
|
|
|
+@Component(SystemConstant.ORDER_ITEM_TYPE_VIP)
|
|
|
+public class VIPDelivery implements DeliveryStrategy {
|
|
|
|
|
|
@Autowired
|
|
|
private UserVipServiceMapper userVipServiceMapper;
|
|
@@ -1598,47 +1598,50 @@ public class VIPPaidRecordStrategy implements PaidRecordStrategy {
|
|
|
private UserInfoMapper userInfoMapper;
|
|
|
|
|
|
/**
|
|
|
- * 处理购买商品类型为会员-发货逻辑
|
|
|
+ * 虚拟物品-普通会员-发货行为实现
|
|
|
+ *
|
|
|
* @param userPaidRecordVo
|
|
|
*/
|
|
|
@Override
|
|
|
- public void handlerPaidRecord(UserPaidRecordVo userPaidRecordVo) {
|
|
|
- log.info("[用户服务]处理购买商品类型为会员发货逻辑");
|
|
|
- //3.处理购买项目类型-VIP会员
|
|
|
- //3.1 根据订单编号查询是否处理过VIP购买记录
|
|
|
- LambdaQueryWrapper<UserVipService> userVipServiceLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
|
|
- userVipServiceLambdaQueryWrapper.eq(UserVipService::getOrderNo, userPaidRecordVo.getOrderNo());
|
|
|
- Long count = userVipServiceMapper.selectCount(userVipServiceLambdaQueryWrapper);
|
|
|
+ public void delivery(UserPaidRecordVo userPaidRecordVo) {
|
|
|
+ //3. 处理虚拟物品"会员"发货逻辑
|
|
|
+ //3.1 根据订单编号查询已购会员记录,如果存在,则返回
|
|
|
+ Long count = userVipServiceMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<UserVipService>()
|
|
|
+ .eq(UserVipService::getOrderNo, userPaidRecordVo.getOrderNo())
|
|
|
+ );
|
|
|
if (count > 0) {
|
|
|
return;
|
|
|
}
|
|
|
- //3.2 保存本次会员购买记录
|
|
|
- UserVipService userVipService = new UserVipService();
|
|
|
- //3.2.1 获取当前用户VIP身份
|
|
|
+ //3.2 判断当前用户是否为会员
|
|
|
Boolean isVIP = false;
|
|
|
UserInfo userInfo = userInfoMapper.selectById(userPaidRecordVo.getUserId());
|
|
|
- if (userInfo.getIsVip().intValue() == 1 && userInfo.getVipExpireTime().after(new Date())) {
|
|
|
+ Date now = new Date();
|
|
|
+ if (userInfo.getIsVip().intValue() == 1 && userInfo.getVipExpireTime().after(now)) {
|
|
|
isVIP = true;
|
|
|
}
|
|
|
- //3.2.2 计算本次会员开始及过期时间
|
|
|
- //3.2.3 获取VIP套餐信息
|
|
|
+ //3.3 新增会员购买记录
|
|
|
+ UserVipService userVipService = new UserVipService();
|
|
|
+ userVipService.setOrderNo(userPaidRecordVo.getOrderNo());
|
|
|
+ userVipService.setUserId(userPaidRecordVo.getUserId());
|
|
|
+ //3.3.1 获取会员套餐信息得到服务月数
|
|
|
VipServiceConfig vipServiceConfig = vipServiceConfigMapper.selectById(userPaidRecordVo.getItemIdList().get(0));
|
|
|
Integer serviceMonth = vipServiceConfig.getServiceMonth();
|
|
|
-
|
|
|
- Date startTime = new Date();
|
|
|
+ //3.3.2 计算本次会员购买记录生效时间
|
|
|
+ //3.3.3 计算本次会员购买记录失效时间
|
|
|
if (!isVIP) {
|
|
|
- //普通用户
|
|
|
- userVipService.setStartTime(startTime);
|
|
|
- userVipService.setExpireTime(DateUtil.offsetMonth(startTime, serviceMonth));
|
|
|
+ //普通用户,生效时间为当前时间 失效时间=当前时间+购买会员套餐月数
|
|
|
+ userVipService.setStartTime(now);
|
|
|
+ DateTime expireTime = DateUtil.offsetMonth(now, serviceMonth);
|
|
|
+ userVipService.setExpireTime(expireTime);
|
|
|
} else {
|
|
|
- startTime = DateUtil.offsetDay(userInfo.getVipExpireTime(), 1);
|
|
|
- userVipService.setStartTime(startTime);
|
|
|
- userVipService.setExpireTime(DateUtil.offsetMonth(startTime, serviceMonth));
|
|
|
+ //会员用户,生效时间为=会员的到期时间+1 失效时间=会员的到期时间+购买会员套餐月数
|
|
|
+ Date vipExpireTime = userInfo.getVipExpireTime(); //6.14
|
|
|
+ userVipService.setStartTime(DateUtil.offsetDay(vipExpireTime, 1)); //6.15
|
|
|
+ userVipService.setExpireTime(DateUtil.offsetMonth(userVipService.getStartTime(), serviceMonth)); //7.15
|
|
|
}
|
|
|
- userVipService.setOrderNo(userPaidRecordVo.getOrderNo());
|
|
|
- userVipService.setUserId(userPaidRecordVo.getUserId());
|
|
|
userVipServiceMapper.insert(userVipService);
|
|
|
- //3.3 修改用户VIP标识及过期时间
|
|
|
+ //3.4 更新用户会员标识以及更新会员过期时间
|
|
|
userInfo.setIsVip(1);
|
|
|
userInfo.setVipExpireTime(userVipService.getExpireTime());
|
|
|
userInfoMapper.updateById(userInfo);
|
|
@@ -1649,12 +1652,12 @@ public class VIPPaidRecordStrategy implements PaidRecordStrategy {
|
|
|
**专辑购买项处理策略实现类**:其中Bean对象ID要跟前端提交项目类型一致
|
|
|
|
|
|
```java
|
|
|
-package com.atguigu.tingshu.user.strategy.impl;
|
|
|
+package com.atguigu.tingshu.user.pattern.impl;
|
|
|
|
|
|
-import cn.hutool.core.collection.CollectionUtil;
|
|
|
+import com.atguigu.tingshu.common.constant.SystemConstant;
|
|
|
import com.atguigu.tingshu.model.user.UserPaidAlbum;
|
|
|
import com.atguigu.tingshu.user.mapper.UserPaidAlbumMapper;
|
|
|
-import com.atguigu.tingshu.user.strategy.PaidRecordStrategy;
|
|
|
+import com.atguigu.tingshu.user.pattern.DeliveryStrategy;
|
|
|
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
@@ -1663,39 +1666,36 @@ import org.springframework.stereotype.Component;
|
|
|
|
|
|
/**
|
|
|
* @author: atguigu
|
|
|
- * @create: 2024-08-23 10:32
|
|
|
+ * @create: 2025-06-16 09:31
|
|
|
*/
|
|
|
@Slf4j
|
|
|
-@Component("1001")
|
|
|
-public class AlbumPaidRecordStrategy implements PaidRecordStrategy {
|
|
|
+@Component(SystemConstant.ORDER_ITEM_TYPE_ALBUM)//默认Bean的ID 为 albumDelivery
|
|
|
+public class AlbumDelivery implements DeliveryStrategy {
|
|
|
|
|
|
@Autowired
|
|
|
private UserPaidAlbumMapper userPaidAlbumMapper;
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
- * 处理购买商品类型为专辑-发货逻辑
|
|
|
+ * 虚拟物品-专辑-发货行为实现
|
|
|
* @param userPaidRecordVo
|
|
|
*/
|
|
|
@Override
|
|
|
- public void handlerPaidRecord(UserPaidRecordVo userPaidRecordVo) {
|
|
|
- log.info("[用户服务]处理购买商品类型为专辑发货逻辑");
|
|
|
- //2.TODO 处理购买项目类型-专辑
|
|
|
- //2.1 根据订单编号查询是否处理过专辑购买记录
|
|
|
- LambdaQueryWrapper<UserPaidAlbum> paidAlbumLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
|
|
- paidAlbumLambdaQueryWrapper.eq(UserPaidAlbum::getOrderNo, userPaidRecordVo.getOrderNo());
|
|
|
- if (userPaidAlbumMapper.selectCount(paidAlbumLambdaQueryWrapper) > 0) {
|
|
|
+ public void delivery(UserPaidRecordVo userPaidRecordVo) {
|
|
|
+ //1.1 根据订单编号查询已购专辑记录,如果存在,则返回
|
|
|
+ Long count = userPaidAlbumMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<UserPaidAlbum>()
|
|
|
+ .eq(UserPaidAlbum::getOrderNo, userPaidRecordVo.getOrderNo())
|
|
|
+ );
|
|
|
+ if (count > 0) {
|
|
|
+ log.info("已存在该订单:{}的已购专辑记录", userPaidRecordVo.getOrderNo());
|
|
|
return;
|
|
|
}
|
|
|
- //2.2 保存专辑购买记录
|
|
|
- if (CollectionUtil.isNotEmpty(userPaidRecordVo.getItemIdList())) {
|
|
|
- Long albumId = userPaidRecordVo.getItemIdList().get(0);
|
|
|
- UserPaidAlbum userPaidAlbum = new UserPaidAlbum();
|
|
|
- userPaidAlbum.setOrderNo(userPaidRecordVo.getOrderNo());
|
|
|
- userPaidAlbum.setUserId(userPaidRecordVo.getUserId());
|
|
|
- userPaidAlbum.setAlbumId(albumId);
|
|
|
- userPaidAlbumMapper.insert(userPaidAlbum);
|
|
|
- }
|
|
|
+ //1.2 如果不存在,则构建专辑已购记录完成保存
|
|
|
+ UserPaidAlbum userPaidAlbum = new UserPaidAlbum();
|
|
|
+ userPaidAlbum.setOrderNo(userPaidRecordVo.getOrderNo());
|
|
|
+ userPaidAlbum.setUserId(userPaidRecordVo.getUserId());
|
|
|
+ userPaidAlbum.setAlbumId(userPaidRecordVo.getItemIdList().get(0));
|
|
|
+ userPaidAlbumMapper.insert(userPaidAlbum);
|
|
|
}
|
|
|
}
|
|
|
```
|
|
@@ -1703,14 +1703,14 @@ public class AlbumPaidRecordStrategy implements PaidRecordStrategy {
|
|
|
**声音购买项处理策略实现类**:其中Bean对象ID要跟前端提交项目类型一致
|
|
|
|
|
|
```java
|
|
|
-package com.atguigu.tingshu.user.strategy.impl;
|
|
|
+package com.atguigu.tingshu.user.pattern.impl;
|
|
|
|
|
|
-import cn.hutool.core.collection.CollectionUtil;
|
|
|
import com.atguigu.tingshu.album.AlbumFeignClient;
|
|
|
+import com.atguigu.tingshu.common.constant.SystemConstant;
|
|
|
import com.atguigu.tingshu.model.album.TrackInfo;
|
|
|
import com.atguigu.tingshu.model.user.UserPaidTrack;
|
|
|
import com.atguigu.tingshu.user.mapper.UserPaidTrackMapper;
|
|
|
-import com.atguigu.tingshu.user.strategy.PaidRecordStrategy;
|
|
|
+import com.atguigu.tingshu.user.pattern.DeliveryStrategy;
|
|
|
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
@@ -1721,94 +1721,112 @@ import java.util.List;
|
|
|
|
|
|
/**
|
|
|
* @author: atguigu
|
|
|
- * @create: 2024-08-23 10:32
|
|
|
+ * @create: 2025-06-16 09:31
|
|
|
*/
|
|
|
@Slf4j
|
|
|
-@Component("1002")
|
|
|
-public class TrackPaidRecordStrategy implements PaidRecordStrategy {
|
|
|
+@Component(SystemConstant.ORDER_ITEM_TYPE_TRACK)//默认Bean的ID 为 albumDelivery
|
|
|
+public class TrackDelivery implements DeliveryStrategy {
|
|
|
|
|
|
@Autowired
|
|
|
- private AlbumFeignClient albumFeignClient;
|
|
|
+ private UserPaidTrackMapper userPaidTrackMapper;
|
|
|
|
|
|
@Autowired
|
|
|
- private UserPaidTrackMapper userPaidTrackMapper;
|
|
|
+ private AlbumFeignClient albumFeignClient;
|
|
|
|
|
|
/**
|
|
|
- * 处理购买商品类型为声音-发货逻辑
|
|
|
+ * 虚拟物品-声音-发货行为实现
|
|
|
+ *
|
|
|
* @param userPaidRecordVo
|
|
|
*/
|
|
|
@Override
|
|
|
- public void handlerPaidRecord(UserPaidRecordVo userPaidRecordVo) {
|
|
|
- log.info("[用户服务]处理购买商品类型为声音发货逻辑");
|
|
|
- //1.根据订单编号查询是否处理过声音购买记录
|
|
|
- LambdaQueryWrapper<UserPaidTrack> userPaidTrackLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
|
|
- userPaidTrackLambdaQueryWrapper.eq(UserPaidTrack::getOrderNo, userPaidRecordVo.getOrderNo());
|
|
|
- userPaidTrackLambdaQueryWrapper.select(UserPaidTrack::getId);
|
|
|
- if (userPaidTrackMapper.selectCount(userPaidTrackLambdaQueryWrapper) > 0) {
|
|
|
+ public void delivery(UserPaidRecordVo userPaidRecordVo) {
|
|
|
+ //2. 处理虚拟物品"声音"发货逻辑
|
|
|
+ //2.1 根据订单编号查询已购声音记录,如果存在,则返回
|
|
|
+ Long count = userPaidTrackMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<UserPaidTrack>()
|
|
|
+ .eq(UserPaidTrack::getOrderNo, userPaidRecordVo.getOrderNo())
|
|
|
+ );
|
|
|
+ if (count > 0) {
|
|
|
+ log.info("已存在该订单:{}的已购声音记录", userPaidRecordVo.getOrderNo());
|
|
|
return;
|
|
|
}
|
|
|
- //2.保存声音购买记录
|
|
|
- //2.1 根据声音ID查询声音信息获取所属专辑ID
|
|
|
+ //2.2 如果不存在,则构建声音已购记录完成保存
|
|
|
+ TrackInfo trackInfo = albumFeignClient.getTrackInfo(userPaidRecordVo.getItemIdList().get(0)).getData();
|
|
|
List<Long> trackIdList = userPaidRecordVo.getItemIdList();
|
|
|
- if (CollectionUtil.isNotEmpty(trackIdList)) {
|
|
|
- TrackInfo trackInfo = albumFeignClient.getTrackInfo(trackIdList.get(0)).getData();
|
|
|
- Long albumId = trackInfo.getAlbumId();
|
|
|
- for (Long trackId : trackIdList) {
|
|
|
- UserPaidTrack userPaidTrack = new UserPaidTrack();
|
|
|
- userPaidTrack.setAlbumId(albumId);
|
|
|
- userPaidTrack.setOrderNo(userPaidRecordVo.getOrderNo());
|
|
|
- userPaidTrack.setTrackId(trackId);
|
|
|
- userPaidTrack.setUserId(userPaidRecordVo.getUserId());
|
|
|
- userPaidTrackMapper.insert(userPaidTrack);
|
|
|
- }
|
|
|
+ for (Long trackId : trackIdList) {
|
|
|
+ UserPaidTrack userPaidTrack = new UserPaidTrack();
|
|
|
+ userPaidTrack.setOrderNo(userPaidRecordVo.getOrderNo());
|
|
|
+ userPaidTrack.setUserId(userPaidRecordVo.getUserId());
|
|
|
+ userPaidTrack.setAlbumId(trackInfo.getAlbumId());
|
|
|
+ userPaidTrack.setTrackId(trackId);
|
|
|
+ userPaidTrackMapper.insert(userPaidTrack);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
```
|
|
|
|
|
|
**提供工厂类**组装所有的策略实现类
|
|
|
|
|
|
```java
|
|
|
-package com.atguigu.tingshu.user.strategy.fac;
|
|
|
+package com.atguigu.tingshu.user.pattern.impl;
|
|
|
|
|
|
-import com.atguigu.tingshu.user.strategy.PaidRecordStrategy;
|
|
|
-import com.atguigu.tingshu.user.strategy.impl.AlbumPaidRecordStrategy;
|
|
|
-import com.atguigu.tingshu.user.strategy.impl.TrackPaidRecordStrategy;
|
|
|
+import com.atguigu.tingshu.album.AlbumFeignClient;
|
|
|
+import com.atguigu.tingshu.common.constant.SystemConstant;
|
|
|
+import com.atguigu.tingshu.model.album.TrackInfo;
|
|
|
+import com.atguigu.tingshu.model.user.UserPaidTrack;
|
|
|
+import com.atguigu.tingshu.user.mapper.UserPaidTrackMapper;
|
|
|
+import com.atguigu.tingshu.user.pattern.DeliveryStrategy;
|
|
|
+import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
-import javax.swing.*;
|
|
|
-import java.util.Map;
|
|
|
+import java.util.List;
|
|
|
|
|
|
/**
|
|
|
* @author: atguigu
|
|
|
- * @create: 2024-08-23 10:35
|
|
|
+ * @create: 2025-06-16 09:31
|
|
|
*/
|
|
|
@Slf4j
|
|
|
-@Component
|
|
|
-public class StrategyFactory {
|
|
|
+@Component(SystemConstant.ORDER_ITEM_TYPE_TRACK)//默认Bean的ID 为 albumDelivery
|
|
|
+public class TrackDelivery implements DeliveryStrategy {
|
|
|
|
|
|
- /**
|
|
|
- * 通过Spring注入机制将PaidRecordStrategy接口下所有实现类对象自动注入到Map
|
|
|
- * Map中Key 为策略类型-策略实现类Bean名称(1001,1002,1003)
|
|
|
- * Map中Value 具体策略实现类对象
|
|
|
- */
|
|
|
@Autowired
|
|
|
- private Map<String, PaidRecordStrategy> strategyMap;
|
|
|
+ private UserPaidTrackMapper userPaidTrackMapper;
|
|
|
|
|
|
+ @Autowired
|
|
|
+ private AlbumFeignClient albumFeignClient;
|
|
|
|
|
|
/**
|
|
|
- * 根据策略类型获取对应策略实现类对象
|
|
|
+ * 虚拟物品-声音-发货行为实现
|
|
|
*
|
|
|
- * @param strategy
|
|
|
- * @return
|
|
|
+ * @param userPaidRecordVo
|
|
|
*/
|
|
|
- public PaidRecordStrategy getStrategy(String strategy) {
|
|
|
- if (strategyMap.containsKey(strategy)) {
|
|
|
- return strategyMap.get(strategy);
|
|
|
+ @Override
|
|
|
+ public void delivery(UserPaidRecordVo userPaidRecordVo) {
|
|
|
+ //2. 处理虚拟物品"声音"发货逻辑
|
|
|
+ //2.1 根据订单编号查询已购声音记录,如果存在,则返回
|
|
|
+ Long count = userPaidTrackMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<UserPaidTrack>()
|
|
|
+ .eq(UserPaidTrack::getOrderNo, userPaidRecordVo.getOrderNo())
|
|
|
+ );
|
|
|
+ if (count > 0) {
|
|
|
+ log.info("已存在该订单:{}的已购声音记录", userPaidRecordVo.getOrderNo());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ //2.2 如果不存在,则构建声音已购记录完成保存
|
|
|
+ TrackInfo trackInfo = albumFeignClient.getTrackInfo(userPaidRecordVo.getItemIdList().get(0)).getData();
|
|
|
+ List<Long> trackIdList = userPaidRecordVo.getItemIdList();
|
|
|
+ for (Long trackId : trackIdList) {
|
|
|
+ UserPaidTrack userPaidTrack = new UserPaidTrack();
|
|
|
+ userPaidTrack.setOrderNo(userPaidRecordVo.getOrderNo());
|
|
|
+ userPaidTrack.setUserId(userPaidRecordVo.getUserId());
|
|
|
+ userPaidTrack.setAlbumId(trackInfo.getAlbumId());
|
|
|
+ userPaidTrack.setTrackId(trackId);
|
|
|
+ userPaidTrackMapper.insert(userPaidTrack);
|
|
|
}
|
|
|
- throw new RuntimeException("策略类型不存在");
|
|
|
}
|
|
|
}
|
|
|
```
|
|
@@ -1817,19 +1835,21 @@ public class StrategyFactory {
|
|
|
|
|
|
```java
|
|
|
@Autowired
|
|
|
-private StrategyFactory strategyFactory;
|
|
|
+private DeliveryStrategyFactory deliveryStrategyFactory;
|
|
|
|
|
|
/**
|
|
|
- * 保存用户不同购买项目类型的购买记录
|
|
|
+ * 用户支付成功后,虚拟物品发货
|
|
|
*
|
|
|
* @param userPaidRecordVo
|
|
|
+ * @return
|
|
|
*/
|
|
|
@Override
|
|
|
public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
|
|
|
- //1001-专辑 1002-声音 1003-vip会员
|
|
|
- String itemType = userPaidRecordVo.getItemType();
|
|
|
- PaidRecordStrategy strategy = strategyFactory.getStrategy(itemType);
|
|
|
- strategy.handlerPaidRecord(userPaidRecordVo);
|
|
|
+ //TODO 策略模式+工厂模式优化
|
|
|
+ //1. 根据商品类型从工厂中返回具体策略实现类对象
|
|
|
+ DeliveryStrategy strategy = deliveryStrategyFactory.getStrategy(userPaidRecordVo.getItemType());
|
|
|
+ //2. 调用策略实现类对象进行虚拟物品发货逻辑
|
|
|
+ strategy.delivery(userPaidRecordVo);
|
|
|
}
|
|
|
```
|
|
|
|
|
@@ -1883,23 +1903,19 @@ OrderInfo saveOrderInfo(Long userId, OrderInfoVo orderInfoVo);
|
|
|
```
|
|
|
|
|
|
```java
|
|
|
-@Autowired
|
|
|
-private AccountFeignClient accountFeignClient;
|
|
|
-
|
|
|
/**
|
|
|
- * 提交订单
|
|
|
+ * 提交订单,支持付款方式(微信、余额)
|
|
|
*
|
|
|
- * @param userId 用户ID
|
|
|
- * @param orderInfoVo 订单vo信息
|
|
|
- * @return {orderNo:"订单编号"}
|
|
|
+ * @param orderInfoVo 订单vo
|
|
|
+ * @return {orderNo:"订单唯一编号"}
|
|
|
*/
|
|
|
@Override
|
|
|
@GlobalTransactional(rollbackFor = Exception.class)
|
|
|
-public Map<String, String> submitOrder(Long userId, OrderInfoVo orderInfoVo) {
|
|
|
- //1.业务校验:验证流水号是否一致 避免用户误触回退按钮导致订单重复提交
|
|
|
- //1.1 确保判断流水号删除流水号原子性采用lua脚本来实现
|
|
|
+public Map<String, String> submitOrder(OrderInfoVo orderInfoVo) {
|
|
|
+ Long userId = AuthContextHolder.getUserId();
|
|
|
+ //1.业务校验1,验证流水号,防止订单重复提交 采用Lua脚本判断删除原子性
|
|
|
String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
|
|
|
- //1.2 创建Lua脚本
|
|
|
+ //KEYS[1]=流水号Key ARGV[1]=用户提交流水号值
|
|
|
String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
|
|
|
"then\n" +
|
|
|
" return redis.call(\"del\",KEYS[1])\n" +
|
|
@@ -1909,54 +1925,74 @@ public Map<String, String> submitOrder(Long userId, OrderInfoVo orderInfoVo) {
|
|
|
DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>(script, Boolean.class);
|
|
|
Boolean flag = (Boolean) redisTemplate.execute(redisScript, Arrays.asList(tradeKey), orderInfoVo.getTradeNo());
|
|
|
if (!flag) {
|
|
|
- throw new GuiguException(500, "流水号校验失败请重试");
|
|
|
+ throw new GuiguException(500, "验证流水号失败");
|
|
|
}
|
|
|
- //2.业务校验:验证签名是否一致 避免用户篡改订单vo中参数导致商户利益损害
|
|
|
- //2.1 将提交订单VO转为Map用于验证签名
|
|
|
+
|
|
|
+ //2.业务校验2,验证签名,防止订单确认页中订单数据(商品、金额、数量)被篡改
|
|
|
+ //2.1 将参与签名订单VO转为Map,TODO 加签时付款方式属性payWay未参与签名
|
|
|
Map<String, Object> orderInfoMap = BeanUtil.beanToMap(orderInfoVo);
|
|
|
- //2.2 将Map中的payWay属性去除 在订单确认页生成签名没有加入payWay属性
|
|
|
+ //2.2 手动删除掉签名属性payWay
|
|
|
orderInfoMap.remove("payWay");
|
|
|
+ //2.3 验签
|
|
|
SignHelper.checkSign(orderInfoMap);
|
|
|
- //3.核心业务1:保存订单相关信息(订单、订单明细、订单减免) 订单状态:未支付
|
|
|
- OrderInfo orderInfo = this.saveOrderInfo(userId, orderInfoVo);
|
|
|
|
|
|
- //4.判断付款方式-如果是账户余额付款
|
|
|
- if (SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfoVo.getPayWay())) {
|
|
|
- //4.1.核心业务2:余额扣减
|
|
|
- //4.1.1 构建扣减余额VO对象
|
|
|
+ //TODO 后续核心业务处理
|
|
|
+ //3. 保存订单,返回订单对象 此时订单支付状态:未支付
|
|
|
+ OrderInfo orderInfo = this.saveOrderInfo(orderInfoVo, userId);
|
|
|
+
|
|
|
+ //支付方式:1101-微信 1102-支付宝 1103-账户余额
|
|
|
+ String payWay = orderInfoVo.getPayWay();
|
|
|
+
|
|
|
+ //4.TODO 处理付款方式为余额
|
|
|
+ if (SystemConstant.ORDER_PAY_ACCOUNT.equals(payWay)) {
|
|
|
+ //4.1 远程调用"账户服务",检查扣减余额
|
|
|
+ //4.1.1 构建扣减账户余额VO对象AccountDeductVo
|
|
|
AccountDeductVo accountDeductVo = new AccountDeductVo();
|
|
|
accountDeductVo.setOrderNo(orderInfo.getOrderNo());
|
|
|
accountDeductVo.setUserId(orderInfo.getUserId());
|
|
|
accountDeductVo.setAmount(orderInfo.getOrderAmount());
|
|
|
accountDeductVo.setContent(orderInfo.getOrderTitle());
|
|
|
- //4.1.2 远程调用账户服务扣减余额 一定要判断Feign调用结果业务状态码
|
|
|
+
|
|
|
+ //4.1.2 远程调用
|
|
|
Result result = accountFeignClient.checkAndDeduct(accountDeductVo);
|
|
|
- if (!result.getCode().equals(200)) {
|
|
|
- throw new GuiguException(500, "账户余额不足");
|
|
|
+ //4.1.3 判断远程调用响应业务状态码,如果不是200抛出异常,触发全局事务回滚
|
|
|
+ if (result.getCode().intValue() != 200) {
|
|
|
+ //4.2 扣减余额失败,业务终止,回滚全局事务
|
|
|
+ throw new GuiguException(result.getCode(), result.getMessage());
|
|
|
}
|
|
|
-
|
|
|
- //扣减余额成功后,修改订单状态:已支付
|
|
|
+ //4.3 扣减余额成功,订单状态改为已支付
|
|
|
orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
|
|
|
orderInfoMapper.updateById(orderInfo);
|
|
|
|
|
|
- //4.2.核心业务3:虚拟物品发货
|
|
|
- //4.2.1 构建虚拟物品发货VO对象
|
|
|
+ //4.4 远程调用"用户服务",虚拟物品发货(增加购买记录)
|
|
|
+ //4.4.1 构建虚拟物品发货VO对象UserPaidRecordVo
|
|
|
UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
|
|
|
userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());
|
|
|
userPaidRecordVo.setUserId(orderInfo.getUserId());
|
|
|
userPaidRecordVo.setItemType(orderInfo.getItemType());
|
|
|
- List<Long> itemIdList = orderInfoVo.getOrderDetailVoList().stream().map(OrderDetailVo::getItemId).collect(Collectors.toList());
|
|
|
- userPaidRecordVo.setItemIdList(itemIdList);
|
|
|
- //4.2.2 远程调用用户服务虚拟物品发货 一定要判断Feign调用结果业务状态码
|
|
|
- result = userFeignClient.savePaidRecord(userPaidRecordVo);
|
|
|
- if (!result.getCode().equals(200)) {
|
|
|
- throw new GuiguException(500, "虚拟物品发货异常");
|
|
|
+ List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
|
|
|
+ if (CollUtil.isNotEmpty(orderDetailVoList)) {
|
|
|
+ List<Long> itemIdList = orderDetailVoList
|
|
|
+ .stream()
|
|
|
+ .map(OrderDetailVo::getItemId)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ userPaidRecordVo.setItemIdList(itemIdList);
|
|
|
+ //4.4.2 远程调用
|
|
|
+ result = userFeignClient.savePaidRecord(userPaidRecordVo);
|
|
|
+ if (result.getCode().intValue() != 200) {
|
|
|
+ //4.2 虚拟物品发货失败,业务终止,回滚全局事务
|
|
|
+ throw new GuiguException(result.getCode(), result.getMessage());
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- //5.TODO 判断付款方式-如果是微信付款,采用延迟消息完成延迟关单
|
|
|
- if (SystemConstant.ORDER_PAY_WAY_WEIXIN.equals(orderInfoVo.getPayWay())) {
|
|
|
-
|
|
|
+ //5.处理付款方式为微信
|
|
|
+ if (SystemConstant.ORDER_PAY_WAY_WEIXIN.equals(payWay)) {
|
|
|
+ //5.1 发送延迟关单消息,自动关闭超时未支付订单
|
|
|
+ int delayTime = 60 * 15;
|
|
|
+ rabbitService.sendDelayMessage(MqConst.EXCHANGE_CANCEL_ORDER, MqConst.ROUTING_CANCEL_ORDER, orderInfo.getId(), delayTime);
|
|
|
}
|
|
|
+
|
|
|
+ //x.封装响应结果 如果是微信支付,根据响应订单编号对接微信支付;如果是余额支付成功,返回订单编号,查询订单详情
|
|
|
Map<String, String> map = new HashMap<>();
|
|
|
map.put("orderNo", orderInfo.getOrderNo());
|
|
|
return map;
|
|
@@ -1993,51 +2029,53 @@ private OrderDetailMapper orderDetailMapper;
|
|
|
private OrderDerateMapper orderDerateMapper;
|
|
|
|
|
|
/**
|
|
|
- * 保存订单相关信息(订单、订单明细、订单减免)
|
|
|
+ * 保存订单相关信息(订单、订单明细、优惠明细)
|
|
|
*
|
|
|
- * @param userId
|
|
|
* @param orderInfoVo
|
|
|
+ * @param userId
|
|
|
* @return
|
|
|
*/
|
|
|
@Override
|
|
|
-public OrderInfo saveOrderInfo(Long userId, OrderInfoVo orderInfoVo) {
|
|
|
- //1.保存订单信息
|
|
|
+public OrderInfo saveOrderInfo(OrderInfoVo orderInfoVo, Long userId) {
|
|
|
+ //1.封装订单对象,保存订单
|
|
|
OrderInfo orderInfo = BeanUtil.copyProperties(orderInfoVo, OrderInfo.class);
|
|
|
- //1.1 设置订单状态设置为:未支付
|
|
|
- orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
|
|
|
- //1.2 设置用户ID
|
|
|
+ //1.1 设置用户ID
|
|
|
orderInfo.setUserId(userId);
|
|
|
- //1.3 设置订单名称(从订单项获取名称)
|
|
|
+ //1.2 设置订单标题
|
|
|
List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
|
|
|
- if (CollectionUtil.isNotEmpty(orderDetailVoList)) {
|
|
|
- orderInfo.setOrderTitle(orderDetailVoList.get(0).getItemName());
|
|
|
+ if (CollUtil.isNotEmpty(orderDetailVoList)) {
|
|
|
+ OrderDetailVo orderDetailVo = orderDetailVoList.get(0);
|
|
|
+ orderInfo.setOrderTitle(orderDetailVo.getItemName());
|
|
|
}
|
|
|
- //1.4 设置订单编号(确保全局唯一用于对接支付平台) 年月日+雪花算法
|
|
|
- String oderNo = DateUtil.today().replaceAll("-", "") + IdUtil.getSnowflake(1, 1).nextIdStr();
|
|
|
- orderInfo.setOrderNo(oderNo);
|
|
|
+ //1.3 设置订单编号 确保全局唯一且趋势递增 生成策略=当日+雪花算法
|
|
|
+ String orderNo = DateUtil.today().replaceAll("-", "") + IdUtil.getSnowflakeNextId();
|
|
|
+ orderInfo.setOrderNo(orderNo);
|
|
|
+ //1.4 设置订单状态 订单状态:0901-未支付 0902-已支付 0903-已取消
|
|
|
+ orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
|
|
|
+ //1.5 保存订单
|
|
|
orderInfoMapper.insert(orderInfo);
|
|
|
Long orderId = orderInfo.getId();
|
|
|
- //2.保存订单明细列表
|
|
|
- //2.1 获取订单明细列表
|
|
|
- if (CollectionUtil.isNotEmpty(orderDetailVoList)) {
|
|
|
- for (OrderDetailVo orderDetailVo : orderDetailVoList) {
|
|
|
- OrderDetail orderDetail = BeanUtil.copyProperties(orderDetailVo, OrderDetail.class);
|
|
|
- //2.1 关联订单
|
|
|
- orderDetail.setOrderId(orderId);
|
|
|
- //2.2 保存订单明细
|
|
|
- orderDetailMapper.insert(orderDetail);
|
|
|
- }
|
|
|
+
|
|
|
+ //2.封装订单明细对象,保存订单明细
|
|
|
+ if (CollUtil.isNotEmpty(orderDetailVoList)) {
|
|
|
+ List<OrderDetail> orderDetailList = orderDetailVoList
|
|
|
+ .stream()
|
|
|
+ .map(orderDetailVo -> {
|
|
|
+ OrderDetail orderDetail = BeanUtil.copyProperties(orderDetailVo, OrderDetail.class);
|
|
|
+ //将订单明细关联订单ID
|
|
|
+ orderDetail.setOrderId(orderId);
|
|
|
+ return orderDetail;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+ orderDetailService.saveBatch(orderDetailList);
|
|
|
}
|
|
|
- //3.保存订单减免列表
|
|
|
+ //3.封装订单优惠对象,保存订单优惠明细
|
|
|
List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
|
|
|
- if (CollectionUtil.isNotEmpty(orderDerateVoList)) {
|
|
|
- for (OrderDerateVo orderDerateVo : orderDerateVoList) {
|
|
|
+ if (CollUtil.isNotEmpty(orderDerateVoList)) {
|
|
|
+ orderDerateVoList.stream().forEach(orderDerateVo -> {
|
|
|
OrderDerate orderDerate = BeanUtil.copyProperties(orderDerateVo, OrderDerate.class);
|
|
|
- //3.1 关联订单
|
|
|
orderDerate.setOrderId(orderId);
|
|
|
- //3.2 保存订单减免
|
|
|
orderDerateMapper.insert(orderDerate);
|
|
|
- }
|
|
|
+ });
|
|
|
}
|
|
|
return orderInfo;
|
|
|
}
|