第14天 策略和工厂模式

This commit is contained in:
Y1NanPing 2025-08-04 17:13:18 +08:00
parent e287bcea12
commit 314fffde91
53 changed files with 1061 additions and 183 deletions

View File

@ -4,9 +4,12 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.Positive;
import lombok.Data; import lombok.Data;
import org.springframework.stereotype.Component;
@Data @Data
@Schema(description = "订单确认对象") @Schema(description = "订单确认对象")
@Component
public class TradeVo { public class TradeVo {
@NotEmpty(message = "付款项目类型不能为空") @NotEmpty(message = "付款项目类型不能为空")

View File

@ -1,7 +1,11 @@
package com.atguigu.tingshu.account; package com.atguigu.tingshu.account;
import com.atguigu.tingshu.account.impl.AccountDegradeFeignClient; import com.atguigu.tingshu.account.impl.AccountDegradeFeignClient;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/** /**
* <p> * <p>
@ -10,7 +14,14 @@ import org.springframework.cloud.openfeign.FeignClient;
* *
* @author atguigu * @author atguigu
*/ */
@FeignClient(value = "service-account", fallback = AccountDegradeFeignClient.class) @FeignClient(value = "service-account", path = "api/account", fallback = AccountDegradeFeignClient.class)
public interface AccountFeignClient { public interface AccountFeignClient {
/**
* 检查扣减账户余额
* @param accountDeductVo
* @return
*/
@PostMapping("/userAccount/checkAndDeduct")
public Result checkAndDeduct(@RequestBody AccountDeductVo accountDeductVo);
} }

View File

@ -2,9 +2,18 @@ package com.atguigu.tingshu.account.impl;
import com.atguigu.tingshu.account.AccountFeignClient; import com.atguigu.tingshu.account.AccountFeignClient;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component @Component
@Slf4j
public class AccountDegradeFeignClient implements AccountFeignClient { public class AccountDegradeFeignClient implements AccountFeignClient {
@Override
public Result checkAndDeduct(AccountDeductVo accountDeductVo) {
log.error("[账户服务]checkAndDeduct服务调用失败");
return null;
}
} }

View File

@ -51,6 +51,8 @@ public interface AlbumFeignClient {
@GetMapping("/trackInfo/findPaidTrackInfoList/{trackId}/{trackCount}") @GetMapping("/trackInfo/findPaidTrackInfoList/{trackId}/{trackCount}")
public Result<List<TrackInfo>> findWaitBuyTrackList(@PathVariable Long trackId, @PathVariable Integer trackCount); public Result<List<TrackInfo>> findWaitBuyTrackList(@PathVariable Long trackId, @PathVariable Integer trackCount);
@GetMapping("/trackInfo/getTrackInfo/{id}")
public Result<TrackInfo> getTrackInfo(@PathVariable Long id);
} }

View File

@ -52,4 +52,10 @@ public class AlbumDegradeFeignClient implements AlbumFeignClient {
return null; return null;
} }
@Override
public Result<TrackInfo> getTrackInfo(Long id) {
log.error("[专辑服务]提供远程调用方法getTrackInfo执行服务降级");
return null;
}
} }

View File

@ -4,6 +4,7 @@ import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.user.VipServiceConfig; import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.user.client.impl.UserDegradeFeignClient; import com.atguigu.tingshu.user.client.impl.UserDegradeFeignClient;
import com.atguigu.tingshu.vo.user.UserInfoVo; import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
@ -46,5 +47,7 @@ public interface UserFeignClient {
@GetMapping("/userInfo/findUserPaidTrackList/{albumId}") @GetMapping("/userInfo/findUserPaidTrackList/{albumId}")
public Result<List<Long>> findUserPaidTrackList(@PathVariable Long albumId); public Result<List<Long>> findUserPaidTrackList(@PathVariable Long albumId);
@PostMapping("/userInfo/savePaidRecord")
public Result savePaidRecord(@RequestBody UserPaidRecordVo userPaidRecordVo);
} }

View File

@ -5,6 +5,7 @@ import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.user.VipServiceConfig; import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.user.client.UserFeignClient; import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.user.UserInfoVo; import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -44,4 +45,10 @@ public class UserDegradeFeignClient implements UserFeignClient {
log.error("[用户服务]提供远程调用方法findUserPaidTrackList执行服务降级"); log.error("[用户服务]提供远程调用方法findUserPaidTrackList执行服务降级");
return null; return null;
} }
@Override
public Result savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
log.error("[用户服务]提供远程调用方法savePaidRecord执行服务降级");
return null;
}
} }

View File

@ -4,12 +4,11 @@ import com.atguigu.tingshu.account.service.UserAccountService;
import com.atguigu.tingshu.common.login.GuiGuLogin; import com.atguigu.tingshu.common.login.GuiGuLogin;
import com.atguigu.tingshu.common.result.Result; import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder; import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -30,6 +29,20 @@ public class UserAccountApiController {
return Result.ok(Amount); return Result.ok(Amount);
} }
/**
* 检查扣减账户余额
* @param accountDeductVo
* @return
*/
@GuiGuLogin
@Operation(summary = "检查扣减账户余额")
@PostMapping("/userAccount/checkAndDeduct")
public Result checkAndDeduct(@RequestBody AccountDeductVo accountDeductVo){
userAccountService.checkAndDeduct(accountDeductVo);
return Result.ok();
}
} }

View File

@ -4,7 +4,10 @@ import com.atguigu.tingshu.model.account.UserAccount;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.math.BigDecimal;
@Mapper @Mapper
public interface UserAccountMapper extends BaseMapper<UserAccount> { public interface UserAccountMapper extends BaseMapper<UserAccount> {
UserAccount checkDeduction(Long userId, BigDecimal amount);
} }

View File

@ -1,6 +1,7 @@
package com.atguigu.tingshu.account.service; package com.atguigu.tingshu.account.service;
import com.atguigu.tingshu.model.account.UserAccount; import com.atguigu.tingshu.model.account.UserAccount;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -24,4 +25,7 @@ public interface UserAccountService extends IService<UserAccount> {
void saveUserAccountDetail(Long userId, String title, BigDecimal amount, String orderNo); void saveUserAccountDetail(Long userId, String title, BigDecimal amount, String orderNo);
BigDecimal getAvailableAmount(Long userId); BigDecimal getAvailableAmount(Long userId);
void checkAndDeduct(AccountDeductVo accountDeductVo);
} }

View File

