第14天 策略和工厂模式
This commit is contained in:
parent
e287bcea12
commit
314fffde91
|
@ -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 = "付款项目类型不能为空")
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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("当前商品类型不支持处理!");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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> {
|
||||||
|
}
|
|
@ -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> {
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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("当前商品类型不支持处理!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue