Browse Source

day14
策略模式+工厂模式优化;分布式事务Seata;

it_lv 1 week ago
parent
commit
0fbb53d266

+ 16 - 0
service/service-account/pom.xml

@@ -23,6 +23,22 @@
             <artifactId>rabbit-util</artifactId>
             <version>1.0</version>
         </dependency>
+        <!--seata-->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
+            <!-- 默认seata客户端版本比较低,排除后重新引入指定版本-->
+            <exclusions>
+                <exclusion>
+                    <groupId>io.seata</groupId>
+                    <artifactId>seata-spring-boot-starter</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>io.seata</groupId>
+            <artifactId>seata-spring-boot-starter</artifactId>
+        </dependency>
     </dependencies>
 
     <build>

+ 16 - 0
service/service-order/pom.xml

@@ -28,6 +28,22 @@
             <artifactId>service-account-client</artifactId>
             <version>1.0</version>
         </dependency>
+        <!--seata-->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
+            <!-- 默认seata客户端版本比较低,排除后重新引入指定版本-->
+            <exclusions>
+                <exclusion>
+                    <groupId>io.seata</groupId>
+                    <artifactId>seata-spring-boot-starter</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>io.seata</groupId>
+            <artifactId>seata-spring-boot-starter</artifactId>
+        </dependency>
     </dependencies>
 
     <build>

+ 8 - 1
service/service-order/src/main/java/com/atguigu/tingshu/order/service/impl/OrderInfoServiceImpl.java

@@ -33,6 +33,7 @@ import com.atguigu.tingshu.vo.user.UserInfoVo;
 import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
 import com.baomidou.mybatisplus.core.toolkit.IdWorker;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.seata.spring.annotation.GlobalTransactional;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
@@ -246,6 +247,7 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
      * @return {orderNo:订单编号}
      */
     @Override
+    @GlobalTransactional(rollbackFor = Exception.class)
     public String submitOrder(Long userId, OrderInfoVo orderInfoVo) {
         //1.业务校验-验证流水号 防止回退造成订单重复提交
         //1.1 从Redis中获取当前用户存入流水号值
@@ -290,7 +292,7 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
             accountDeductVo.setContent(orderInfo.getOrderTitle());
             //4.1.2 远程调用完成扣减余额
             Result result = accountFeignClient.checkAndDeduct(accountDeductVo);
-            //4.1.3 判断业务状态码是否为200
+            //4.1.3 判断业务状态码是否为200 TODO 如果这里不判断响应业务状态码 下游系统调用发生异常,由于存在全局异常处理,正常响应Result Http请求状态码200
             if (!result.getCode().equals(200)) {
                 throw new GuiguException(result.getCode(), result.getMessage());
             }
@@ -315,7 +317,11 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
                 if (!result.getCode().equals(200)) {
                     throw new GuiguException(result.getCode(), result.getMessage());
                 }
+            }
 