@ -6,7 +6,9 @@ import com.atguigu.tingshu.account.service.UserAccountService;
import com.atguigu.tingshu.common.constant.SystemConstant; import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.model.account.UserAccount; import com.atguigu.tingshu.model.account.UserAccount;
import com.atguigu.tingshu.model.account.UserAccountDetail; import com.atguigu.tingshu.model.account.UserAccountDetail;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -68,5 +70,33 @@ public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserA
BigDecimal availableAmount = userAccount.getAvailableAmount(); BigDecimal availableAmount = userAccount.getAvailableAmount();
return availableAmount; return availableAmount;
}
@Override
public void checkAndDeduct(AccountDeductVo accountDeductVo) {
//1.检查余额是否满足扣减条件采用数据库"锁机制"悲观锁解决并发问题
//使用for update 查看有没有索引 有索引了就是行锁
//可以保证不重复扣导致扣超过账户余额
UserAccount userAccount = userAccountMapper.checkDeduction(accountDeductVo.getUserId(), accountDeductVo.getAmount());
//如果为空说明账户余额不足
if(userAccount==null){
throw new RuntimeException("账户余额不足!");
}
//2.如果满足扣减条件扣减账户余额
int rows = userAccountMapper.update(null,
new LambdaUpdateWrapper<UserAccount>()
.eq(UserAccount::getUserId, accountDeductVo.getUserId())
.setSql("total_amount = total_amount - " + accountDeductVo.getAmount())
.setSql("available_amount = available_amount - " + accountDeductVo.getAmount())
.setSql("total_pay_amount = total_pay_amount + " + accountDeductVo.getAmount())
);
if (rows == 0) {
throw new RuntimeException("账户余额不足!");
}
//3.保存账户变动日志
this.saveUserAccountDetail(accountDeductVo.getUserId(), accountDeductVo.getContent(), accountDeductVo.getAmount(), accountDeductVo.getOrderNo());
} }
} }

View File

@ -12,5 +12,10 @@
id,user_id,total_amount,lock_amount,available_amount,total_income_amount,total_pay_amount,create_time,update_time,is_deleted id,user_id,total_amount,lock_amount,available_amount,total_income_amount,total_pay_amount,create_time,update_time,is_deleted
</sql> </sql>
<!--采用数据库悲观锁避免账户金额超扣-->
<select id="checkDeduction" resultType="com.atguigu.tingshu.model.account.UserAccount">
SELECT * from user_account where user_id = #{userId} and available_amount >= #{amount} and is_deleted =0 for update
</select>
</mapper> </mapper>

View File

@ -12,5 +12,10 @@
id,user_id,total_amount,lock_amount,available_amount,total_income_amount,total_pay_amount,create_time,update_time,is_deleted id,user_id,total_amount,lock_amount,available_amount,total_income_amount,total_pay_amount,create_time,update_time,is_deleted
</sql> </sql>
<!--采用数据库悲观锁避免账户金额超扣-->
<select id="checkDeduction" resultType="com.atguigu.tingshu.model.account.UserAccount">
SELECT * from user_account where user_id = #{userId} and available_amount >= #{amount} and is_deleted =0 for update
</select>
</mapper> </mapper>

View File

@ -41,6 +41,7 @@ public class ReviewTask {
RLock lock = redissonClient.getLock("reviewtask:lock"); RLock lock = redissonClient.getLock("reviewtask:lock");
boolean b = lock.tryLock(); boolean b = lock.tryLock();
if (b) { if (b) {
try {
log.info("开始检查审核任务"); log.info("开始检查审核任务");
//1.查询审核状态为"审核中"声音列表 //1.查询审核状态为"审核中"声音列表
List<TrackInfo> trackInfoList = trackInfoMapper.selectList( List<TrackInfo> trackInfoList = trackInfoMapper.selectList(
@ -66,6 +67,10 @@ public class ReviewTask {
} }
} }
} }
} finally {
lock.unlock();
}
} }
} }

View File

@ -28,6 +28,22 @@
<artifactId>service-account-client</artifactId> <artifactId>service-account-client</artifactId>
<version>1.0</version> <version>1.0</version>
</dependency> </dependency>
<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> </dependencies>
<build> <build>

View File

@ -2,6 +2,7 @@ package com.atguigu.tingshu.order.api;
import com.atguigu.tingshu.common.login.GuiGuLogin; import com.atguigu.tingshu.common.login.GuiGuLogin;
import com.atguigu.tingshu.common.result.Result; import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.order.service.OrderInfoService; import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.vo.order.OrderInfoVo; import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo; import com.atguigu.tingshu.vo.order.TradeVo;
@ -13,6 +14,8 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@Tag(name = "订单管理") @Tag(name = "订单管理")
@RestController @RestController
@RequestMapping("api/order") @RequestMapping("api/order")
@ -31,6 +34,20 @@ public class OrderInfoApiController {
return Result.ok(orderInfoVo); return Result.ok(orderInfoVo);
} }
/**
* 提交订单付款方式1.余额 2.微信
* @param orderInfoVo
* @return
*/
@GuiGuLogin
@Operation(summary = "提交订单付款方式1.余额 2.微信)")
@PostMapping("/orderInfo/submitOrder")
public Result<Map<String, String>> submitOrder(@RequestBody OrderInfoVo orderInfoVo) {
Long userId = AuthContextHolder.getUserId();
Map<String, String> result = orderInfoService.submitOrder(orderInfoVo, userId);
return Result.ok(result);
}
} }

View File

@ -0,0 +1,10 @@
package com.atguigu.tingshu.order.pattern;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
public interface TradeTypeStrategy {
OrderInfoVo trade(TradeVo tradeVo);
}

View File

@ -0,0 +1,28 @@
package com.atguigu.tingshu.order.pattern.factory;
import com.atguigu.tingshu.order.pattern.TradeTypeStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
@Slf4j
public class TradeTypeStrategyFactory {
@Autowired
private Map<String,TradeTypeStrategy> tradeTypeStrategyMap;
public TradeTypeStrategy getTradeTypeStrategy(String itemType)
{
log.info("itemType:{}",itemType);
if (tradeTypeStrategyMap.containsKey(itemType)) {
return tradeTypeStrategyMap.get(itemType);
}
log.error("当前商品类型不支持处理!");
throw new RuntimeException("当前商品类型不支持处理!");
}
}

View File

@ -0,0 +1,150 @@
package com.atguigu.tingshu.order.pattern.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
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.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.pattern.TradeTypeStrategy;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Component(SystemConstant.ORDER_ITEM_TYPE_ALBUM)
@Slf4j
public class AlbumTradeType implements TradeTypeStrategy {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private UserFeignClient userFeignClient;
@Autowired
private AlbumFeignClient albumFeignClient;
@Override
public OrderInfoVo trade(TradeVo tradeVo) {
//从线程中取出userid来
Long userId = AuthContextHolder.getUserId();
//1.创建订单VO对象创建初始三个金额两个集合商品优惠
OrderInfoVo orderInfoVo = new OrderInfoVo();
//1.1 初始化原价金额
// 必须是BigDecimal类型且必须是字符串
BigDecimal originalAmount = new BigDecimal("0.00");
//1.2 初始化减免金额
BigDecimal derateAmount = new BigDecimal("0.00");
//1.3 初始化订单金额
BigDecimal orderAmount = new BigDecimal("0.00");
//1.4.初始化订单明细
List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
//1.5.初始化订单减免明细列表
List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
//1.6 获取购买项目类型 付款项目类型: 1001-专辑 1002-声音 1003-vip会员
String itemType = tradeVo.getItemType();
//3. 处理购买项目类型专辑
//3.1 远程调用用户服务判断是否重复购买专辑如果已购过则业务终止
Long albumId = tradeVo.getItemId();
Boolean data = userFeignClient.isPaidAlbum(albumId).getData();
//如果data为true的话就走断言
Assert.isFalse(data, "用户已购买专辑{}", albumId);
//3.2 远程调用专辑服务获取专辑价格以及折扣普通用户折扣VIP会员折扣
AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
BigDecimal price = albumInfo.getPrice();
BigDecimal discount = albumInfo.getDiscount();
BigDecimal vipDiscount = albumInfo.getVipDiscount();
//判断用户是否为vip 如果是vip就走vip折扣 如果不是就走普通用户的折扣
Boolean isVIP = false;
UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
Assert.notNull(userInfoVo, "用户{}不存在", userId);
//判断一下用户是否为vip以及vip的过期时间是否超过了当时的时间
if (userInfoVo.getIsVip().intValue() == 1
&& userInfoVo.getVipExpireTime().after(new Date())) {
isVIP = true;
}
//3.4 计算相关价格
//3.4.1 暂时将订单价=原价
originalAmount = price;
orderAmount = originalAmount;
//当你是普通客户 但是专辑是有普通用户折扣的时候 计算一下专辑价格
//discount.doubleValue()!=-1
//BigDecimal.doubleValue() 是将 BigDecimal 转换为 double 类型的操作
//如果是普通用户且有折扣,则订单价=原价 100 *折扣 6
//..divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP)是保留两位小数的标准写法
if (!isVIP && discount.doubleValue() != -1) {
orderAmount = originalAmount.multiply(discount)
.divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
}
//如果是会员,则按照会员折扣走
if (isVIP && vipDiscount.doubleValue() != -1) {
orderAmount = originalAmount.multiply(vipDiscount)
.divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
}
//3.5 封装商品信息列表
OrderDetailVo orderDetailVo = new OrderDetailVo();
orderDetailVo.setItemId(albumId);
orderDetailVo.setItemName("专辑:" + albumInfo.getAlbumTitle());
orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
orderDetailVo.setItemPrice(originalAmount);
orderDetailVoList.add(orderDetailVo);
//2.4 如果存在优惠封装优惠列表
if (originalAmount.compareTo(orderAmount) == 1) {
OrderDerateVo orderDerateVo = new OrderDerateVo();
orderDerateVo.setRemarks(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
orderDerateVo.setDerateType("VIP套餐限时优惠:" + derateAmount);
orderDerateVo.setDerateAmount(originalAmount.subtract(orderAmount));
orderDerateVoList.add(orderDerateVo);
}
//5.封装订单VO对象
//5.1 封装相关价格信息
orderInfoVo.setOriginalAmount(originalAmount);
orderInfoVo.setOrderAmount(orderAmount);
orderInfoVo.setDerateAmount(derateAmount);
//5.2 封装商品相关集合
orderInfoVo.setOrderDetailVoList(orderDetailVoList);
orderInfoVo.setOrderDerateVoList(orderDerateVoList);
//5.3 TODO 封装其他信息流水号时间戳及签名项目类型等
//5.3.1 封装项目的类型
orderInfoVo.setItemType(itemType);
//5.3.2 流水号机制防止订单重复提交1.用户网络卡连续点击结算按钮2.成功提交订单回退到订单确认页再次提交
//5.3.2.1 构建当前用户当前订单流水号Key
String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
//5.3.2.2 采用UUID生成本次订单流水号值
String tradeNo = IdUtil.randomUUID();
//5.3.2.3 将流水号值保存到Redis中设置过期时间5分钟防止订单重复提交
redisTemplate.opsForValue().set(tradeKey, tradeNo, 5, TimeUnit.MINUTES);
orderInfoVo.setTradeNo(tradeNo);
//5.3.3 签名机制时间戳+签名值防止订单结算页数据被篡改
orderInfoVo.setTimestamp(System.currentTimeMillis());
//5.3.1 将订单VO转为Map对象由于无法确定支付方式固将VO中payWay为空属性排除掉
Map<String, Object> map = BeanUtil.beanToMap(orderInfoVo, false, true);
String sign = SignHelper.getSign(map);
orderInfoVo.setSign(sign);
//6.响应订单VO对象
return orderInfoVo;
}
}

View File

@ -0,0 +1,116 @@
package com.atguigu.tingshu.order.pattern.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
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.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.pattern.TradeTypeStrategy;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Component(SystemConstant.ORDER_ITEM_TYPE_TRACK)
@Slf4j
public class TrackTradeType implements TradeTypeStrategy {
@Autowired
private AlbumFeignClient albumFeignClient;
@Autowired
private RedisTemplate redisTemplate;
@Override
public OrderInfoVo trade(TradeVo tradeVo) {
//从线程中取出userid来
Long userId = AuthContextHolder.getUserId();
//1.创建订单VO对象创建初始三个金额两个集合商品优惠
OrderInfoVo orderInfoVo = new OrderInfoVo();
//1.1 初始化原价金额
// 必须是BigDecimal类型且必须是字符串
BigDecimal originalAmount = new BigDecimal("0.00");
//1.2 初始化减免金额
BigDecimal derateAmount = new BigDecimal("0.00");
//1.3 初始化订单金额
BigDecimal orderAmount = new BigDecimal("0.00");
//1.4.初始化订单明细
List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
//1.5.初始化订单减免明细列表
List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
//1.6 获取购买项目类型 付款项目类型: 1001-专辑 1002-声音 1003-vip会员
String itemType = tradeVo.getItemType();
//4.处理购买项目类型声音
//4.1 远程调用专辑服务获取未购买声音列表
Long trackId = tradeVo.getItemId();
List<TrackInfo> trackInfoList =
albumFeignClient.findWaitBuyTrackList(trackId, tradeVo.getTrackCount()).getData();
Assert.notNull(trackInfoList, "不存在待结算声音", trackId);
//4.2 远程调用专辑服务获取专辑价格声音单价声音不支持折扣
AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(trackInfoList.get(0).getAlbumId()).getData();
Assert.notNull(albumInfo, "专辑{}不存在", albumInfo.getId());
BigDecimal price = albumInfo.getPrice();
//4.3 计算相关价格
originalAmount = price.multiply(new BigDecimal(trackInfoList.size()));
orderAmount = originalAmount;
//4.4 封装订单明细列表
orderDetailVoList = trackInfoList.stream().map(
trackInfo -> {
OrderDetailVo orderDetailVo = new OrderDetailVo();
orderDetailVo.setItemId(trackInfo.getId());
orderDetailVo.setItemName("声音:" + trackInfo.getTrackTitle());
orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
orderDetailVo.setItemPrice(price);
return orderDetailVo;
}
).collect(Collectors.toList());
//5.封装订单VO对象
//5.1 封装相关价格信息
orderInfoVo.setOriginalAmount(originalAmount);
orderInfoVo.setOrderAmount(orderAmount);
orderInfoVo.setDerateAmount(derateAmount);
//5.2 封装商品相关集合
orderInfoVo.setOrderDetailVoList(orderDetailVoList);
orderInfoVo.setOrderDerateVoList(orderDerateVoList);
//5.3 TODO 封装其他信息流水号时间戳及签名项目类型等
//5.3.1 封装项目的类型
orderInfoVo.setItemType(itemType);
//5.3.2 流水号机制防止订单重复提交1.用户网络卡连续点击结算按钮2.成功提交订单回退到订单确认页再次提交
//5.3.2.1 构建当前用户当前订单流水号Key
String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
//5.3.2.2 采用UUID生成本次订单流水号值
String tradeNo = IdUtil.randomUUID();
//5.3.2.3 将流水号值保存到Redis中设置过期时间5分钟防止订单重复提交
redisTemplate.opsForValue().set(tradeKey, tradeNo, 5, TimeUnit.MINUTES);
orderInfoVo.setTradeNo(tradeNo);
//5.3.3 签名机制时间戳+签名值防止订单结算页数据被篡改
orderInfoVo.setTimestamp(System.currentTimeMillis());
//5.3.1 将订单VO转为Map对象由于无法确定支付方式固将VO中payWay为空属性排除掉
Map<String, Object> map = BeanUtil.beanToMap(orderInfoVo, false, true);
String sign = SignHelper.getSign(map);
orderInfoVo.setSign(sign);
return orderInfoVo;
}
}

View File

@ -0,0 +1,119 @@
package com.atguigu.tingshu.order.pattern.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.pattern.TradeTypeStrategy;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Component(SystemConstant.ORDER_ITEM_TYPE_VIP)
@Slf4j
public class VIPTradeType implements TradeTypeStrategy {
@Autowired
private UserFeignClient userFeignClient;
@Autowired
private RedisTemplate redisTemplate;
@Override
public OrderInfoVo trade(TradeVo tradeVo) {
//从线程中取出userid来
Long userId = AuthContextHolder.getUserId();
//1.创建订单VO对象创建初始三个金额两个集合商品优惠
OrderInfoVo orderInfoVo = new OrderInfoVo();
//1.1 初始化原价金额
// 必须是BigDecimal类型且必须是字符串
BigDecimal originalAmount = new BigDecimal("0.00");
//1.2 初始化减免金额
BigDecimal derateAmount = new BigDecimal("0.00");
//1.3 初始化订单金额
BigDecimal orderAmount = new BigDecimal("0.00");
//1.4.初始化订单明细
List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
//1.5.初始化订单减免明细列表
List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
//1.6 获取购买项目类型 付款项目类型: 1001-专辑 1002-声音 1003-vip会员
String itemType = tradeVo.getItemType();
//2.1 远程调用用户服务得到VIP套餐信息
VipServiceConfig vipServiceConfig = userFeignClient.getVipServiceConfig(tradeVo.getItemId()).getData();
Assert.notNull(vipServiceConfig, "VIP套餐{}不存在", tradeVo.getItemId());
//2.2 计算原金额订单金额减免金额
originalAmount = vipServiceConfig.getPrice();
orderAmount = vipServiceConfig.getDiscountPrice();
//2.3 封装商品信息列表
OrderDetailVo orderDetailVo = new OrderDetailVo();
//套餐的ID
orderDetailVo.setItemId(tradeVo.getItemId());
//套餐的名字
orderDetailVo.setItemName("VIP套餐" + vipServiceConfig.getName());
//套餐图片的url
orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
//套餐的折后价
orderDetailVo.setItemPrice(orderAmount);
orderDetailVoList.add(orderDetailVo);
//2.4 如果存在优惠封装优惠列表
//bigdecimal是不能用equals判断的 因为1.10和1.1不相等
//compareTo(orderAmount)==0是相等
//compareTo(orderAmount)==-1小于
//compareTo(orderAmount)==1是大于 如果大于的话说明有了折扣
if (originalAmount.compareTo(orderAmount) == 1) {
derateAmount = originalAmount.subtract(orderAmount);
//存在优惠 我们要封装优惠列表
OrderDerateVo orderDerateVo = new OrderDerateVo();
orderDerateVo.setDerateAmount(derateAmount);
orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
orderDerateVo.setRemarks("VIP套餐限时优惠:" + derateAmount);
orderDerateVoList.add(orderDerateVo);
}
//5.封装订单VO对象
//5.1 封装相关价格信息
orderInfoVo.setOriginalAmount(originalAmount);
orderInfoVo.setOrderAmount(orderAmount);
orderInfoVo.setDerateAmount(derateAmount);
//5.2 封装商品相关集合
orderInfoVo.setOrderDetailVoList(orderDetailVoList);
orderInfoVo.setOrderDerateVoList(orderDerateVoList);
//5.3 TODO 封装其他信息流水号时间戳及签名项目类型等
//5.3.1 封装项目的类型
orderInfoVo.setItemType(itemType);
//5.3.2 流水号机制防止订单重复提交1.用户网络卡连续点击结算按钮2.成功提交订单回退到订单确认页再次提交
//5.3.2.1 构建当前用户当前订单流水号Key
String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
//5.3.2.2 采用UUID生成本次订单流水号值
String tradeNo = IdUtil.randomUUID();
//5.3.2.3 将流水号值保存到Redis中设置过期时间5分钟防止订单重复提交
redisTemplate.opsForValue().set(tradeKey, tradeNo, 5, TimeUnit.MINUTES);
orderInfoVo.setTradeNo(tradeNo);
//5.3.3 签名机制时间戳+签名值防止订单结算页数据被篡改
orderInfoVo.setTimestamp(System.currentTimeMillis());
//5.3.1 将订单VO转为Map对象由于无法确定支付方式固将VO中payWay为空属性排除掉
Map<String, Object> map = BeanUtil.beanToMap(orderInfoVo, false, true);
String sign = SignHelper.getSign(map);
orderInfoVo.setSign(sign);
//6.响应订单VO对象
return orderInfoVo;
}
}

View File

@ -0,0 +1,7 @@
package com.atguigu.tingshu.order.service;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.baomidou.mybatisplus.extension.service.IService;
public interface OrderDerateService extends IService<OrderDerate> {
}

View File

@ -0,0 +1,7 @@
package com.atguigu.tingshu.order.service;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.baomidou.mybatisplus.extension.service.IService;
public interface OrderDetailService extends IService<OrderDetail> {
}

View File

@ -5,8 +5,14 @@ import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo; import com.atguigu.tingshu.vo.order.TradeVo;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import java.util.Map;
public interface OrderInfoService extends IService<OrderInfo> { public interface OrderInfoService extends IService<OrderInfo> {
OrderInfoVo trade(TradeVo tradeVo); OrderInfoVo trade(TradeVo tradeVo);
Map<String, String> submitOrder(OrderInfoVo orderInfoVo, Long userId);
OrderInfo saveOrderInfo(OrderInfoVo orderInfoVo, Long userId);
} }

View File

@ -0,0 +1,17 @@
package com.atguigu.tingshu.order.service.impl;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.service.OrderDerateService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @author: atguigu
* @create: 2025-08-02 11:35
*/
@Slf4j
@Service
public class OrderDerateServiceImpl extends ServiceImpl<OrderDerateMapper, OrderDerate> implements OrderDerateService {
}

View File

@ -0,0 +1,13 @@
package com.atguigu.tingshu.order.service.impl;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.service.OrderDetailService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class OrderDetailServiceImpl extends ServiceImpl<OrderDetailMapper, OrderDetail> implements OrderDetailService {
}

View File

@ -1,34 +1,51 @@
package com.atguigu.tingshu.order.service.impl; package com.atguigu.tingshu.order.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.account.AccountFeignClient;
import com.atguigu.tingshu.album.AlbumFeignClient; 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.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result; import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder; import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumInfo; import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo; import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo; import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig; import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper; import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.pattern.TradeTypeStrategy;
import com.atguigu.tingshu.order.pattern.factory.TradeTypeStrategyFactory;
import com.atguigu.tingshu.order.service.OrderDerateService;
import com.atguigu.tingshu.order.service.OrderDetailService;
import com.atguigu.tingshu.order.service.OrderInfoService; import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserFeignClient; import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo; import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo; import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo; import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo; import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo; import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.aspectj.weaver.ast.Or;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.util.ArrayList; import java.util.*;
import java.util.Date; import java.util.concurrent.TimeUnit;
import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Slf4j @Slf4j
@ -40,163 +57,162 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
private OrderInfoMapper orderInfoMapper; private OrderInfoMapper orderInfoMapper;
@Autowired @Autowired
private UserFeignClient userFeignClient; private UserFeignClient userFeignClient;
@Qualifier("com.atguigu.tingshu.album.AlbumFeignClient")
@Autowired @Autowired
private AlbumFeignClient albumFeignClient; private AlbumFeignClient albumFeignClient;
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private OrderDetailService orderDetailService;
@Autowired
private OrderDerateService orderDerateService;
@Autowired
private AccountFeignClient accountFeignClient;
@Autowired
private TradeTypeStrategyFactory tradeTypeStrategyFactory;
@Override @Override
public OrderInfoVo trade(TradeVo tradeVo) { public OrderInfoVo trade(TradeVo tradeVo) {
//从线程中取出userid来
Long userId = AuthContextHolder.getUserId();
//1.创建订单VO对象创建初始三个金额两个集合商品优惠
OrderInfoVo orderInfoVo = new OrderInfoVo();
//1.1 初始化原价金额
// 必须是BigDecimal类型且必须是字符串
BigDecimal originalAmount = new BigDecimal("0.00");
//1.2 初始化减免金额
BigDecimal derateAmount = new BigDecimal("0.00");
//1.3 初始化订单金额
BigDecimal orderAmount = new BigDecimal("0.00");
//1.4.初始化订单明细
List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
//1.5.初始化订单减免明细列表
List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
//todo 流水号 签名之类的还未添加
//1.6 获取购买项目类型 付款项目类型: 1001-专辑 1002-声音 1003-vip会员
String itemType = tradeVo.getItemType(); String itemType = tradeVo.getItemType();
//2. 处理购买项目类型VIP套餐 //从策略工厂获取具体实现类对象
if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)) { TradeTypeStrategy tradeTypeStrategy = tradeTypeStrategyFactory.getTradeTypeStrategy(itemType);
//2.1 远程调用用户服务得到VIP套餐信息 OrderInfoVo trade = tradeTypeStrategy.trade(tradeVo);
VipServiceConfig vipServiceConfig = userFeignClient.getVipServiceConfig(tradeVo.getItemId()).getData(); //返回订单信息
Assert.notNull(vipServiceConfig, "VIP套餐{}不存在", tradeVo.getItemId()); return trade;
//2.2 计算原金额订单金额减免金额
originalAmount = vipServiceConfig.getPrice();
orderAmount = vipServiceConfig.getDiscountPrice();
//2.3 封装商品信息列表
OrderDetailVo orderDetailVo = new OrderDetailVo();
//套餐的ID
orderDetailVo.setItemId(tradeVo.getItemId());
//套餐的名字
orderDetailVo.setItemName("VIP套餐" + vipServiceConfig.getName());
//套餐图片的url
orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
//套餐的折后价
orderDetailVo.setItemPrice(orderAmount);
orderDetailVoList.add(orderDetailVo);
//2.4 如果存在优惠封装优惠列表
//bigdecimal是不能用equals判断的 因为1.10和1.1不相等
//compareTo(orderAmount)==0是相等
//compareTo(orderAmount)==-1小于
//compareTo(orderAmount)==1是大于 如果大于的话说明有了折扣
if (originalAmount.compareTo(orderAmount) == 1) {
derateAmount = originalAmount.subtract(orderAmount);
//存在优惠 我们要封装优惠列表
OrderDerateVo orderDerateVo = new OrderDerateVo();
orderDerateVo.setDerateAmount(derateAmount);
orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
orderDerateVo.setRemarks("VIP套餐限时优惠:" + derateAmount);
orderDerateVoList.add(orderDerateVo);
} }
} else if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)) { @Override
//3. 处理购买项目类型专辑 @GlobalTransactional(rollbackFor = Exception.class)
//3.1 远程调用用户服务判断是否重复购买专辑如果已购过则业务终止 public Map<String, String> submitOrder(OrderInfoVo orderInfoVo, Long userId) {
Long albumId = tradeVo.getItemId(); //1.业务校验验证流水号防止订单重复提交 采用Lua脚本保证判断删除原子性
Boolean data = userFeignClient.isPaidAlbum(albumId).getData(); String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
//如果data为true的话就走断言 //1.1 定义lua脚本
Assert.isFalse(data, "用户已购买专辑{}", albumId); String luaScript = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
//3.2 远程调用专辑服务获取专辑价格以及折扣普通用户折扣VIP会员折扣 "then\n" +
AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData(); " return redis.call(\"del\",KEYS[1])\n" +
BigDecimal price = albumInfo.getPrice(); "else\n" +
BigDecimal discount = albumInfo.getDiscount(); " return 0\n" +
BigDecimal vipDiscount = albumInfo.getVipDiscount(); "end";
//判断用户是否为vip 如果是vip就走vip折扣 如果不是就走普通用户的折扣 //1.2 创建脚本对象
Boolean isVIP = false; DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData(); redisScript.setScriptText(luaScript);
Assert.notNull(userInfoVo, "用户{}不存在", userId); redisScript.setResultType(Boolean.class);
//判断一下用户是否为vip以及vip的过期时间是否超过了当时的时间 //1.3 执行Lua脚本
if (userInfoVo.getIsVip().intValue() == 1 Boolean flag = (Boolean) redisTemplate.execute(redisScript, Arrays.asList(tradeKey), orderInfoVo.getTradeNo());
&& userInfoVo.getVipExpireTime().after(new Date())) { if (!flag) {
isVIP = true; throw new GuiguException(500, "验证流水号失败");
} }
//3.4 计算相关价格 //2.业务校验`验证签名`防止订单数据被篡改
//3.4.1 暂时将订单价=原价 //2.1 将订单VO转为Map对象 由于当时加签将payWay排除掉并未加入到签名中
originalAmount = price; //为何在初始化初始数值时候要用bigdecimal数值中要写String类型,如果数值写了int类型的话
orderAmount = originalAmount; //会导致数值不一样 比如说0.00会变成0 导致md5编码过程中编码错误 结果导致验签失败
//当你是普通客户 但是专辑是有普通用户折扣的时候 计算一下专辑价格 Map<String, Object> stringObjectMap = BeanUtil.beanToMap(orderInfoVo);
//discount.doubleValue()!=-1 stringObjectMap.remove("payWay");
//BigDecimal.doubleValue() 是将 BigDecimal 转换为 double 类型的操作 //2.2 调用验证签名工具方法
SignHelper.checkSign(stringObjectMap);
//如果是普通用户且有折扣,则订单价=原价 100 *折扣 6 //当校验流水号和验证签名成功以后 执行核心业务
//..divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP)是保留两位小数的标准写法 //3.核心业务保存订单以及订单明细优惠列表等 -- 此时订单未支付
if (!isVIP && discount.doubleValue() != -1) { OrderInfo orderInfo = this.saveOrderInfo(orderInfoVo, userId);
orderAmount =originalAmount.multiply(discount) //支付方式1101-微信 1102-支付宝 1103-账户余额
.divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP); String payWay = orderInfoVo.getPayWay();
} //4.TODO 判断支付方式余额扣减核心业务
//如果是会员,则按照会员折扣走 if(SystemConstant.ORDER_PAY_ACCOUNT.equals(payWay)){
if (isVIP && vipDiscount.doubleValue() != -1) { //余额扣减
orderAmount = originalAmount.multiply(vipDiscount) //4.1 远程调用账户服务扣减账户余额
.divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP); AccountDeductVo accountDeductVo = new AccountDeductVo();
} accountDeductVo.setAmount(orderInfo.getOrderAmount());
//3.5 封装商品信息列表 accountDeductVo.setOrderNo(orderInfo.getOrderNo());
OrderDetailVo orderDetailVo = new OrderDetailVo(); accountDeductVo.setContent(orderInfo.getOrderTitle());
orderDetailVo.setItemId(albumId); accountDeductVo.setUserId(orderInfo.getUserId());
orderDetailVo.setItemName("专辑:"+albumInfo.getAlbumTitle());
orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
orderDetailVo.setItemPrice(originalAmount);
orderDetailVoList.add(orderDetailVo);
//2.4 如果存在优惠封装优惠列表
if(originalAmount.compareTo(orderAmount)==1){ Result result = accountFeignClient.checkAndDeduct(accountDeductVo);
OrderDerateVo orderDerateVo = new OrderDerateVo(); //判断是否为扣减成功
orderDerateVo.setRemarks(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT); if(result.getCode().intValue()!=200){
orderDerateVo.setDerateType("VIP套餐限时优惠:" + derateAmount); throw new GuiguException(result.getCode(), result.getMessage());
orderDerateVo.setDerateAmount(originalAmount.subtract(orderAmount)); }
orderDerateVoList.add(orderDerateVo); //4.2 如果扣减余额成功修改订单状态改为已支付
orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
orderInfoMapper.updateById(orderInfo);
//4.3 远程调用用户服务虚拟物品发货
//4.3.1 构建虚拟发货VO对象
UserPaidRecordVo userPaidRecordVo =new UserPaidRecordVo();
userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());
userPaidRecordVo.setUserId(orderInfo.getUserId());
userPaidRecordVo.setItemType(orderInfo.getItemType());
//根据订单编号查询订单明细获取购买项目ID列表
List<OrderDetail> orderDetailList = orderDetailService.list(
new LambdaQueryWrapper<OrderDetail>()
.eq(OrderDetail::getOrderId, orderInfo.getId())
.select(OrderDetail::getItemId)
);
List<Long> itemIdList = orderDetailList.stream()
.map(OrderDetail::getItemId).collect(Collectors.toList());
userPaidRecordVo.setItemIdList(itemIdList);
//4.3.2 远程调用用户服务,发放用户权益
result = userFeignClient.savePaidRecord(userPaidRecordVo);
//4.3.3 判断远程调用结果业务状态码是否为200
if (result.getCode().intValue() != 200) {
throw new GuiguException(result.getCode(), result.getMessage());
} }
} else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(itemType)) {
//4.处理购买项目类型声音 }
//4.1 远程调用专辑服务获取未购买声音列表 //5.TODO 判断支付方式微信
Long trackId = tradeVo.getItemId(); //else if(SystemConstant.ORDER_PAY_WAY_WEIXIN.equals(payWay)){
List<TrackInfo> trackInfoList =
albumFeignClient.findWaitBuyTrackList(trackId, tradeVo.getTrackCount()).getData(); //}
Assert.notNull(trackInfoList, "不存在待结算声音",trackId); //6.响应结果
//4.2 远程调用专辑服务获取专辑价格声音单价声音不支持折扣 return Map.of("orderNo", orderInfo.getOrderNo());
AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(trackInfoList.get(0).getAlbumId()).getData();
Assert.notNull(albumInfo, "专辑{}不存在", albumInfo.getId()); }
BigDecimal price = albumInfo.getPrice();
//4.3 计算相关价格 @Override
originalAmount = price.multiply(new BigDecimal(trackInfoList.size())); public OrderInfo saveOrderInfo(OrderInfoVo orderInfoVo, Long userId) {
orderAmount = originalAmount; //1.保存订单
//4.4 封装订单明细列表 //2.1 创建订单对象
orderDetailVoList = trackInfoList.stream().map( OrderInfo orderInfo = BeanUtil.copyProperties(orderInfoVo, OrderInfo.class);
trackInfo -> { //2.2 设置用户ID
OrderDetailVo orderDetailVo = new OrderDetailVo(); orderInfo.setUserId(userId);
orderDetailVo.setItemId(trackInfo.getId()); //2.3 设置订单标题
orderDetailVo.setItemName("声音:" + trackInfo.getTrackTitle()); List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
orderDetailVo.setItemUrl(trackInfo.getCoverUrl()); if (CollUtil.isNotEmpty(orderDetailVoList)) {
orderDetailVo.setItemPrice(price); String itemName = orderDetailVoList.get(0).getItemName();
return orderDetailVo; orderInfo.setOrderTitle(itemName);
}
//2.4 订单状态未支付
orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
//2.5 设置订单编号 确保全局唯一且趋势递增 形式当天日期+雪花算法
String code = DateUtil.today().replaceAll("-", "") + IdUtil.getSnowflakeNextId();
orderInfo.setOrderNo(code);
//2.6 保存订单,获取订单ID
orderInfoMapper.insert(orderInfo);
Long orderId = orderInfo.getId();
if (CollUtil.isNotEmpty(orderDetailVoList)) {
List<OrderDetail> collect = orderDetailVoList.stream().map(
m -> {
OrderDetail orderDetail = BeanUtil.copyProperties(m, OrderDetail.class);
orderDetail.setOrderId(orderId);
return orderDetail;
} }
).collect(Collectors.toList()); ).collect(Collectors.toList());
orderDetailService.saveBatch(collect);
} }
//5.封装订单VO对象 List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
//5.1 封装相关价格信息 if (CollUtil.isNotEmpty(orderDerateVoList)) {
orderInfoVo.setOriginalAmount(originalAmount); List<OrderDerate> collect = orderDerateVoList.stream().map(
orderInfoVo.setOrderAmount(orderAmount); m -> {
orderInfoVo.setDerateAmount(derateAmount); OrderDerate orderDerate = BeanUtil.copyProperties(m, OrderDerate.class);
//5.2 封装商品相关集合 orderDerate.setOrderId(orderId);
orderInfoVo.setOrderDetailVoList(orderDetailVoList); return orderDerate;
orderInfoVo.setOrderDerateVoList(orderDerateVoList);
//5.3 TODO 封装其他信息流水号时间戳及签名项目类型等
//6.响应订单VO对象
return orderInfoVo;
} }
).collect(Collectors.toList());
orderDerateService.saveBatch(collect);
}
return orderInfo;
}
} }