+            //模拟异常
+            if (orderInfo.getId() % 2 == 0) {
+                int i = 1 / 0;
             }
         }
         if (ORDER_PAY_WAY_WEIXIN.equals(payWay)) {
@@ -334,6 +340,7 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
      * @return
      */
     @Override
+
     public OrderInfo saveOrderInfo(OrderInfoVo orderInfoVo) {
         //1.保存订单信息
         //1.1 将订单VO拷贝到订单PO对象中

+ 16 - 0
service/service-user/pom.xml

@@ -37,6 +37,22 @@
             <artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
             <version>4.5.0</version>
         </dependency>
+        <!--seata-->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
+            <!-- 默认seata客户端版本比较低,排除后重新引入指定版本-->
+            <exclusions>
+                <exclusion>
+                    <groupId>io.seata</groupId>
+                    <artifactId>seata-spring-boot-starter</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>io.seata</groupId>
+            <artifactId>seata-spring-boot-starter</artifactId>
+        </dependency>
     </dependencies>
 
     <build>

+ 11 - 91
service/service-user/src/main/java/com/atguigu/tingshu/user/service/impl/UserInfoServiceImpl.java

@@ -5,20 +5,21 @@ import cn.binarywang.wx.miniapp.api.WxMaUserService;
 import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.IdUtil;
 import com.atguigu.tingshu.album.AlbumFeignClient;
 import com.atguigu.tingshu.common.constant.RedisConstant;
-import com.atguigu.tingshu.common.constant.SystemConstant;
 import com.atguigu.tingshu.common.rabbit.constant.MqConst;
 import com.atguigu.tingshu.common.rabbit.service.RabbitService;
 import com.atguigu.tingshu.common.util.AuthContextHolder;
-import com.atguigu.tingshu.model.album.TrackInfo;
-import com.atguigu.tingshu.model.user.*;
+import com.atguigu.tingshu.model.user.UserInfo;
+import com.atguigu.tingshu.model.user.UserPaidAlbum;
+import com.atguigu.tingshu.model.user.UserPaidTrack;
 import com.atguigu.tingshu.user.mapper.*;
 import com.atguigu.tingshu.user.service.UserInfoService;
 import com.atguigu.tingshu.user.service.UserPaidTrackService;
+import com.atguigu.tingshu.user.strategy.PaidRecordStrategy;
+import com.atguigu.tingshu.user.strategy.factory.StrategyFactory;
 import com.atguigu.tingshu.vo.user.UserInfoVo;
 import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -30,7 +31,6 @@ import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 
 import java.math.BigDecimal;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -249,6 +249,9 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> i
         return null;
     }
 
+    @Autowired
+    private StrategyFactory strategyFactory;
+
     /**
      * 虚拟物品发货,保存购买记录(声音、专辑、VIP会员)
      *
@@ -259,93 +262,10 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> i
     public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
         //1.获取付款项目类型  付款项目类型: 1001-专辑 1002-声音 1003-vip会员
         String itemType = userPaidRecordVo.getItemType();
-        //2.处理付款项目类型为:声音 新增声音购买记录
-        if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(itemType)) {
-            //2.1 判断订单编号是否已处理
-            Long count = userPaidTrackMapper.selectCount(
-                    new LambdaQueryWrapper<UserPaidTrack>()
-                            .eq(UserPaidTrack::getOrderNo, userPaidRecordVo.getOrderNo())
-            );
-            if (count > 0) {
-                return;
-            }
-            //2.2 未处理 构建声音购买记录(可能存在多条) 完成新增
-            List<Long> trackIdList = userPaidRecordVo.getItemIdList();
-            if (CollUtil.isNotEmpty(trackIdList)) {
-                //2.2.1 根据声音ID远程调用"专辑服务"查询声音信息
-                TrackInfo trackInfo = albumFeignClient.getTrackInfo(trackIdList.get(0)).getData();
-                //2.2.2 批量保存声音购买记录
-                List<UserPaidTrack> userPaidTrackList = trackIdList
-                        .stream()
-                        .map(trackId -> {
-                            UserPaidTrack userPaidTrack = new UserPaidTrack();
-                            userPaidTrack.setOrderNo(userPaidRecordVo.getOrderNo());
-                            userPaidTrack.setUserId(userPaidRecordVo.getUserId());
-                            userPaidTrack.setAlbumId(trackInfo.getAlbumId());
-                            userPaidTrack.setTrackId(trackId);
-                            return userPaidTrack;
-                        }).collect(Collectors.toList());
-                userPaidTrackService.saveBatch(userPaidTrackList);
-            }
-        } else if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)) {
-            //3.处理付款项目类型为:专辑 新增专辑购买记录
-            //3.1 判断订单编号是否已处理
-            Long count = userPaidAlbumMapper.selectCount(
-                    new LambdaQueryWrapper<UserPaidAlbum>().eq(UserPaidAlbum::getOrderNo, userPaidRecordVo.getOrderNo())
-            );
-            if (count > 0) {
-                return;
-            }
-            //3.2 创建专辑购买记录对象 新增
-            UserPaidAlbum userPaidAlbum = new UserPaidAlbum();
-            userPaidAlbum.setOrderNo(userPaidRecordVo.getOrderNo());
-            userPaidAlbum.setUserId(userPaidRecordVo.getUserId());
-            userPaidAlbum.setAlbumId(userPaidRecordVo.getItemIdList().get(0));
-            userPaidAlbumMapper.insert(userPaidAlbum);
-        } else if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)) {
-            //4.处理付款项目类型为:VIP套餐 新增套餐购买记录 更新 会员标识及过期时间
-            //4.1 判断订单编号是否已处理
-            Long count = userVipServiceMapper.selectCount(
-                    new LambdaQueryWrapper<UserVipService>()
-                            .eq(UserVipService::getOrderNo, userPaidRecordVo.getOrderNo())
-            );
-            if (count > 0) {
-                return;
-            }
-            //4.2 根据用户ID查询用户信息 判断是否为VIP
-            UserInfoVo userInfoVo = this.getUserInfo(userPaidRecordVo.getUserId());
-            Boolean isVIP = false;
-            if (userInfoVo.getIsVip().intValue() == 1 && userInfoVo.getVipExpireTime().after(new Date())) {
-                isVIP = true;
-            }
-
-            //4.3 根据套餐ID查询套餐信息 得到套餐服务月数
-            VipServiceConfig vipServiceConfig = vipServiceConfigMapper.selectById(userPaidRecordVo.getItemIdList().get(0));
-            Integer serviceMonth = vipServiceConfig.getServiceMonth();
-
-            //4.4 封装会员购买套餐记录并且保存 计算会员生效时间跟过期时间
-            UserVipService userVipService = new UserVipService();
-            //4.4.1 如果是新开会员,本次会员购买记录生效时间=当前系统时间,过期时间=当前系统时间+套餐服务月数
-            if (!isVIP) {
-                userVipService.setStartTime(new Date());
-                DateTime expireTime = DateUtil.offsetMonth(new Date(), serviceMonth);
-                userVipService.setExpireTime(expireTime);
-            } else {
-                //4.4.2 如果是续费会员,本次会员购买记录生效时间=现有会员过期时间+1天   过期时间:现有会员过期时间+1天+服务月数
-                userVipService.setStartTime(DateUtil.offsetDay(userInfoVo.getVipExpireTime(), 1));
-                userVipService.setExpireTime(DateUtil.offsetMonth(userVipService.getStartTime(), serviceMonth));
-            }
-            //4.4.3 设置其他属性
-            userVipService.setOrderNo(userPaidRecordVo.getOrderNo());
-            userVipService.setUserId(userPaidRecordVo.getUserId());
-            userVipServiceMapper.insert(userVipService);
 
-            //4.5 更新用户表中会员标识以及过期时间
-            userInfoVo.setIsVip(1);
-            userInfoVo.setVipExpireTime(userVipService.getExpireTime());
-            UserInfo userInfo = BeanUtil.copyProperties(userInfoVo, UserInfo.class);
-            baseMapper.updateById(userInfo);
-        }
+        //2.从策略工厂中获取付款项目类型对应的策略实现类对象
+        PaidRecordStrategy strategy = strategyFactory.getStrategy(itemType);
+        strategy.handlerPaidRecord(userPaidRecordVo);
     }
 
 }

+ 17 - 0
service/service-user/src/main/java/com/atguigu/tingshu/user/strategy/PaidRecordStrategy.java

@@ -0,0 +1,17 @@
+package com.atguigu.tingshu.user.strategy;
+
+import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
+
+/**
+ * 处理不同类型商品购买记录的策略接口
+ */
+public interface PaidRecordStrategy {
+
+
+    /**
+     * 抽象出公用处理不同购买项目类型接口方法
+     * @param userPaidRecordVo
+     */
+    public void handlerPaidRecord(UserPaidRecordVo userPaidRecordVo);
+
+}

+ 52 - 0
service/service-user/src/main/java/com/atguigu/tingshu/user/strategy/factory/StrategyFactory.java

@@ -0,0 +1,52 @@
+package com.atguigu.tingshu.user.strategy.factory;
+
+import com.atguigu.tingshu.common.execption.GuiguException;
+import com.atguigu.tingshu.user.strategy.PaidRecordStrategy;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 策略工厂:管理所有策略实现类对象;对外提供根据商品类型返回策略实现对象方法
+ *
+ * @author: atguigu
+ * @create: 2025-03-25 09:14
+ */
+@Slf4j
+@Component
+public class StrategyFactory {
+
+
+    /**
+     * 自动注入方式为Map注入
+     * Map中key是Bean对象ID,自动从IOC容器中获取PaidRecordStrategy类型Bean,value是策略实现类Bean对象
+     */
+    @Autowired
+    private Map<String, PaidRecordStrategy> strategyMap;
+
+    /**
+     * 获取策略实现类对象注入到List
+     */
+    @Autowired
+    private List<PaidRecordStrategy> listStrategy;
+
+
+    /**
+     * 根据商品类型返回策略实现对象
+     * @param type
+     * @return
+     */
+    public PaidRecordStrategy getStrategy(String type) {
+        //根据商品类型返回对应的策略实现类对象
+        if (!strategyMap.containsKey(type)) {
+            log.error("[用户服务-策略工厂]未找到适配策略实现类:{}", type);
+            throw new GuiguException(500, "未找到适配策略实现类");
+        }
+        PaidRecordStrategy strategy = strategyMap.get(type);
+        log.info("[用户服务-策略工厂]根据商品类型返回对应的策略实现类对象:{}", strategy);
+        return strategy;
+    }
+}

+ 45 - 0
service/service-user/src/main/java/com/atguigu/tingshu/user/strategy/impl/AlbumPaidRecordStrategy.java

@@ -0,0 +1,45 @@
+package com.atguigu.tingshu.user.strategy.impl;
+
+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.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;
+
+/**
+ * @author: atguigu
+ * @create: 2025-03-25 09:08
+ */
+@Slf4j
+@Component("1001") //默认Bean对象ID=类名(首首字母小写)
+public class AlbumPaidRecordStrategy implements PaidRecordStrategy {
+
+    @Autowired
+    private UserPaidAlbumMapper userPaidAlbumMapper;
+
+    /**
+     * 处理专辑虚拟物品发货逻辑
+     * @param userPaidRecordVo
+     */
+    @Override
+    public void handlerPaidRecord(UserPaidRecordVo userPaidRecordVo) {
+        log.info("处理专辑虚拟物品发货逻辑");
+        //3.处理付款项目类型为:专辑 新增专辑购买记录
+        //3.1 判断订单编号是否已处理
+        Long count = userPaidAlbumMapper.selectCount(
+                new LambdaQueryWrapper<UserPaidAlbum>().eq(UserPaidAlbum::getOrderNo, userPaidRecordVo.getOrderNo())
+        );
+        if (count > 0) {
+            return;
+        }
+        //3.2 创建专辑购买记录对象 新增
+        UserPaidAlbum userPaidAlbum = new UserPaidAlbum();
+        userPaidAlbum.setOrderNo(userPaidRecordVo.getOrderNo());
+        userPaidAlbum.setUserId(userPaidRecordVo.getUserId());
+        userPaidAlbum.setAlbumId(userPaidRecordVo.getItemIdList().get(0));
+        userPaidAlbumMapper.insert(userPaidAlbum);
+    }
+}

+ 66 - 0
service/service-user/src/main/java/com/atguigu/tingshu/user/strategy/impl/TrackPaidRecordStrategy.java

@@ -0,0 +1,66 @@
+package com.atguigu.tingshu.user.strategy.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import com.atguigu.tingshu.album.AlbumFeignClient;
+import com.atguigu.tingshu.model.album.TrackInfo;
+import com.atguigu.tingshu.model.user.UserPaidTrack;
+import com.atguigu.tingshu.user.service.UserPaidTrackService;
+import com.atguigu.tingshu.user.strategy.PaidRecordStrategy;
+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 java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author: atguigu
+ * @create: 2025-03-25 09:08
+ */
+@Slf4j
+@Component("1002") //默认Bean对象ID=类名(首首字母小写)
+public class TrackPaidRecordStrategy implements PaidRecordStrategy {
+
+    @Autowired
+    private UserPaidTrackService userPaidTrackService;
+
+    @Autowired
+    private AlbumFeignClient albumFeignClient;
+
+    /**
+     * 处理声音虚拟物品发货逻辑
+     * @param userPaidRecordVo
+     */
+    @Override
+    public void handlerPaidRecord(UserPaidRecordVo userPaidRecordVo) {
+        log.info("处理声音虚拟物品发货逻辑");
+        //2.1 判断订单编号是否已处理
+        Long count = userPaidTrackService.count(
+                new LambdaQueryWrapper<UserPaidTrack>()
+                        .eq(UserPaidTrack::getOrderNo, userPaidRecordVo.getOrderNo())
+        );
+        if (count > 0) {
+            return;
+        }
+        //2.2 未处理 构建声音购买记录(可能存在多条) 完成新增
+        List<Long> trackIdList = userPaidRecordVo.getItemIdList();
+        if (CollUtil.isNotEmpty(trackIdList)) {
+            //2.2.1 根据声音ID远程调用"专辑服务"查询声音信息
+            TrackInfo trackInfo = albumFeignClient.getTrackInfo(trackIdList.get(0)).getData();
+            //2.2.2 批量保存声音购买记录
+            List<UserPaidTrack> userPaidTrackList = trackIdList
+                    .stream()
+                    .map(trackId -> {
+                        UserPaidTrack userPaidTrack = new UserPaidTrack();
+                        userPaidTrack.setOrderNo(userPaidRecordVo.getOrderNo());
+                        userPaidTrack.setUserId(userPaidRecordVo.getUserId());
+                        userPaidTrack.setAlbumId(trackInfo.getAlbumId());
+                        userPaidTrack.setTrackId(trackId);
+                        return userPaidTrack;
+                    }).collect(Collectors.toList());
+            userPaidTrackService.saveBatch(userPaidTrackList);
+        }
+    }
+}

+ 86 - 0
service/service-user/src/main/java/com/atguigu/tingshu/user/strategy/impl/VIPPaidRecordStrategy.java

@@ -0,0 +1,86 @@
+package com.atguigu.tingshu.user.strategy.impl;
+
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+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.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 java.util.Date;
+
+/**
+ * @author: atguigu
+ * @create: 2025-03-25 09:08
+ */
+@Slf4j
+@Component("1003") //默认Bean对象ID=类名(首首字母小写)
+public class VIPPaidRecordStrategy implements PaidRecordStrategy {
+
+    @Autowired
+    private UserVipServiceMapper userVipServiceMapper;
+
+    @Autowired
+    private UserInfoMapper userInfoMapper;
+
+    @Autowired
+    private VipServiceConfigMapper vipServiceConfigMapper;
+
+    /**
+     * 处理VIP虚拟物品发货逻辑
+     * @param userPaidRecordVo
+     */
+    @Override
+    public void handlerPaidRecord(UserPaidRecordVo userPaidRecordVo) {
+        log.info("处理VIP虚拟物品发货逻辑");
+        //4.处理付款项目类型为:VIP套餐 新增套餐购买记录 更新 会员标识及过期时间
+        //4.1 判断订单编号是否已处理
+        Long count = userVipServiceMapper.selectCount(
+                new LambdaQueryWrapper<UserVipService>()
+                        .eq(UserVipService::getOrderNo, userPaidRecordVo.getOrderNo())
+        );
+        if (count > 0) {
+            return;
+        }
+        //4.2 根据用户ID查询用户信息 判断是否为VIP
+        UserInfo userInfo = userInfoMapper.selectById(userPaidRecordVo.getUserId());
+        Boolean isVIP = false;
+        if (userInfo.getIsVip().intValue() == 1 && userInfo.getVipExpireTime().after(new Date())) {
+            isVIP = true;
+        }
+
+        //4.3 根据套餐ID查询套餐信息 得到套餐服务月数
+        VipServiceConfig vipServiceConfig = vipServiceConfigMapper.selectById(userPaidRecordVo.getItemIdList().get(0));
+        Integer serviceMonth = vipServiceConfig.getServiceMonth();
+
+        //4.4 封装会员购买套餐记录并且保存 计算会员生效时间跟过期时间
+        UserVipService userVipService = new UserVipService();
+        //4.4.1 如果是新开会员,本次会员购买记录生效时间=当前系统时间,过期时间=当前系统时间+套餐服务月数
+        if (!isVIP) {
+            userVipService.setStartTime(new Date());
+            DateTime expireTime = DateUtil.offsetMonth(new Date(), serviceMonth);
+            userVipService.setExpireTime(expireTime);
+        } else {
+            //4.4.2 如果是续费会员,本次会员购买记录生效时间=现有会员过期时间+1天   过期时间:现有会员过期时间+1天+服务月数
+            userVipService.setStartTime(DateUtil.offsetDay(userInfo.getVipExpireTime(), 1));
+            userVipService.setExpireTime(DateUtil.offsetMonth(userVipService.getStartTime(), serviceMonth));
+        }
+        //4.4.3 设置其他属性
+        userVipService.setOrderNo(userPaidRecordVo.getOrderNo());
+        userVipService.setUserId(userPaidRecordVo.getUserId());
+        userVipServiceMapper.insert(userVipService);
+
+        //4.5 更新用户表中会员标识以及过期时间
+        userInfo.setIsVip(1);
+        userInfo.setVipExpireTime(userVipService.getExpireTime());
+        userInfoMapper.updateById(userInfo);
+    }
+}

+ 24 - 0
service/service-user/src/main/java/com/atguigu/tingshu/user/strategy/impl/VIPProPaidRecordStrategy.java

@@ -0,0 +1,24 @@
+package com.atguigu.tingshu.user.strategy.impl;
+
+import com.atguigu.tingshu.user.strategy.PaidRecordStrategy;
+import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author: atguigu
+ * @create: 2025-03-25 09:08
+ */
+@Slf4j
+@Component("1004") //默认Bean对象ID=类名(首首字母小写)
+public class VIPProPaidRecordStrategy implements PaidRecordStrategy {
+
+    /**
+     * 处理VIPPRO虚拟物品发货逻辑
+     * @param userPaidRecordVo
+     */
+    @Override
+    public void handlerPaidRecord(UserPaidRecordVo userPaidRecordVo) {
+        log.info("处理VIP-PRO虚拟物品发货逻辑");
+    }
+}