View File

@ -5,6 +5,7 @@ import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder; import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.user.service.UserInfoService; import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.vo.user.UserInfoVo; import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -83,8 +84,18 @@ public class UserInfoApiController {
Long userId = AuthContextHolder.getUserId(); Long userId = AuthContextHolder.getUserId();
List<Long> list = userInfoService.findUserPaidTrackList(userId, albumId); List<Long> list = userInfoService.findUserPaidTrackList(userId, albumId);
return Result.ok(list); return Result.ok(list);
}
/**
* 用户支付成功后权益方法虚拟物品VIP会员专辑声音发货
* 如果支付方式是微信微信支付会异步回调没有token造成接口无法调用故不加@GuiGuLogin注解
* @param userPaidRecordVo
* @return
*/
@Operation(summary = "用户支付成功后权益方法虚拟物品VIP会员、专辑、声音发货")
@PostMapping("/userInfo/savePaidRecord")
public Result savePaidRecord(@RequestBody UserPaidRecordVo userPaidRecordVo){
userInfoService.savePaidRecord(userPaidRecordVo);
return Result.ok();
} }
} }

View File

@ -0,0 +1,12 @@
package com.atguigu.tingshu.user.pattern;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
public interface DeliveryStrategy {
/**
* 虚拟物品"发货"抽象方法
* @param userPaidRecordVo
*/
void delivery(UserPaidRecordVo userPaidRecordVo);
}

View File

@ -0,0 +1,29 @@
package com.atguigu.tingshu.user.pattern.factory;
import com.atguigu.tingshu.user.pattern.DeliveryStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
@Slf4j
public class DeliveryStrategyFactory {
@Autowired
private Map<String, DeliveryStrategy> deliveryStrategyMap;
public DeliveryStrategy getDeliveryStrategy(String itemType){
log.info("itemType:{}", itemType);
if (deliveryStrategyMap.containsKey(itemType)) {
return deliveryStrategyMap.get(itemType);
}
log.error("当前商品类型不支持处理!");
throw new RuntimeException("当前商品类型不支持处理!");
}
}

View File

@ -0,0 +1,39 @@
package com.atguigu.tingshu.user.pattern.impl;
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.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;
@Component(SystemConstant.ORDER_ITEM_TYPE_ALBUM)
@Slf4j
public class AlbumDelivery implements DeliveryStrategy {
@Autowired
private UserPaidAlbumMapper userPaidAlbumMapper;
@Override
public void delivery(UserPaidRecordVo userPaidRecordVo) {
log.info("专辑发货");
//1.1 根据订单编号查询是否存在购买记录如果存在则忽略
Long count = userPaidAlbumMapper.selectCount(
new LambdaQueryWrapper<UserPaidAlbum>()
.eq(UserPaidAlbum::getOrderNo, userPaidRecordVo.getOrderNo())
.select(UserPaidAlbum::getId)
);
if (count > 0){
return;
}
UserPaidAlbum userPaidAlbum =new UserPaidAlbum();
userPaidAlbum.setUserId(userPaidRecordVo.getUserId());
userPaidAlbum.setOrderNo(userPaidRecordVo.getOrderNo());
userPaidAlbum.setAlbumId(userPaidRecordVo.getItemIdList().get(0));
userPaidAlbumMapper.insert(userPaidAlbum);
}
}

View File

@ -0,0 +1,52 @@
package com.atguigu.tingshu.user.pattern.impl;
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;
@Slf4j
@Component(SystemConstant.ORDER_ITEM_TYPE_TRACK)
public class TrackDelivery implements DeliveryStrategy {
@Autowired
private UserPaidTrackMapper userPaidTrackMapper;
@Autowired
private AlbumFeignClient albumFeignClient;
@Override
public void delivery(UserPaidRecordVo userPaidRecordVo) {
log.info("声音发货");
//2. 处理购买项目类型为声音
//2.1 根据订单编号查询是否存在购买记录如果存在则忽略
Long count = userPaidTrackMapper.selectCount(
new LambdaQueryWrapper<UserPaidTrack>()
.eq(UserPaidTrack::getOrderNo, userPaidRecordVo.getOrderNo())
.select(UserPaidTrack::getId)
);
if(count>0){
return;
}
//2.2 新增声音购买记录
//远程调用专辑服务获取声音信息得到所属专辑ID
TrackInfo trackInfo = albumFeignClient.getTrackInfo(userPaidRecordVo.getItemIdList().get(0)).getData();
Long albumId = trackInfo.getAlbumId();
userPaidRecordVo.getItemIdList().forEach(trackId -> {
UserPaidTrack userPaidTrack = new UserPaidTrack();
userPaidTrack.setOrderNo(userPaidRecordVo.getOrderNo());
userPaidTrack.setUserId(userPaidRecordVo.getUserId());
userPaidTrack.setAlbumId(albumId);
userPaidTrack.setTrackId(trackId);
userPaidTrackMapper.insert(userPaidTrack);
});
}
}

View File

@ -0,0 +1,83 @@
package com.atguigu.tingshu.user.pattern.impl;
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.pattern.DeliveryStrategy;
import com.atguigu.tingshu.user.service.impl.UserInfoServiceImpl;
import com.atguigu.tingshu.vo.user.UserInfoVo;
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;
@Slf4j
@Component(SystemConstant.ORDER_ITEM_TYPE_VIP)
public class VIPDelivery implements DeliveryStrategy {
@Autowired
private UserVipServiceMapper userVipServiceMapper;
@Autowired
private VipServiceConfigMapper vipServiceConfigMapper;
@Autowired
private UserInfoMapper userInfoMapper;
@Override
public void delivery(UserPaidRecordVo userPaidRecordVo) {
//3.TODO 处理购买项目类型为VIP会员
//3.1 根据订单编号查询是否存在购买记录如果存在则忽略
Long cout = userVipServiceMapper.selectCount(
new LambdaQueryWrapper<UserVipService>()
.eq(UserVipService::getOrderNo, userPaidRecordVo.getOrderNo())
.select(UserVipService::getId)
);
if (cout > 0) {
return;
}
//3.2 新增会员购买记录
UserVipService userVipService = new UserVipService();
userVipService.setOrderNo(userPaidRecordVo.getOrderNo());
userVipService.setUserId(userPaidRecordVo.getUserId());
//3.2.1 获取当前用户身份判断是否为VIP
Boolean isVIP = false;
UserInfo userInfo = userInfoMapper.selectById(userPaidRecordVo.getUserId());
if (userInfo.getIsVip().intValue() == 1 && userInfo.getVipExpireTime().after(new Date())) {
isVIP = true;
}
//3.2.2 获取用户购买套餐信息得到套餐服务月
Long vipID = userPaidRecordVo.getItemIdList().get(0);
VipServiceConfig vipServiceConfig = vipServiceConfigMapper.selectById(vipID);
Integer serviceMonth = vipServiceConfig.getServiceMonth();
//3.2.3 本次会员生效时间
if (!isVIP) {
// 如果是普通用户生效时间为当前时间
userVipService.setStartTime(new Date());
//3.2.4 本次会员到期时间
userVipService.setExpireTime(DateUtil.offsetMonth(new Date(), serviceMonth));
} else {
// 如果是VIP用户
// 生效时间=现有会员到期时间+1天
userVipService.setStartTime(DateUtil.offsetDay(userInfo.getVipExpireTime(), 1));
userVipService.setExpireTime(DateUtil.offsetMonth(userVipService.getStartTime(), serviceMonth));
}
userVipServiceMapper.insert(userVipService);
//3.3 更新用户表会员标识以及会员到期时间
UserInfo userInfo1 = new UserInfo();
userInfo1.setId(userPaidRecordVo.getUserId());
userInfo1.setIsVip(1);
userInfo1.setVipExpireTime(userVipService.getExpireTime());
userInfoMapper.updateById(userInfo1);
}
}

View File

@ -2,6 +2,7 @@ package com.atguigu.tingshu.user.service;
import com.atguigu.tingshu.model.user.UserInfo; import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.vo.user.UserInfoVo; import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List; import java.util.List;
@ -20,4 +21,7 @@ public interface UserInfoService extends IService<UserInfo> {
Boolean isPaidAlbum(Long userId, Long albumId); Boolean isPaidAlbum(Long userId, Long albumId);
List<Long> findUserPaidTrackList(Long userId, Long albumId); List<Long> findUserPaidTrackList(Long userId, Long albumId);
void savePaidRecord(UserPaidRecordVo userPaidRecordVo);
} }

View File

@ -6,18 +6,20 @@ import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.cache.GuiGuCache; import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.RedisConstant; 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.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService; import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.model.user.UserInfo; import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.user.UserPaidAlbum; import com.atguigu.tingshu.model.user.*;
import com.atguigu.tingshu.model.user.UserPaidTrack; import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.mapper.UserInfoMapper; import com.atguigu.tingshu.user.pattern.DeliveryStrategy;
import com.atguigu.tingshu.user.mapper.UserPaidAlbumMapper; import com.atguigu.tingshu.user.pattern.factory.DeliveryStrategyFactory;
import com.atguigu.tingshu.user.mapper.UserPaidTrackMapper;
import com.atguigu.tingshu.user.service.UserInfoService; import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.vo.user.UserInfoVo; import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -27,6 +29,7 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -50,6 +53,12 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> i
private UserPaidAlbumMapper userPaidAlbumMapper; private UserPaidAlbumMapper userPaidAlbumMapper;
@Autowired @Autowired
private UserPaidTrackMapper userPaidTrackMapper; private UserPaidTrackMapper userPaidTrackMapper;
@Autowired
private VipServiceConfigMapper vipServiceConfigMapper;
@Autowired
private UserVipServiceMapper userVipServiceMapper;
@Autowired
private DeliveryStrategyFactory deliveryStrategyFactory;
@Override @Override
public Map<String, String> wxLogin(String code) { public Map<String, String> wxLogin(String code) {
@ -202,5 +211,16 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> i
} }
return null; return null;
} }
@Override
public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
//项目类型: 1001-专辑 1002-声音 1003-vip会员
String itemType = userPaidRecordVo.getItemType();
//1.从策略工厂获取具体策略实现类对象
DeliveryStrategy strategy = deliveryStrategyFactory.getDeliveryStrategy(itemType);
//2.调用策略实现类对象进行发货方法调用
strategy.delivery(userPaidRecordVo);
}
} }