订单模块

This commit is contained in:
yovinchen 2023-10-13 10:24:32 +08:00
parent 2a39b11337
commit 1d6ed7f564
90 changed files with 2862 additions and 65 deletions

View File

@ -16,6 +16,7 @@
<module name="service-sys" />
<module name="service-user-client" />
<module name="service-util" />
<module name="service-order" />
<module name="service-activity-client" />
<module name="service-acl" />
<module name="service-product" />
@ -23,6 +24,7 @@
<module name="service-cart" />
<module name="service-search" />
<module name="model" />
<module name="service-cart-client" />
<module name="service-activity" />
</profile>
</annotationProcessing>
@ -37,9 +39,11 @@
<module name="service-activity" options="-parameters" />
<module name="service-activity-client" options="-parameters" />
<module name="service-cart" options="-parameters" />
<module name="service-cart-client" options="-parameters" />
<module name="service-client" options="" />
<module name="service-gateway" options="-parameters" />
<module name="service-home" options="-parameters" />
<module name="service-order" options="-parameters" />
<module name="service-product" options="-parameters" />
<module name="service-product-client" options="-parameters" />
<module name="service-search" options="-parameters" />

View File

@ -17,5 +17,36 @@
<jdbc-url>jdbc:mysql://82.157.68.223:3306/shequ-activity</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="shequ-order" uuid="397a8c77-3077-4c17-a22d-675f1442d6d6">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<remarks>权限表</remarks>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://82.157.68.223:3306/shequ-order</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="shequ-product" uuid="7176aa17-21d6-4b9f-a902-11f4d21a4353">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<remarks>权限表</remarks>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://82.157.68.223:3306</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="shequ-user" uuid="ad5f372a-c8a7-4ae7-9a68-afd94b10e63e">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://82.157.68.223:3306/shequ-user</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="shequ-acl" uuid="1b4548c2-6868-414c-b36e-02d0e0b6ed51">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<remarks>权限表</remarks>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://82.157.68.223:3306</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

View File

@ -8,6 +8,7 @@
<file url="file://$PROJECT_DIR$/guigu-ssyx-parent/common/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/guigu-ssyx-parent/model/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/guigu-ssyx-parent/service-client/service-activity-client/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/guigu-ssyx-parent/service-client/service-cart-client/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/guigu-ssyx-parent/service-client/service-product-client/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/guigu-ssyx-parent/service-client/service-search-client/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/guigu-ssyx-parent/service-client/service-user-client/src/main/java" charset="UTF-8" />
@ -18,6 +19,7 @@
<file url="file://$PROJECT_DIR$/guigu-ssyx-parent/service/service-activity/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/guigu-ssyx-parent/service/service-cart/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/guigu-ssyx-parent/service/service-home/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/guigu-ssyx-parent/service/service-order/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/guigu-ssyx-parent/service/service-product/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/guigu-ssyx-parent/service/service-search/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/guigu-ssyx-parent/service/service-sys/src/main/java" charset="UTF-8" />

View File

@ -0,0 +1,126 @@
package com.atguigu.ssyx.common.utils;
import org.apache.commons.lang.time.DateUtils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* ClassName: DateUtil
* Package: com.atguigu.ssyx.common.utils
* 日期操作工具类
*
* @author yovinchen
* @Create 2023/10/12 18:04
*/
public class DateUtil {
private static final String dateFormat = "yyyy-MM-dd";
private static final String timeFormat = "HH:mm:ss";
/**
* 格式化日期
*
* @param date
* @return
*/
public static String formatDate(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
return sdf.format(date);
}
/**
* 格式化日期
*
* @param date
* @return
*/
public static String formatTime(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat(timeFormat);
return sdf.format(date);
}
public static Date parseTime(String date) {
SimpleDateFormat sdf = new SimpleDateFormat(timeFormat);
try {
return sdf.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
/**
* 截取比较断两个日期对象的field处的值
* 如果第一个日期小于等于大于第二个则对应返回负整数0正整数
*
* @param date1 第一个日期对象非null
* @param date2 第二个日期对象非null
* @param field Calendar中的阈值
* <p>
* date1 > date2 返回1
* date1 = date2 返回0
* date1 < date2 返回-1
*/
public static int truncatedCompareTo(final Date date1, final Date date2, final int field) {
return DateUtils.truncatedCompareTo(date1, date2, field);
}
/**
* 比对日期与时间大小
*
* @param beginDate
* @param endDate
* @return
*/
public static boolean dateCompare(Date beginDate, Date endDate) {
// endDate > beginDate
return DateUtil.truncatedCompareTo(beginDate, endDate, Calendar.SECOND) != 1;
}
/**
* 比对日期与时间大小
*
* @param beginDate
* @param endDate
* @return
*/
public static boolean timeCompare(Date beginDate, Date endDate) {
Calendar instance1 = Calendar.getInstance();
instance1.setTime(beginDate); //设置时间为当前时间
instance1.set(Calendar.YEAR, 0);
instance1.set(Calendar.MONTH, 0);
instance1.set(Calendar.DAY_OF_MONTH, 0);
Calendar instance2 = Calendar.getInstance();
instance2.setTime(endDate); //设置时间为当前时间
instance2.set(Calendar.YEAR, 0);
instance2.set(Calendar.MONTH, 0);
instance2.set(Calendar.DAY_OF_MONTH, 0);
// endDate > beginDate
return DateUtil.truncatedCompareTo(instance1.getTime(), instance2.getTime(), Calendar.SECOND) != 1;
}
/**
* 获取当前时间到晚上23点59分59秒的时间间隔单位
*
* @return
*/
public static Long getCurrentExpireTimes() {
//过期截止时间
Calendar instance = Calendar.getInstance();
instance.setTime(new Date()); //设置时间为当前时间
instance.set(Calendar.HOUR_OF_DAY, 23);
instance.set(Calendar.MINUTE, 59);
instance.set(Calendar.SECOND, 59);
Date endTime = instance.getTime();
//当前时间与截止时间间隔单位
long interval = (endTime.getTime() - new Date().getTime()) / 1000;
return 100 * 60 * 60 * 24 * 365L;
}
}

View File

@ -0,0 +1,60 @@
package com.atguigu.ssyx.common.config;
import lombok.Data;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
/**
* ClassName: RedissonConfig
* Package: com.atguigu.ssyx.common.config
*
* @author yovinchen
* @Create 2023/10/12 16:48
*/
@Data
@Configuration
@ConfigurationProperties("spring.redis")
public class RedissonConfig {
private static String ADDRESS_PREFIX = "redis://";
private String host;
private String addresses;
private String password;
private String port;
private int timeout = 3000;
private int connectionPoolSize = 64;
private int connectionMinimumIdleSize = 10;
private int pingConnectionInterval = 60000;
/**
* 自动装配
*/
@Bean
RedissonClient redissonSingle() {
Config config = new Config();
// 判断redis 的host是否为空
if (StringUtils.isEmpty(host)) {
throw new RuntimeException("host is empty");
}
// 配置hostport等参数
SingleServerConfig serverConfig = config.useSingleServer()
//redis://127.0.0.1:7181
.setAddress(ADDRESS_PREFIX + this.host + ":" + port)
.setTimeout(this.timeout)
.setPingConnectionInterval(pingConnectionInterval)
.setConnectionPoolSize(this.connectionPoolSize)
.setConnectionMinimumIdleSize(this.connectionMinimumIdleSize);
// 判断进入redis 是否密码
if (!StringUtils.isEmpty(this.password)) {
serverConfig.setPassword(this.password);
}
// RedissonClient redisson = Redisson.create(config);
return Redisson.create(config);
}
}

View File

@ -25,7 +25,7 @@ public class CodeGet {
// 2全局配置
// 全局配置
GlobalConfig gc = new GlobalConfig();
gc.setOutputDir("guigu-ssyx-parent/service/service-activity" + "/src/main/java");
gc.setOutputDir("guigu-ssyx-parent/service/service-order" + "/src/main/java");
gc.setServiceName("%sService"); //去掉Service接口的首字母I
gc.setAuthor("atguigu");
@ -34,17 +34,17 @@ public class CodeGet {
// 3数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://82.157.68.223:3306/shequ-activity?serverTimezone=GMT%2B8&useSSL=false");
dsc.setUrl("jdbc:mysql://82.157.68.223:3306/shequ-order?serverTimezone=GMT%2B8&useSSL=false");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("shequ-activity");
dsc.setPassword("shequ-activity");
dsc.setUsername("shequ-order");
dsc.setPassword("shequ-order");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4包配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.atguigu.ssyx");
pc.setModuleName("activity"); //模块名
pc.setModuleName("order"); //模块名
pc.setController("controller");
pc.setService("service");
pc.setMapper("mapper");
@ -53,7 +53,7 @@ public class CodeGet {
// 5策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("activity_info", "activity_rule", "activity_sku", "coupon_info", "coupon_range", "coupon_use", "home_subject", "home_subject_sku", "seckill", "seckill_sku", "seckill_sku_notice", "seckill_time", "sku_info");
strategy.setInclude("cart_info", "order_info", "order_deliver", "order_item", "order_log", "order_return_apply", "order_return_reason", "order_set", "payment_info", "refund_info");
strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略

View File

@ -16,6 +16,7 @@
<module>service-user-client</module>
<module>service-search-client</module>
<module>service-activity-client</module>
<module>service-cart-client</module>
</modules>
<dependencies>
<dependency>

View File

@ -1,5 +1,9 @@
package com.atguigu.ssyx.client.activity;
import com.atguigu.ssyx.model.activity.CouponInfo;
import com.atguigu.ssyx.model.order.CartInfo;
import com.atguigu.ssyx.vo.order.CartInfoVo;
import com.atguigu.ssyx.vo.order.OrderConfirmVo;
import io.swagger.annotations.ApiOperation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@ -20,10 +24,30 @@ import java.util.Map;
@FeignClient(value = "service-activity")
public interface ActivityFeignClient {
@ApiOperation(value = "根据skuId列表获取促销信息")
@PostMapping("/api/activity/inner/findActivity")
Map<Long, List<String>> findActivity(@RequestBody List<Long> skuIdList);
@ApiOperation("根据skuID获取营销数据和优惠卷")
@GetMapping("/api/activity/inner/findActivityAndCoupon/{skuId}/{userId}")
Map<String, Object> findActivityAndCoupon(@PathVariable("skuId") Long skuId, @PathVariable("userId") Long userId);
@ApiOperation("获取购物车中满足条件优惠券和活动")
@PostMapping("/api/activity/inner/findCartActivityAndCoupon/{userId}")
OrderConfirmVo findCartActivityAndCoupon(
@RequestBody List<CartInfo> cartInfoList, @PathVariable("userId") Long userId);
@ApiOperation("获取购物车对应规则数据")
@PostMapping("/api/activity/inner/findCartActivityList")
List<CartInfoVo> findCartActivityList(@RequestBody List<CartInfo> cartInfoList);
@ApiOperation("获取购物车对应优惠券数据")
@PostMapping("/api/activity/inner/findRangeSkuIdList")
CouponInfo findRangeSkuIdList(@RequestBody List<CartInfo> cartInfoList, @PathVariable("couponId") Long couponId);
@ApiOperation(value = "更新优惠卷使用状态")
@GetMapping("/api/activity/inner/updateCouponInfoUseStatus/{couponId}/{userId}/{orderId}")
Boolean updateCouponInfoUseStatus(@PathVariable("couponId") Long couponId,
@PathVariable("userId") Long userId,
@PathVariable("orderId") Long orderId);
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.atguigu</groupId>
<artifactId>service-client</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>service-cart-client</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@ -0,0 +1,24 @@
package com.atguigu.ssyx.client.cart;
import com.atguigu.ssyx.model.order.CartInfo;
import io.swagger.annotations.ApiOperation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;
/**
* ClassName: CartFeignClient
* Package: com.atguigu.ssyx.client.cart
*
* @author yovinchen
* @Create 2023/10/12 15:49
*/
@FeignClient(value = "service-cart")
public interface CartFeignClient {
@ApiOperation(value = "根据用户Id查询购物车列表")
@PostMapping("/api/cart/inner/getCartCheckedList/{userId}")
List<CartInfo> getCartCheckedList(@PathVariable("userId") Long userId);
}

View File

@ -3,6 +3,7 @@ package com.atguigu.ssyx.client.product;
import com.atguigu.ssyx.model.product.Category;
import com.atguigu.ssyx.model.product.SkuInfo;
import com.atguigu.ssyx.vo.product.SkuInfoVo;
import com.atguigu.ssyx.vo.product.SkuStockLockVo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@ -53,7 +54,7 @@ public interface ProductFeignClient {
* @param skuIdList
* @return
*/
@GetMapping("/api/product/inner/findSkuInfoList")
@PostMapping("/api/product/inner/findSkuInfoList")
List<SkuInfo> findSkuInfoList(@RequestBody List<Long> skuIdList);
/**
@ -89,4 +90,7 @@ public interface ProductFeignClient {
*/
@GetMapping("/api/product/inner/getSkuInfoVo/{skuId}")
SkuInfoVo getSkuInfoVo(@PathVariable Long skuId);
@PostMapping("/api/product/inner/checkAndLock/{orderNo}")
Boolean checkAndLock(@RequestBody List<SkuStockLockVo> commonStockLockVoList, @PathVariable("orderNo") String orderNo);
}

View File

@ -20,6 +20,7 @@
<module>service-user</module>
<module>service-home</module>
<module>service-cart</module>
<module>service-order</module>
</modules>
<dependencies>

View File

@ -14,7 +14,18 @@ spring:
url: jdbc:p6spy:mysql://82.157.68.223:3306/shequ-acl?characterEncoding=utf-8&useSSL=false
username: shequ-acl
password: shequ-acl
redis:
host: 82.157.68.223
port: 6379
database: 0
timeout: 1800000
password:
lettuce:
pool:
max-active: 20 #最大连接数
max-wait: -1 #最大阻塞等待时间(负数表示没限制)
max-idle: 5 #最大空闲
min-idle: 0 #最小空闲
jackson:
date-format: yyyy-MM-dd HH:mm:ss

View File

@ -1,9 +1,15 @@
package com.atguigu.ssyx.activity.api;
import com.atguigu.ssyx.activity.service.ActivityInfoService;
import com.atguigu.ssyx.activity.service.CouponInfoService;
import com.atguigu.ssyx.model.activity.CouponInfo;
import com.atguigu.ssyx.model.order.CartInfo;
import com.atguigu.ssyx.vo.order.CartInfoVo;
import com.atguigu.ssyx.vo.order.OrderConfirmVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@ -26,6 +32,17 @@ public class ActivityApiController {
@Resource
private ActivityInfoService activityInfoService;
@Autowired
private CouponInfoService couponInfoService;
@ApiOperation("获取购物车中满足条件优惠券和活动")
@PostMapping("inner/findCartActivityAndCoupon/{userId}")
public OrderConfirmVo findCartActivityAndCoupon(
@RequestBody List<CartInfo> cartInfoList, @PathVariable("userId") Long userId) {
return activityInfoService.findCartActivityAndCoupon(cartInfoList, userId);
}
@ApiOperation(value = "根据skuId列表获取促销信息")
@PostMapping("inner/findActivity")
public Map<Long, List<String>> findActivity(@RequestBody List<Long> skuIdList) {
@ -34,7 +51,29 @@ public class ActivityApiController {
@ApiOperation("根据skuID获取营销数据和优惠卷")
@GetMapping("inner/findActivityAndCoupon/{skuId}/{userId}")
public Map<String, Object> findActivityAndCoupon(@PathVariable Long skuId, @PathVariable Long userId) {
public Map<String, Object> findActivityAndCoupon(
@PathVariable("skuId") Long skuId, @PathVariable("userId") Long userId) {
return activityInfoService.findActivityAndCoupon(skuId, userId);
}
@ApiOperation("获取购物车对应规则数据")
@PostMapping("inner/findCartActivityList")
List<CartInfoVo> findCartActivityList(@RequestBody List<CartInfo> cartInfoList) {
return activityInfoService.findCartActivityList(cartInfoList);
}
@ApiOperation("获取购物车对应优惠券数据")
@PostMapping("inner/findRangeSkuIdList")
CouponInfo findRangeSkuIdList(@RequestBody List<CartInfo> cartInfoList, @PathVariable("couponId") Long couponId) {
return couponInfoService.findRangeSkuIdList(cartInfoList, couponId);
}
@ApiOperation(value = "更新优惠卷使用状态")
@GetMapping("inner/updateCouponInfoUseStatus/{couponId}/{userId}/{orderId}")
public Boolean updateCouponInfoUseStatus(@PathVariable("couponId") Long couponId,
@PathVariable("userId") Long userId,
@PathVariable("orderId") Long orderId) {
couponInfoService.updateCouponInfoUseStatus(couponId, userId, orderId);
return true;
}
}

View File

@ -2,6 +2,7 @@ package com.atguigu.ssyx.activity.mapper;
import com.atguigu.ssyx.model.activity.ActivityInfo;
import com.atguigu.ssyx.model.activity.ActivityRule;
import com.atguigu.ssyx.model.activity.ActivitySku;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import feign.Param;
import org.springframework.stereotype.Repository;
@ -33,5 +34,13 @@ public interface ActivityInfoMapper extends BaseMapper<ActivityInfo> {
* @param skuId
* @return
*/
List<ActivityRule> findActivityRule(Long skuId);
List<ActivityRule> findActivityRule(@Param("skuId") Long skuId);
/**
* 根据所有skuId列表获取参与活动
*
* @param skuIdList
* @return
*/
List<ActivitySku> selectCartActivity(@Param("skuIdList") List<Long> skuIdList);
}

View File

@ -26,4 +26,12 @@ public interface CouponInfoMapper extends BaseMapper<CouponInfo> {
*/
List<CouponInfo> selectCouponInfoList(
@Param("skuId") Long id, @Param("categoryId") Long categoryId, @Param("userId") Long userId);
/**
* 根据userId获取用户全部优惠卷
*
* @param userId
* @return
*/
List<CouponInfo> selectCartCouponInfoList(@Param("userId") Long userId);
}

View File

@ -2,6 +2,7 @@ package com.atguigu.ssyx.activity.mapper;
import com.atguigu.ssyx.model.activity.CouponUse;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
/**
* <p>
@ -11,6 +12,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
* @author atguigu
* @since 2023-09-17
*/
@Repository
public interface CouponUseMapper extends BaseMapper<CouponUse> {
}

View File

@ -2,8 +2,11 @@ package com.atguigu.ssyx.activity.service;
import com.atguigu.ssyx.model.activity.ActivityInfo;
import com.atguigu.ssyx.model.order.CartInfo;
import com.atguigu.ssyx.model.product.SkuInfo;
import com.atguigu.ssyx.vo.activity.ActivityRuleVo;
import com.atguigu.ssyx.vo.order.CartInfoVo;
import com.atguigu.ssyx.vo.order.OrderConfirmVo;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
@ -69,4 +72,21 @@ public interface ActivityInfoService extends IService<ActivityInfo> {
* @return
*/
Map<String, Object> findActivityAndCoupon(Long skuId, Long userId);
/**
* 获取购物车中满足条件优惠券和活动
*
* @param cartInfoList
* @param userId
* @return
*/
OrderConfirmVo findCartActivityAndCoupon(List<CartInfo> cartInfoList, Long userId);
/**
* 获取购物车对应规则数据
*
* @param cartInfoList
* @return
*/
List<CartInfoVo> findCartActivityList(List<CartInfo> cartInfoList);
}

View File

@ -1,6 +1,7 @@
package com.atguigu.ssyx.activity.service;
import com.atguigu.ssyx.model.activity.CouponInfo;
import com.atguigu.ssyx.model.order.CartInfo;
import com.atguigu.ssyx.vo.activity.CouponRuleVo;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
@ -68,4 +69,31 @@ public interface CouponInfoService extends IService<CouponInfo> {
*/
List<CouponInfo> findCouponInfoList(Long skuId, Long userId);
/**
* 获取购物车可以使用优惠卷列表
*
* @param cartInfoList
* @param userId
* @return
*/
List<CouponInfo> findCartCouponInfo(List<CartInfo> cartInfoList, Long userId);
/**
* 获取购物车对应优惠券数据
*
* @param cartInfoList
* @param couponId
* @return
*/
CouponInfo findRangeSkuIdList(List<CartInfo> cartInfoList, Long couponId);
/**
* 更新优惠卷使用状态
*
* @param couponId
* @param userId
* @param orderId
*/
void updateCouponInfoUseStatus(Long couponId, Long userId, Long orderId);
}

View File

@ -11,8 +11,11 @@ import com.atguigu.ssyx.model.activity.ActivityInfo;
import com.atguigu.ssyx.model.activity.ActivityRule;
import com.atguigu.ssyx.model.activity.ActivitySku;
import com.atguigu.ssyx.model.activity.CouponInfo;
import com.atguigu.ssyx.model.order.CartInfo;
import com.atguigu.ssyx.model.product.SkuInfo;
import com.atguigu.ssyx.vo.activity.ActivityRuleVo;
import com.atguigu.ssyx.vo.order.CartInfoVo;
import com.atguigu.ssyx.vo.order.OrderConfirmVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@ -23,10 +26,8 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
/**
@ -245,6 +246,265 @@ public class ActivityInfoServiceImpl extends ServiceImpl<ActivityInfoMapper, Act
return map;
}
/**
* 获取购物车中满足条件优惠券和活动
*
* @param cartInfoList
* @param userId
* @return
*/
@Override
public OrderConfirmVo findCartActivityAndCoupon(List<CartInfo> cartInfoList, Long userId) {
//1 获取购物车每个购物项参与活动根据活动规则分组
//一个规则对应多个商品
List<CartInfoVo> cartInfoVoList = this.findCartActivityList(cartInfoList);
//2 计算参与活动之后金额
BigDecimal activityReduceAmount = cartInfoVoList.stream()
.filter(cartInfoVo -> cartInfoVo.getActivityRule() != null)
.map(cartInfoVo -> cartInfoVo.getActivityRule()
.getReduceAmount())
.reduce(BigDecimal.ZERO, BigDecimal::add);
//3 获取购物车可以使用优惠卷列表
List<CouponInfo> couponInfoList = couponInfoService.findCartCouponInfo(cartInfoList, userId);
//4 计算商品使用优惠卷之后金额一次只能使用一张优惠卷
BigDecimal couponReduceAmount = new BigDecimal(0);
if (!CollectionUtils.isEmpty(couponInfoList)) {
couponReduceAmount = couponInfoList.stream()
.filter(couponInfo -> couponInfo.getIsOptimal()
.intValue() == 1)
.map(couponInfo -> couponInfo.getAmount())
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
//5 计算没有参与活动没有使用优惠卷原始金额
BigDecimal originalTotalAmount = cartInfoList.stream()
.filter(cartInfo -> cartInfo.getIsChecked() == 1)
.map(cartInfo -> cartInfo.getCartPrice()
.multiply(new BigDecimal(cartInfo.getSkuNum())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
//6 最终金额
BigDecimal totalAmount = originalTotalAmount.subtract(activityReduceAmount)
.subtract(couponReduceAmount);
//7 封装需要数据到OrderConfirmVo,返回
OrderConfirmVo orderTradeVo = new OrderConfirmVo();
orderTradeVo.setCarInfoVoList(cartInfoVoList);
orderTradeVo.setActivityReduceAmount(activityReduceAmount);
orderTradeVo.setCouponInfoList(couponInfoList);
orderTradeVo.setCouponReduceAmount(couponReduceAmount);
orderTradeVo.setOriginalTotalAmount(originalTotalAmount);
orderTradeVo.setTotalAmount(totalAmount);
return orderTradeVo;
}
/**
* 获取购物车对应规则数据
*
* @param cartInfoList
* @return
*/
@Override
public List<CartInfoVo> findCartActivityList(List<CartInfo> cartInfoList) {
//创建最终返回集合
List<CartInfoVo> cartInfoVoList = new ArrayList<>();
//获取所有skuId
List<Long> skuIdList = cartInfoList.stream()
.map(CartInfo::getSkuId)
.collect(Collectors.toList());
//根据所有skuId列表获取参与活动
List<ActivitySku> activitySkuList = baseMapper.selectCartActivity(skuIdList);
//根据活动进行分组每个活动里面有哪些skuId信息
//map里面key是分组字段 活动id
// value是每组里面sku列表数据set集合
Map<Long, Set<Long>> activityIdToSkuIdListMap = activitySkuList.stream()
.collect(Collectors.groupingBy(ActivitySku::getActivityId, Collectors.mapping(ActivitySku::getSkuId, Collectors.toSet())));
//获取活动里面规则数据
//key是活动id value是活动里面规则列表数据
Map<Long, List<ActivityRule>> activityIdToActivityRuleListMap = new HashMap<>();
//所有活动id
Set<Long> activityIdSet = activitySkuList.stream()
.map(ActivitySku::getActivityId)
.collect(Collectors.toSet());
if (!CollectionUtils.isEmpty(activityIdSet)) {
//activity_rule表
LambdaQueryWrapper<ActivityRule> wrapper = new LambdaQueryWrapper<>();
wrapper.orderByDesc(ActivityRule::getConditionAmount, ActivityRule::getConditionNum);
wrapper.in(ActivityRule::getActivityId, activityIdSet);
List<ActivityRule> activityRuleList = activityRuleMapper.selectList(wrapper);
//封装到activityIdToActivityRuleListMap里面
//根据活动id进行分组
activityIdToActivityRuleListMap = activityRuleList.stream()
.collect(Collectors.groupingBy(activityRule -> activityRule.getActivityId()));
}
//有活动的购物项skuId
Set<Long> activitySkuIdSet = new HashSet<>();
if (!CollectionUtils.isEmpty(activityIdToSkuIdListMap)) {
//遍历activityIdToSkuIdListMap集合
Iterator<Map.Entry<Long, Set<Long>>> iterator = activityIdToSkuIdListMap.entrySet()
.iterator();
while (iterator.hasNext()) {
Map.Entry<Long, Set<Long>> entry = iterator.next();
//活动id
Long activityId = entry.getKey();
//每个活动对应skuId列表
Set<Long> currentActivitySkuIdSet = entry.getValue();
//获取当前活动对应的购物项列表
List<CartInfo> currentActivityCartInfoList = cartInfoList.stream()
.filter(cartInfo -> currentActivitySkuIdSet.contains(cartInfo.getSkuId()))
.collect(Collectors.toList());
//计数购物项总金额和总数量
BigDecimal activityTotalAmount = this.computeTotalAmount(currentActivityCartInfoList);
int activityTotalNum = this.computeCartNum(currentActivityCartInfoList);
//计算活动对应规则
//根据activityId获取活动对应规则
List<ActivityRule> currentActivityRuleList = activityIdToActivityRuleListMap.get(activityId);
ActivityType activityType = currentActivityRuleList.get(0)
.getActivityType();
//判断活动类型满减和打折
ActivityRule activityRule = null;
if (activityType == ActivityType.FULL_REDUCTION) {//满减"
activityRule = this.computeFullReduction(activityTotalAmount, currentActivityRuleList);
} else {//满量
activityRule = this.computeFullDiscount(activityTotalNum, activityTotalAmount, currentActivityRuleList);
}
//CartInfoVo封装
CartInfoVo cartInfoVo = new CartInfoVo();
cartInfoVo.setActivityRule(activityRule);
cartInfoVo.setCartInfoList(currentActivityCartInfoList);
cartInfoVoList.add(cartInfoVo);
//记录哪些购物项参与活动
activitySkuIdSet.addAll(currentActivitySkuIdSet);
}
}
//没有活动购物项skuId
//获取哪些skuId没有参加活动
skuIdList.removeAll(activitySkuIdSet);
if (!CollectionUtils.isEmpty(skuIdList)) {
//skuId对应购物项
Map<Long, CartInfo> skuIdCartInfoMap = cartInfoList.stream()
.collect(Collectors.toMap(CartInfo::getSkuId, CartInfo -> CartInfo));
for (Long skuId : skuIdList) {
CartInfoVo cartInfoVo = new CartInfoVo();
cartInfoVo.setActivityRule(null);//没有活动
List<CartInfo> cartInfos = new ArrayList<>();
cartInfos.add(skuIdCartInfoMap.get(skuId));
cartInfoVo.setCartInfoList(cartInfos);
cartInfoVoList.add(cartInfoVo);
}
}
return cartInfoVoList;
}
/**
* 计算满量打折最优规则
* 该活动规则skuActivityRuleList数据已经按照优惠折扣从大到小排序了
*
* @param totalNum
* @param activityRuleList
*/
private ActivityRule computeFullDiscount(Integer totalNum, BigDecimal totalAmount, List<ActivityRule> activityRuleList) {
ActivityRule optimalActivityRule = null;
//该活动规则skuActivityRuleList数据已经按照优惠金额从大到小排序了
for (ActivityRule activityRule : activityRuleList) {
//如果订单项购买个数大于等于满减件数则优化打折
if (totalNum.intValue() >= activityRule.getConditionNum()) {
BigDecimal skuDiscountTotalAmount = totalAmount.multiply(activityRule.getBenefitDiscount()
.divide(new BigDecimal("10")));
BigDecimal reduceAmount = totalAmount.subtract(skuDiscountTotalAmount);
activityRule.setReduceAmount(reduceAmount);
optimalActivityRule = activityRule;
break;
}
}
if (null == optimalActivityRule) {
//如果没有满足条件的取最小满足条件的一项
optimalActivityRule = activityRuleList.get(activityRuleList.size() - 1);
optimalActivityRule.setReduceAmount(new BigDecimal("0"));
optimalActivityRule.setSelectType(1);
String ruleDesc = "" +
optimalActivityRule.getConditionNum() +
"元打" +
optimalActivityRule.getBenefitDiscount() +
"折,还差" +
(totalNum - optimalActivityRule.getConditionNum()) +
"";
optimalActivityRule.setRuleDesc(ruleDesc);
} else {
String ruleDesc = "" +
optimalActivityRule.getConditionNum() +
"元打" +
optimalActivityRule.getBenefitDiscount() +
"折,已减" +
optimalActivityRule.getReduceAmount() +
"";
optimalActivityRule.setRuleDesc(ruleDesc);
optimalActivityRule.setSelectType(2);
}
return optimalActivityRule;
}
/**
* 计算满减最优规则
* 该活动规则skuActivityRuleList数据已经按照优惠金额从大到小排序了
*
* @param totalAmount
* @param activityRuleList
*/
private ActivityRule computeFullReduction(BigDecimal totalAmount, List<ActivityRule> activityRuleList) {
ActivityRule optimalActivityRule = null;
//该活动规则skuActivityRuleList数据已经按照优惠金额从大到小排序了
for (ActivityRule activityRule : activityRuleList) {
//如果订单项金额大于等于满减金额则优惠金额
if (totalAmount.compareTo(activityRule.getConditionAmount()) > -1) {
//优惠后减少金额
activityRule.setReduceAmount(activityRule.getBenefitAmount());
optimalActivityRule = activityRule;
break;
}
}
if (null == optimalActivityRule) {
//如果没有满足条件的取最小满足条件的一项
optimalActivityRule = activityRuleList.get(activityRuleList.size() - 1);
optimalActivityRule.setReduceAmount(new BigDecimal("0"));
optimalActivityRule.setSelectType(1);
String ruleDesc = "" +
optimalActivityRule.getConditionAmount() +
"元减" +
optimalActivityRule.getBenefitAmount() +
"元,还差" +
totalAmount.subtract(optimalActivityRule.getConditionAmount()) +
"";
optimalActivityRule.setRuleDesc(ruleDesc);
} else {
String ruleDesc = "" +
optimalActivityRule.getConditionAmount() +
"元减" +
optimalActivityRule.getBenefitAmount() +
"元,已减" +
optimalActivityRule.getReduceAmount() +
"";
optimalActivityRule.setRuleDesc(ruleDesc);
optimalActivityRule.setSelectType(2);
}
return optimalActivityRule;
}
//根据skuId获取活动规则数据
public List<ActivityRule> findActivityRuleBySkuId(Long skuId) {
List<ActivityRule> activityRuleList = baseMapper.findActivityRule(skuId);
@ -255,4 +515,32 @@ public class ActivityInfoServiceImpl extends ServiceImpl<ActivityInfoMapper, Act
}
return activityRuleList;
}
//计算购物车中金额
private BigDecimal computeTotalAmount(List<CartInfo> cartInfoList) {
BigDecimal total = new BigDecimal("0");
for (CartInfo cartInfo : cartInfoList) {
//是否选中
if (cartInfo.getIsChecked()
.intValue() == 1) {
BigDecimal itemTotal = cartInfo.getCartPrice()
.multiply(new BigDecimal(cartInfo.getSkuNum()));
total = total.add(itemTotal);
}
}
return total;
}
//计算购物车中总数量
private int computeCartNum(List<CartInfo> cartInfoList) {
int total = 0;
for (CartInfo cartInfo : cartInfoList) {
//是否选中
if (cartInfo.getIsChecked()
.intValue() == 1) {
total += cartInfo.getSkuNum();
}
}
return total;
}
}

View File

@ -2,11 +2,16 @@ package com.atguigu.ssyx.activity.service.impl;
import com.atguigu.ssyx.activity.mapper.CouponInfoMapper;
import com.atguigu.ssyx.activity.mapper.CouponRangeMapper;
import com.atguigu.ssyx.activity.mapper.CouponUseMapper;
import com.atguigu.ssyx.activity.service.CouponInfoService;
import com.atguigu.ssyx.client.product.ProductFeignClient;
import com.atguigu.ssyx.enums.CouponRangeType;
import com.atguigu.ssyx.enums.CouponStatus;
import com.atguigu.ssyx.model.activity.CouponInfo;
import com.atguigu.ssyx.model.activity.CouponRange;
import com.atguigu.ssyx.model.activity.CouponUse;
import com.atguigu.ssyx.model.base.BaseEntity;
import com.atguigu.ssyx.model.order.CartInfo;
import com.atguigu.ssyx.model.product.Category;
import com.atguigu.ssyx.model.product.SkuInfo;
import com.atguigu.ssyx.vo.activity.CouponRuleVo;
@ -18,9 +23,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
/**
@ -34,12 +38,12 @@ import java.util.stream.Collectors;
@Service
public class CouponInfoServiceImpl extends ServiceImpl<CouponInfoMapper, CouponInfo> implements CouponInfoService {
@Autowired
CouponRangeMapper couponRangeMapper;
@Autowired
ProductFeignClient productFeignClient;
@Autowired
private CouponUseMapper couponUseMapper;
/**
* 获取分页列表
@ -55,7 +59,7 @@ public class CouponInfoServiceImpl extends ServiceImpl<CouponInfoMapper, CouponI
List<CouponInfo> couponInfoList = couponInfoPage.getRecords();
couponInfoList.forEach(item -> {
item.setCouponTypeString(item.getCouponType()
.getComment());
.getComment());
CouponRangeType rangeType = item.getRangeType();
if (rangeType != null) {
item.setRangeTypeString(rangeType.getComment());
@ -74,10 +78,10 @@ public class CouponInfoServiceImpl extends ServiceImpl<CouponInfoMapper, CouponI
public CouponInfo getCouponInfo(Long id) {
CouponInfo couponInfo = baseMapper.selectById(id);
couponInfo.setCouponTypeString(couponInfo.getCouponType()
.getComment());
.getComment());
if (couponInfo.getRangeType() != null) {
couponInfo.setRangeTypeString(couponInfo.getRangeType()
.getComment());
.getComment());
}
return couponInfo;
}
@ -99,8 +103,8 @@ public class CouponInfoServiceImpl extends ServiceImpl<CouponInfoMapper, CouponI
//// 如果规则类型 SKU range_id就是skuId值
//// 如果规则类型 CATEGORY range_id就是分类Id值
List<Long> randIdList = couponRangeList.stream()
.map(CouponRange::getRangeId)
.collect(Collectors.toList());
.map(CouponRange::getRangeId)
.collect(Collectors.toList());
Map<String, Object> result = new HashMap<>();
//第三步 分别判断封装不同数据
@ -172,4 +176,186 @@ public class CouponInfoServiceImpl extends ServiceImpl<CouponInfoMapper, CouponI
return couponInfoList;
}
/**
* 获取购物车可以使用优惠卷列表
*
* @param cartInfoList
* @param userId
* @return
*/
@Override
public List<CouponInfo> findCartCouponInfo(List<CartInfo> cartInfoList, Long userId) {
//1 根据userId获取用户全部优惠卷
//coupon_use coupon_info
List<CouponInfo> userAllCouponInfoList = baseMapper.selectCartCouponInfoList(userId);
if (CollectionUtils.isEmpty(userAllCouponInfoList)) {
return new ArrayList<CouponInfo>();
}
//2 从第一步返回list集合中获取所有优惠卷id列表
List<Long> couponIdList = userAllCouponInfoList.stream()
.map(BaseEntity::getId)
.collect(Collectors.toList());
//3 查询优惠卷对应的范围 coupon_range
//couponRangeList
LambdaQueryWrapper<CouponRange> wrapper = new LambdaQueryWrapper<>();
// id in (1,2,3)
wrapper.in(CouponRange::getCouponId, couponIdList);
List<CouponRange> couponRangeList = couponRangeMapper.selectList(wrapper);
//4 获取优惠卷id 对应skuId列表
//优惠卷id进行分组得到map集合
// Map<Long,List<Long>>
Map<Long, List<Long>> couponIdToSkuIdMap = this.findCouponIdToSkuIdMap(cartInfoList, couponRangeList);
//5 遍历全部优惠卷集合判断优惠卷类型
//全场通用 sku和分类
BigDecimal reduceAmount = new BigDecimal(0);
CouponInfo optimalCouponInfo = null;
for (CouponInfo couponInfo : userAllCouponInfoList) {
//全场通用
if (CouponRangeType.ALL == couponInfo.getRangeType()) {
//全场通用
//判断是否满足优惠使用门槛
//计算购物车商品的总价
BigDecimal totalAmount = computeTotalAmount(cartInfoList);
if (totalAmount.subtract(couponInfo.getConditionAmount())
.doubleValue() >= 0) {
couponInfo.setIsSelect(1);
}
} else {
//优惠卷id获取对应skuId列表
List<Long> skuIdList = couponIdToSkuIdMap.get(couponInfo.getId());
//满足使用范围购物项
List<CartInfo> currentCartInfoList = cartInfoList.stream()
.filter(cartInfo -> skuIdList.contains(cartInfo.getSkuId()))
.collect(Collectors.toList());
BigDecimal totalAmount = computeTotalAmount(currentCartInfoList);
if (totalAmount.subtract(couponInfo.getConditionAmount())
.doubleValue() >= 0) {
couponInfo.setIsSelect(1);
}
}
if (couponInfo.getIsSelect()
.intValue() == 1 && couponInfo.getAmount()
.subtract(reduceAmount)
.doubleValue() > 0) {
reduceAmount = couponInfo.getAmount();
optimalCouponInfo = couponInfo;
}
}
//6 返回List<CouponInfo>
if (null != optimalCouponInfo) {
optimalCouponInfo.setIsOptimal(1);
}
return userAllCouponInfoList;
}
/**
* 获取购物车对应优惠券数据
*
* @param cartInfoList
* @param couponId
* @return
*/
@Override
public CouponInfo findRangeSkuIdList(List<CartInfo> cartInfoList, Long couponId) {
//根据优惠卷id基本信息查询
CouponInfo couponInfo = baseMapper.selectById(couponId);
if (couponInfo == null) {
return null;
}
//根据couponId查询对应CouponRange数据
List<CouponRange> couponRangeList = couponRangeMapper.selectList(new LambdaQueryWrapper<CouponRange>().eq(CouponRange::getCouponId, couponId));
//对应sku信息
Map<Long, List<Long>> couponIdToSkuIdMap = this.findCouponIdToSkuIdMap(cartInfoList, couponRangeList);
//遍历map得到value值封装到couponInfo对象
List<Long> skuIdList = couponIdToSkuIdMap.entrySet()
.iterator()
.next()
.getValue();
couponInfo.setSkuIdList(skuIdList);
return couponInfo;
}
/**
* 更新优惠卷使用状态
*
* @param couponId
* @param userId
* @param orderId
*/
@Override
public void updateCouponInfoUseStatus(Long couponId, Long userId, Long orderId) {
//根据couponId查询优惠卷信息
CouponUse couponUse = couponUseMapper.selectOne(
new LambdaQueryWrapper<CouponUse>()
.eq(CouponUse::getCouponId, couponId)
.eq(CouponUse::getUserId, userId)
.eq(CouponUse::getOrderId, orderId)
);
//设置修改值
couponUse.setCouponStatus(CouponStatus.USED);
//调用方法修改
couponUseMapper.updateById(couponUse);
}
//获取优惠卷id 对应skuId列表
private Map<Long, List<Long>> findCouponIdToSkuIdMap(List<CartInfo> cartInfoList, List<CouponRange> couponRangeList) {
Map<Long, List<Long>> couponIdToSkuIdMap = new HashMap<>();
//couponRangeList数据处理根据优惠卷id分组
Map<Long, List<CouponRange>> couponRangeToRangeListMap = couponRangeList.stream()
.collect(Collectors.groupingBy(CouponRange::getCouponId));
//遍历map集合
Iterator<Map.Entry<Long, List<CouponRange>>> iterator = couponRangeToRangeListMap.entrySet()
.iterator();
while (iterator.hasNext()) {
Map.Entry<Long, List<CouponRange>> entry = iterator.next();
Long couponId = entry.getKey();
List<CouponRange> rangeList = entry.getValue();
//创建集合 set
Set<Long> skuIdSet = new HashSet<>();
for (CartInfo cartInfo : cartInfoList) {
for (CouponRange couponRange : rangeList) {
//判断
if (couponRange.getRangeType() == CouponRangeType.SKU && couponRange.getRangeId()
.longValue() == cartInfo.getSkuId()
.longValue()) {
skuIdSet.add(cartInfo.getSkuId());
} else if (couponRange.getRangeType() == CouponRangeType.CATEGORY && couponRange.getRangeId()
.longValue() == cartInfo.getCategoryId()
.longValue()) {
skuIdSet.add(cartInfo.getSkuId());
} else {
}
}
}
couponIdToSkuIdMap.put(couponId, new ArrayList<>(skuIdSet));
}
return couponIdToSkuIdMap;
}
//计算购物车商品的总价
private BigDecimal computeTotalAmount(List<CartInfo> cartInfoList) {
BigDecimal total = new BigDecimal("0");
for (CartInfo cartInfo : cartInfoList) {
//是否选中
if (cartInfo.getIsChecked()
.intValue() == 1) {
BigDecimal itemTotal = cartInfo.getCartPrice()
.multiply(new BigDecimal(cartInfo.getSkuNum()));
total = total.add(itemTotal);
}
}
return total;
}
}

View File

@ -13,6 +13,18 @@ spring:
url: jdbc:mysql://82.157.68.223:3306/shequ-activity?characterEncoding=utf-8&useSSL=false
username: shequ-activity
password: shequ-activity
redis:
host: 82.157.68.223
port: 6379
database: 0
timeout: 1800000
password:
lettuce:
pool:
max-active: 20 #最大连接数
max-wait: -1 #最大阻塞等待时间(负数表示没限制)
max-idle: 5 #最大空闲
min-idle: 0 #最小空闲
jackson:
date-format: yyyy-MM-dd HH:mm:ss

View File

@ -21,41 +21,6 @@
</where>
and now() between info.start_time and info.end_time
</select>
<!--//2 根据skuId+分类id+userId查询优惠卷信息-->
<select id="selectCouponInfoList" resultMap="CouponInfoMap">
select info.id,
info.coupon_type,
info.coupon_name,
info.amount,
info.condition_amount,
info.start_time,
info.end_time,
info.range_type,
info.range_desc,
info.publish_count,
info.per_limit,
info.use_count,
info.receive_count,
info.expire_time,
info.publish_status,
info.create_time,
info.update_time,
info.is_deleted,
cuse.coupon_status
from coupon_info info
left join coupon_range crange on info.id = crange.coupon_id
left join coupon_use cuse on info.id = cuse.coupon_id
and cuse.user_id = #{userId}
where (
info.range_type = 1
or (info.range_type = 2 and crange.range_id = #{skuId})
or (info.range_type = 3 and crange.range_id = #{categoryId})
)
and now() between info.start_time and info.end_time
order by info.amount desc
</select>
<!--//根据skuId进行查询查询sku对应活动里面规则列表-->
<select id="findActivityRule" resultMap="ActivityRuleMap">
select rule.id,

View File

@ -29,6 +29,18 @@
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>service-activity-client</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>rabbit_util</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,8 +1,11 @@
package com.atguigu.ssyx.cart.controller;
import com.atguigu.ssyx.cart.service.CartInfoService;
import com.atguigu.ssyx.client.activity.ActivityFeignClient;
import com.atguigu.ssyx.common.auth.AuthContextHolder;
import com.atguigu.ssyx.common.result.Result;
import com.atguigu.ssyx.model.order.CartInfo;
import com.atguigu.ssyx.vo.order.OrderConfirmVo;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@ -23,6 +26,9 @@ public class CartApiController {
@Autowired
private CartInfoService cartInfoService;
@Autowired
private ActivityFeignClient activityFeignClient;
//添加内容当前登录用户idskuId商品数量
@ApiOperation(value = "添加商品到购物车")
@GetMapping("addToCart/{skuId}/{skuNum}")
@ -56,4 +62,56 @@ public class CartApiController {
cartInfoService.batchDeleteCart(skuIdList, userId);
return Result.ok(null);
}
@ApiOperation(value = "购物车列表")
@GetMapping("cartList")
public Result cartList() {
//获取userId
Long userId = AuthContextHolder.getUserId();
List<CartInfo> cartInfoList = cartInfoService.getCartList(userId);
return Result.ok(cartInfoList);
}
@ApiOperation(value = "查询带优惠卷的购物车")
@GetMapping("activityCartList")
public Result activityCartList() {
// 获取用户Id
Long userId = AuthContextHolder.getUserId();
List<CartInfo> cartInfoList = cartInfoService.getCartList(userId);
OrderConfirmVo orderTradeVo = activityFeignClient.findCartActivityAndCoupon(cartInfoList, userId);
return Result.ok(orderTradeVo);
}
@ApiOperation("根据skuId选中")
@GetMapping("checkCart/{skuId}/{isChecked}")
public Result checkCart(@PathVariable("skuId") Long skuId, @PathVariable("isChecked") Integer isChecked) {
//获取userId
Long userId = AuthContextHolder.getUserId();
//调用方法
cartInfoService.checkCart(userId, skuId, isChecked);
return Result.ok(null);
}
@ApiOperation("全选")
@GetMapping("checkAllCart/{isChecked}")
public Result checkAllCart(@PathVariable("isChecked") Integer isChecked) {
Long userId = AuthContextHolder.getUserId();
cartInfoService.checkAllCart(userId, isChecked);
return Result.ok(null);
}
@ApiOperation("批量选中")
@PostMapping("batchCheckCart/{isChecked}")
public Result batchCheckCart(@RequestBody List<Long> skuIdList, @PathVariable("isChecked") Integer isChecked) {
Long userId = AuthContextHolder.getUserId();
cartInfoService.batchCheckCart(skuIdList, userId, isChecked);
return Result.ok(null);
}
@ApiOperation(value = "根据用户Id查询购物车列表")
@PostMapping("inner/getCartCheckedList/{userId}")
public List<CartInfo> getCartCheckedList(@PathVariable("userId") Long userId) {
return cartInfoService.getCartCheckedList(userId);
}
}

View File

@ -0,0 +1,40 @@
package com.atguigu.ssyx.cart.receiver;
import com.atguigu.ssyx.cart.service.CartInfoService;
import com.atguigu.ssyx.mq.constant.MqConst;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* ClassName: CartReceiver
* Package: com.atguigu.ssyx.cart.receiver
*
* @author yovinchen
* @Create 2023/10/13 08:56
*/
@Component
public class CartReceiver {
@Autowired
private CartInfoService cartInfoService;
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = MqConst.QUEUE_DELETE_CART, durable = "true"),
exchange = @Exchange(value = MqConst.EXCHANGE_ORDER_DIRECT),
key = {MqConst.ROUTING_DELETE_CART}
))
public void deleteCart(Long userId, Message message, Channel channel) throws IOException {
if (userId != null) {
cartInfoService.deleteCartChecked(userId);
}
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}

View File

@ -1,5 +1,7 @@
package com.atguigu.ssyx.cart.service;
import com.atguigu.ssyx.model.order.CartInfo;
import java.util.List;
/**
@ -41,4 +43,48 @@ public interface CartInfoService {
* @param userId
*/
void batchDeleteCart(List<Long> skuIdList, Long userId);
/**
* 购物车列表
*
* @param userId
* @return
*/
List<CartInfo> getCartList(Long userId);
/**
* 根据skuId选中
*
* @param userId
* @param skuId
* @param isChecked
*/
void checkCart(Long userId, Long skuId, Integer isChecked);
/**
* 全选
*
* @param userId
* @param isChecked
*/
void checkAllCart(Long userId, Integer isChecked);
/**
* 批量选中
*
* @param skuIdList
* @param userId
* @param isChecked
*/
void batchCheckCart(List<Long> skuIdList, Long userId, Integer isChecked);
/**
* 根据用户Id查询购物车列表
*
* @param userId
* @return
*/
List<CartInfo> getCartCheckedList(Long userId);
void deleteCartChecked(Long userId);
}

View File

@ -12,10 +12,14 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* ClassName: CartInfoServiceImpl
@ -53,7 +57,7 @@ public class CartInfoServiceImpl implements CartInfoService {
*/
@Override
public void addToCart(Long userId, Long skuId, Integer skuNum) {
//1 因为购物车数据存储到redis里面
//1 因为购物车数据存储到redis里面
// 从redis里面根据key获取数据这个key包含userId
String cartKey = this.getCartKey(userId);
BoundHashOperations<String, String, CartInfo> hashOperations = redisTemplate.boundHashOps(cartKey);
@ -149,8 +153,7 @@ public class CartInfoServiceImpl implements CartInfoService {
BoundHashOperations<String, String, CartInfo> hashOperations = redisTemplate.boundHashOps(cartKey);
List<CartInfo> cartInfoList = hashOperations.values();
for (CartInfo cartInfo : cartInfoList) {
hashOperations.delete(cartInfo.getSkuId()
.toString());
hashOperations.delete(cartInfo.getSkuId().toString());
}
}
@ -168,4 +171,133 @@ public class CartInfoServiceImpl implements CartInfoService {
hashOperations.delete(skuId.toString());
});
}
/**
* 购物车列表
*
* @param userId
* @return
*/
//购物车列表
@Override
public List<CartInfo> getCartList(Long userId) {
//判断userId
List<CartInfo> cartInfoList = new ArrayList<>();
if (StringUtils.isEmpty(userId)) {
return cartInfoList;
}
//从redis获取购物车数据
String cartKey = this.getCartKey(userId);
BoundHashOperations<String, String, CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);
cartInfoList = boundHashOperations.values();
if (!CollectionUtils.isEmpty(cartInfoList)) {
//根据商品添加时间降序
cartInfoList.sort((o1, o2) -> o1.getCreateTime().compareTo(o2.getCreateTime()));
}
return cartInfoList;
}
/**
* 根据skuId选中
*
* @param userId
* @param skuId
* @param isChecked
*/
@Override
public void checkCart(Long userId, Long skuId, Integer isChecked) {
//获取redis的key
String cartKey = this.getCartKey(userId);
//cartKey获取field-value
BoundHashOperations<String, String, CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);
//根据fieldskuId获取valueCartInfo
CartInfo cartInfo = boundHashOperations.get(skuId.toString());
if (cartInfo != null) {
cartInfo.setIsChecked(isChecked);
//更新
boundHashOperations.put(skuId.toString(), cartInfo);
//设置key过期时间
this.setCartKeyExpire(cartKey);
}
}
/**
* 全选
*
* @param userId
* @param isChecked
*/
@Override
public void checkAllCart(Long userId, Integer isChecked) {
String cartKey = this.getCartKey(userId);
BoundHashOperations<String, String, CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);
List<CartInfo> cartInfoList = boundHashOperations.values();
cartInfoList.forEach(cartInfo -> {
cartInfo.setIsChecked(isChecked);
boundHashOperations.put(cartInfo.getSkuId().toString(), cartInfo);
});
this.setCartKeyExpire(cartKey);
}
/**
* 批量选中
*
* @param skuIdList
* @param userId
* @param isChecked
*/
@Override
public void batchCheckCart(List<Long> skuIdList, Long userId, Integer isChecked) {
String cartKey = this.getCartKey(userId);
BoundHashOperations<String, String, CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);
skuIdList.forEach(skuId -> {
CartInfo cartInfo = boundHashOperations.get(skuId.toString());
cartInfo.setIsChecked(isChecked);
boundHashOperations.put(cartInfo.getSkuId().toString(), cartInfo);
});
this.setCartKeyExpire(cartKey);
}
/**
* 根据用户Id查询购物车列表
*
* @param userId
* @return
*/
@Override
public List<CartInfo> getCartCheckedList(Long userId) {
String cartKey = this.getCartKey(userId);
BoundHashOperations<String, String, CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);
List<CartInfo> cartInfoList = boundHashOperations.values();
//isChecked = 1购物项选中
return cartInfoList.stream().filter(cartInfo -> {
return cartInfo.getIsChecked().intValue() == 1;
}).collect(Collectors.toList());
}
/**
* 根据userId删除选中购物车记录
*
* @param userId
*/
@Override
public void deleteCartChecked(Long userId) {
//根据userid查询选中购物车记录
List<CartInfo> cartInfoList = this.getCartCheckedList(userId);
//查询list数据处理得到skuId集合
List<Long> skuIdList = cartInfoList.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
//构建redis的key值
// hash类型 key filed-value
String cartKey = this.getCartKey(userId);
//根据key查询filed-value结构
BoundHashOperations<String, String, CartInfo> hashOperations = redisTemplate.boundHashOps(cartKey);
//根据filedskuId删除redis数据
skuIdList.forEach(skuId -> {
hashOperations.delete(skuId.toString());
});
}
}

View File

@ -0,0 +1,5 @@
FROM openjdk:8-jdk-alpine
LABEL authors="yovinchen"
VOLUME /tmp
ADD ./target/service-order.jar service-order.jar
ENTRYPOINT ["java","-jar","/service-order.jar", "&"]

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.atguigu</groupId>
<artifactId>service</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>service-order</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>service-user-client</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>service-activity-client</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>service-product-client</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>rabbit_util</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>service-cart-client</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,22 @@
package com.atguigu.ssyx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* ClassName: ServiceOrderApplication
* Package: com.atguigu.ssyx
*
* @author yovinchen
* @Create 2023/10/11 19:09
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ServiceOrderApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceOrderApplication.class, args);
}
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 购物车表 前端控制器
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@RestController
@RequestMapping("/order/cart-info")
public class CartInfoController {
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 订单配送表 前端控制器
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@RestController
@RequestMapping("/order/order-deliver")
public class OrderDeliverController {
}

View File

@ -0,0 +1,57 @@
package com.atguigu.ssyx.order.controller;
import com.atguigu.ssyx.common.result.Result;
import com.atguigu.ssyx.model.order.OrderInfo;
import com.atguigu.ssyx.order.service.OrderInfoService;
import com.atguigu.ssyx.vo.order.OrderConfirmVo;
import com.atguigu.ssyx.vo.order.OrderSubmitVo;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* <p>
* 订单 前端控制器
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@RestController
@RequestMapping("/api/order")
public class OrderInfoController {
@Autowired
private OrderInfoService orderInfoService;
@ApiOperation("确认订单")
@GetMapping("auth/confirmOrder")
public Result confirm() {
OrderConfirmVo orderConfirmVo = orderInfoService.confirmOrder();
return Result.ok(orderConfirmVo);
}
@ApiOperation("生成订单")
@PostMapping("auth/submitOrder")
public Result submitOrder(@RequestBody OrderSubmitVo orderParamVo) {
Long orderId = orderInfoService.submitOrder(orderParamVo);
return Result.ok(orderId);
}
@ApiOperation("获取订单详情")
@GetMapping("auth/getOrderInfoById/{orderId}")
public Result getOrderInfoById(@PathVariable("orderId") Long orderId) {
OrderInfo orderInfo = orderInfoService.getOrderInfoById(orderId);
return Result.ok(orderInfo);
}
@ApiOperation(value = "根据orderNo查询订单信息")
@GetMapping("inner/getOrderInfo/{orderNo}")
public OrderInfo getOrderInfo(@PathVariable("orderNo") String orderNo) {
return orderInfoService.getOrderInfoByOrderNo(orderNo);
}
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 订单项信息 前端控制器
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@RestController
@RequestMapping("/order/order-item")
public class OrderItemController {
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 订单操作日志记录 前端控制器
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@RestController
@RequestMapping("/order/order-log")
public class OrderLogController {
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 订单退货申请 前端控制器
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@RestController
@RequestMapping("/order/order-return-apply")
public class OrderReturnApplyController {
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 退货原因表 前端控制器
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@RestController
@RequestMapping("/order/order-return-reason")
public class OrderReturnReasonController {
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 订单设置表 前端控制器
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@RestController
@RequestMapping("/order/order-set")
public class OrderSetController {
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 支付信息表 前端控制器
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@RestController
@RequestMapping("/order/payment-info")
public class PaymentInfoController {
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 退款信息表 前端控制器
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@RestController
@RequestMapping("/order/refund-info")
public class RefundInfoController {
}

View File

@ -0,0 +1,17 @@
package com.atguigu.ssyx.order.mapper;
import com.atguigu.ssyx.model.order.CartInfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 购物车表 Mapper 接口
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface CartInfoMapper extends BaseMapper<CartInfo> {
}

View File

@ -0,0 +1,17 @@
package com.atguigu.ssyx.order.mapper;
import com.atguigu.ssyx.model.order.OrderDeliver;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 订单配送表 Mapper 接口
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface OrderDeliverMapper extends BaseMapper<OrderDeliver> {
}

View File

@ -0,0 +1,18 @@
package com.atguigu.ssyx.order.mapper;
import com.atguigu.ssyx.model.order.OrderInfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
/**
* <p>
* 订单 Mapper 接口
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@Repository
public interface OrderInfoMapper extends BaseMapper<OrderInfo> {
}

View File

@ -0,0 +1,18 @@
package com.atguigu.ssyx.order.mapper;
import com.atguigu.ssyx.model.order.OrderItem;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
/**
* <p>
* 订单项信息 Mapper 接口
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@Repository
public interface OrderItemMapper extends BaseMapper<OrderItem> {
}

View File

@ -0,0 +1,17 @@
package com.atguigu.ssyx.order.mapper;
import com.atguigu.ssyx.model.order.OrderLog;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 订单操作日志记录 Mapper 接口
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface OrderLogMapper extends BaseMapper<OrderLog> {
}

View File

@ -0,0 +1,16 @@
package com.atguigu.ssyx.order.mapper;
import com.atguigu.ssyx.model.order.OrderReturnApply;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 订单退货申请 Mapper 接口
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface OrderReturnApplyMapper extends BaseMapper<OrderReturnApply> {
}

View File

@ -0,0 +1,16 @@
package com.atguigu.ssyx.order.mapper;
import com.atguigu.ssyx.model.order.OrderReturnReason;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 退货原因表 Mapper 接口
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface OrderReturnReasonMapper extends BaseMapper<OrderReturnReason> {
}

View File

@ -0,0 +1,16 @@
package com.atguigu.ssyx.order.mapper;
import com.atguigu.ssyx.model.order.OrderSet;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 订单设置表 Mapper 接口
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface OrderSetMapper extends BaseMapper<OrderSet> {
}

View File

@ -0,0 +1,16 @@
package com.atguigu.ssyx.order.mapper;
import com.atguigu.ssyx.model.order.PaymentInfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 支付信息表 Mapper 接口
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface PaymentInfoMapper extends BaseMapper<PaymentInfo> {
}

View File

@ -0,0 +1,16 @@
package com.atguigu.ssyx.order.mapper;
import com.atguigu.ssyx.model.order.RefundInfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 退款信息表 Mapper 接口
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface RefundInfoMapper extends BaseMapper<RefundInfo> {
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.ssyx.order.mapper.CartInfoMapper">
</mapper>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.ssyx.order.mapper.OrderDeliverMapper">
</mapper>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.ssyx.product.mapper.SkuInfoMapper">
<mapper namespace="com.atguigu.ssyx.order.mapper.OrderInfoMapper">
</mapper>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.ssyx.order.mapper.OrderItemMapper">
</mapper>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.ssyx.order.mapper.OrderLogMapper">
</mapper>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.ssyx.order.mapper.OrderReturnApplyMapper">
</mapper>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.ssyx.order.mapper.OrderReturnReasonMapper">
</mapper>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.ssyx.order.mapper.OrderSetMapper">
</mapper>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.ssyx.order.mapper.PaymentInfoMapper">
</mapper>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.ssyx.order.mapper.RefundInfoMapper">
</mapper>

View File

@ -0,0 +1,16 @@
package com.atguigu.ssyx.order.service;
import com.atguigu.ssyx.model.order.CartInfo;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 购物车表 服务类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface CartInfoService extends IService<CartInfo> {
}

View File

@ -0,0 +1,16 @@
package com.atguigu.ssyx.order.service;
import com.atguigu.ssyx.model.order.OrderDeliver;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 订单配送表 服务类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface OrderDeliverService extends IService<OrderDeliver> {
}

View File

@ -0,0 +1,48 @@
package com.atguigu.ssyx.order.service;
import com.atguigu.ssyx.model.order.OrderInfo;
import com.atguigu.ssyx.vo.order.OrderConfirmVo;
import com.atguigu.ssyx.vo.order.OrderSubmitVo;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 订单 服务类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface OrderInfoService extends IService<OrderInfo> {
/**
* 确认订单
*
* @return
*/
OrderConfirmVo confirmOrder();
/**
* 生成订单
*
* @param orderParamVo
* @return
*/
Long submitOrder(OrderSubmitVo orderParamVo);
/**
* 获取订单详情
*
* @param orderId
* @return
*/
OrderInfo getOrderInfoById(Long orderId);
/**
* 根据orderNo查询订单信息
*
* @param orderNo
* @return
*/
OrderInfo getOrderInfoByOrderNo(String orderNo);
}

View File

@ -0,0 +1,16 @@
package com.atguigu.ssyx.order.service;
import com.atguigu.ssyx.model.order.OrderItem;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 订单项信息 服务类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface OrderItemService extends IService<OrderItem> {
}

View File

@ -0,0 +1,16 @@
package com.atguigu.ssyx.order.service;
import com.atguigu.ssyx.model.order.OrderLog;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 订单操作日志记录 服务类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface OrderLogService extends IService<OrderLog> {
}

View File

@ -0,0 +1,16 @@
package com.atguigu.ssyx.order.service;
import com.atguigu.ssyx.model.order.OrderReturnApply;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 订单退货申请 服务类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface OrderReturnApplyService extends IService<OrderReturnApply> {
}

View File

@ -0,0 +1,16 @@
package com.atguigu.ssyx.order.service;
import com.atguigu.ssyx.model.order.OrderReturnReason;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 退货原因表 服务类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface OrderReturnReasonService extends IService<OrderReturnReason> {
}

View File

@ -0,0 +1,16 @@
package com.atguigu.ssyx.order.service;
import com.atguigu.ssyx.model.order.OrderSet;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 订单设置表 服务类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface OrderSetService extends IService<OrderSet> {
}

View File

@ -0,0 +1,16 @@
package com.atguigu.ssyx.order.service;
import com.atguigu.ssyx.model.order.PaymentInfo;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 支付信息表 服务类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface PaymentInfoService extends IService<PaymentInfo> {
}

View File

@ -0,0 +1,16 @@
package com.atguigu.ssyx.order.service;
import com.atguigu.ssyx.model.order.RefundInfo;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 退款信息表 服务类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
public interface RefundInfoService extends IService<RefundInfo> {
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.service.impl;
import com.atguigu.ssyx.model.order.CartInfo;
import com.atguigu.ssyx.order.mapper.CartInfoMapper;
import com.atguigu.ssyx.order.service.CartInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 购物车表 服务实现类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@Service
public class CartInfoServiceImpl extends ServiceImpl<CartInfoMapper, CartInfo> implements CartInfoService {
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.service.impl;
import com.atguigu.ssyx.model.order.OrderDeliver;
import com.atguigu.ssyx.order.mapper.OrderDeliverMapper;
import com.atguigu.ssyx.order.service.OrderDeliverService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 订单配送表 服务实现类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@Service
public class OrderDeliverServiceImpl extends ServiceImpl<OrderDeliverMapper, OrderDeliver> implements OrderDeliverService {
}

View File

@ -0,0 +1,474 @@
package com.atguigu.ssyx.order.service.impl;
import com.atguigu.ssyx.client.activity.ActivityFeignClient;
import com.atguigu.ssyx.client.cart.CartFeignClient;
import com.atguigu.ssyx.client.product.ProductFeignClient;
import com.atguigu.ssyx.client.user.UserFeignClient;
import com.atguigu.ssyx.common.auth.AuthContextHolder;
import com.atguigu.ssyx.common.constant.RedisConst;
import com.atguigu.ssyx.common.exception.SsyxException;
import com.atguigu.ssyx.common.result.ResultCodeEnum;
import com.atguigu.ssyx.common.utils.DateUtil;
import com.atguigu.ssyx.enums.*;
import com.atguigu.ssyx.model.activity.ActivityRule;
import com.atguigu.ssyx.model.activity.CouponInfo;
import com.atguigu.ssyx.model.order.CartInfo;
import com.atguigu.ssyx.model.order.OrderInfo;
import com.atguigu.ssyx.model.order.OrderItem;
import com.atguigu.ssyx.mq.constant.MqConst;
import com.atguigu.ssyx.mq.service.RabbitService;
import com.atguigu.ssyx.order.mapper.OrderInfoMapper;
import com.atguigu.ssyx.order.mapper.OrderItemMapper;
import com.atguigu.ssyx.order.service.OrderInfoService;
import com.atguigu.ssyx.order.service.OrderItemService;
import com.atguigu.ssyx.vo.order.CartInfoVo;
import com.atguigu.ssyx.vo.order.OrderConfirmVo;
import com.atguigu.ssyx.vo.order.OrderSubmitVo;
import com.atguigu.ssyx.vo.product.SkuStockLockVo;
import com.atguigu.ssyx.vo.user.LeaderAddressVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* <p>
* 订单 服务实现类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {
@Autowired
private UserFeignClient userFeignClient;
@Autowired
private CartFeignClient cartFeignClient;
@Autowired
private ActivityFeignClient activityFeignClient;
@Autowired
private ProductFeignClient productFeignClient;
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private RabbitService rabbitService;
@Autowired
private OrderItemService orderItemService;
@Autowired
private OrderItemMapper orderItemMapper;
/**
* 确认订单
*
* @return
*/
@Override
public OrderConfirmVo confirmOrder() {
//获取用户id
Long userId = AuthContextHolder.getUserId();
//获取用户对应团长信息
LeaderAddressVo leaderAddressVo = userFeignClient.getUserAddressByUserId(userId);
//获取购物车里面选中的商品
List<CartInfo> cartInfoList = cartFeignClient.getCartCheckedList(userId);
//唯一标识订单
String orderNo = System.currentTimeMillis() + "";
redisTemplate.opsForValue().set(RedisConst.ORDER_REPEAT + orderNo, orderNo, 24, TimeUnit.HOURS);
//获取购物车满足条件活动和优惠卷信息
OrderConfirmVo orderConfirmVo = activityFeignClient.findCartActivityAndCoupon(cartInfoList, userId);
//封装其他值
orderConfirmVo.setLeaderAddressVo(leaderAddressVo);
orderConfirmVo.setOrderNo(orderNo);
return orderConfirmVo;
}
/**
* 生成订单
*
* @param orderParamVo
* @return
*/
@Override
public Long submitOrder(OrderSubmitVo orderParamVo) {
//第一步 设置给哪个用户生成订单 设置orderParamVo的userId
Long userId = AuthContextHolder.getUserId();
orderParamVo.setUserId(userId);
//第二步 订单不能重复提交重复提交验证
// 通过redis + lua脚本进行判断
//// lua脚本保证原子性操作
//1 获取传递过来的订单 orderNo
String orderNo = orderParamVo.getOrderNo();
if (StringUtils.isEmpty(orderNo)) {
throw new SsyxException(ResultCodeEnum.ILLEGAL_REQUEST);
}
//2 拿着orderNo redis进行查询
String script = "if(redis.call('get', KEYS[1]) == ARGV[1]) then return redis.call('del', KEYS[1]) else return 0 end";
//3 如果redis有相同orderNo表示正常提交订单把redis的orderNo删除
Boolean flag = (Boolean) redisTemplate.execute(new DefaultRedisScript(script, Boolean.class), Collections.singletonList(RedisConst.ORDER_REPEAT + orderNo), orderNo);
//4 如果redis没有相同orderNo表示重复提交了不能再往后进行
if (Boolean.FALSE.equals(flag)) {
throw new SsyxException(ResultCodeEnum.REPEAT_SUBMIT);
}
//第三步 验证库存 并且 锁定库存
// 比如仓库有10个西红柿我想买2个西红柿
// ** 验证库存查询仓库里面是是否有充足西红柿
// ** 库存充足库存锁定 2锁定目前没有真正减库存
//1远程调用service-cart模块获取当前用户购物车商品选中的购物项
List<CartInfo> cartInfoList = cartFeignClient.getCartCheckedList(userId);
//2购物车有很多商品商品不同类型重点处理普通类型商品
List<CartInfo> commonSkuList = cartInfoList.stream().filter(cartInfo -> Objects.equals(cartInfo.getSkuType(), SkuType.COMMON.getCode())).collect(Collectors.toList());
//3把获取购物车里面普通类型商品list集合
// List<CartInfo>转换List<SkuStockLockVo>
if (!CollectionUtils.isEmpty(commonSkuList)) {
List<SkuStockLockVo> commonStockLockVoList = commonSkuList.stream().map(item -> {
SkuStockLockVo skuStockLockVo = new SkuStockLockVo();
skuStockLockVo.setSkuId(item.getSkuId());
skuStockLockVo.setSkuNum(item.getSkuNum());
return skuStockLockVo;
}).collect(Collectors.toList());
//4远程调用service-product模块实现锁定商品
//// 验证库存并锁定库存保证具备原子性
Boolean isLockSuccess = productFeignClient.checkAndLock(commonStockLockVoList, orderNo);
if (!isLockSuccess) {//库存锁定失败
throw new SsyxException(ResultCodeEnum.ORDER_STOCK_FALL);
}
}
//第四步 下单过程
//1 向两张表添加数据
// order_info order_item
Long orderId = this.saveOrder(orderParamVo, cartInfoList);
//下单完成删除购物车记录
//发送mq消息
rabbitService.sendMessage(MqConst.EXCHANGE_ORDER_DIRECT, MqConst.ROUTING_DELETE_CART, orderParamVo.getUserId());
//第五步 返回订单id
return orderId;
}
//下单过程向两张表添加数据
private Long saveOrder(OrderSubmitVo orderParamVo, List<CartInfo> cartInfoList) {
if (CollectionUtils.isEmpty(cartInfoList)) {
throw new SsyxException(ResultCodeEnum.DATA_ERROR);
}
//查询用户提货点和团长信息
Long userId = AuthContextHolder.getUserId();
LeaderAddressVo leaderAddressVo = userFeignClient.getUserAddressByUserId(userId);
if (leaderAddressVo == null) {
throw new SsyxException(ResultCodeEnum.DATA_ERROR);
}
//计算金额
//营销活动金额
Map<String, BigDecimal> activitySplitAmount = this.computeActivitySplitAmount(cartInfoList);
//优惠卷金额
Map<String, BigDecimal> couponInfoSplitAmount = this.computeCouponInfoSplitAmount(cartInfoList, orderParamVo.getCouponId());
//封装订单项数据
List<OrderItem> orderItemList = new ArrayList<>();
for (CartInfo cartInfo : cartInfoList) {
OrderItem orderItem = new OrderItem();
orderItem.setId(null);
orderItem.setCategoryId(cartInfo.getCategoryId());
if (cartInfo.getSkuType() == SkuType.COMMON.getCode()) {
orderItem.setSkuType(SkuType.COMMON);
} else {
orderItem.setSkuType(SkuType.SECKILL);
}
orderItem.setSkuId(cartInfo.getSkuId());
orderItem.setSkuName(cartInfo.getSkuName());
orderItem.setSkuPrice(cartInfo.getCartPrice());
orderItem.setImgUrl(cartInfo.getImgUrl());
orderItem.setSkuNum(cartInfo.getSkuNum());
orderItem.setLeaderId(orderParamVo.getLeaderId());
//营销活动金额
BigDecimal activityAmount = activitySplitAmount.get("activity:" + orderItem.getSkuId());
if (activityAmount == null) {
activityAmount = new BigDecimal(0);
}
orderItem.setSplitActivityAmount(activityAmount);
//优惠卷金额
BigDecimal couponAmount = couponInfoSplitAmount.get("coupon:" + orderItem.getSkuId());
if (couponAmount == null) {
couponAmount = new BigDecimal(0);
}
orderItem.setSplitCouponAmount(couponAmount);
//总金额
BigDecimal skuTotalAmount = orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum()));
//优惠之后金额
BigDecimal splitTotalAmount = skuTotalAmount.subtract(activityAmount).subtract(couponAmount);
orderItem.setSplitTotalAmount(splitTotalAmount);
orderItemList.add(orderItem);
}
//封装订单OrderInfo数据
OrderInfo orderInfo = new OrderInfo();
orderInfo.setUserId(userId);//用户id
orderInfo.setOrderNo(orderParamVo.getOrderNo()); //订单号 唯一标识
orderInfo.setOrderStatus(OrderStatus.UNPAID); //订单状态生成成功未支付
orderInfo.setLeaderId(orderParamVo.getLeaderId());//团长id
orderInfo.setLeaderName(leaderAddressVo.getLeaderName());//团长名称
orderInfo.setLeaderPhone(leaderAddressVo.getLeaderPhone());
orderInfo.setTakeName(leaderAddressVo.getTakeName());
orderInfo.setReceiverName(orderParamVo.getReceiverName());
orderInfo.setReceiverPhone(orderParamVo.getReceiverPhone());
orderInfo.setReceiverProvince(leaderAddressVo.getProvince());
orderInfo.setReceiverCity(leaderAddressVo.getCity());
orderInfo.setReceiverDistrict(leaderAddressVo.getDistrict());
orderInfo.setReceiverAddress(leaderAddressVo.getDetailAddress());
orderInfo.setWareId(cartInfoList.get(0).getWareId());
orderInfo.setProcessStatus(ProcessStatus.UNPAID);
//计算订单金额
BigDecimal originalTotalAmount = this.computeTotalAmount(cartInfoList);
BigDecimal activityAmount = activitySplitAmount.get("activity:total");
if (null == activityAmount) activityAmount = new BigDecimal(0);
BigDecimal couponAmount = couponInfoSplitAmount.get("coupon:total");
if (null == couponAmount) couponAmount = new BigDecimal(0);
BigDecimal totalAmount = originalTotalAmount.subtract(activityAmount).subtract(couponAmount);
//计算订单金额
orderInfo.setOriginalTotalAmount(originalTotalAmount);
orderInfo.setActivityAmount(activityAmount);
orderInfo.setCouponAmount(couponAmount);
orderInfo.setTotalAmount(totalAmount);
//计算团长佣金
BigDecimal profitRate = new BigDecimal(0);//orderSetService.getProfitRate();
BigDecimal commissionAmount = orderInfo.getTotalAmount().multiply(profitRate);
orderInfo.setCommissionAmount(commissionAmount);
//添加数据到订单基本信息表
baseMapper.insert(orderInfo);
//添加订单里面订单项
orderItemList.forEach(orderItem -> {
orderItem.setOrderId(orderInfo.getId());
orderItemService.save(orderItem);
});
//如果当前订单使用优惠卷更新优惠卷状态
if (orderInfo.getCouponId() != null) {
activityFeignClient.updateCouponInfoUseStatus(orderInfo.getCouponId(), userId, orderInfo.getId());
}
//下单成功记录用户购物商品数量redis
//hash类型 key(userId) - field(skuId)-value(skuNum)
String orderSkuKey = RedisConst.ORDER_SKU_MAP + orderParamVo.getUserId();
BoundHashOperations<String, String, Integer> hashOperations = redisTemplate.boundHashOps(orderSkuKey);
cartInfoList.forEach(cartInfo -> {
if (Boolean.TRUE.equals(hashOperations.hasKey(cartInfo.getSkuId().toString()))) {
Integer orderSkuNum = hashOperations.get(cartInfo.getSkuId().toString()) + cartInfo.getSkuNum();
hashOperations.put(cartInfo.getSkuId().toString(), orderSkuNum);
}
});
redisTemplate.expire(orderSkuKey, DateUtil.getCurrentExpireTimes(), TimeUnit.SECONDS);
//订单id
return orderInfo.getId();
}
/**
* 获取订单详情
*
* @param orderId
* @return
*/
@Override
public OrderInfo getOrderInfoById(Long orderId) {
//根据orderId查询订单基本信息
OrderInfo orderInfo = baseMapper.selectById(orderId);
//根据orderId查询订单所有订单项list列表
List<OrderItem> orderItemList = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItem>().eq(OrderItem::getOrderId, orderInfo.getId()));
//查询所有订单项封装到每个订单对象里面
orderInfo.setOrderItemList(orderItemList);
return orderInfo;
}
/**
* 根据orderNo查询订单信息
*
* @param orderNo
* @return
*/
@Override
public OrderInfo getOrderInfoByOrderNo(String orderNo) {
return null;
}
//计算总金额
private BigDecimal computeTotalAmount(List<CartInfo> cartInfoList) {
BigDecimal total = new BigDecimal(0);
for (CartInfo cartInfo : cartInfoList) {
BigDecimal itemTotal = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
total = total.add(itemTotal);
}
return total;
}
/**
* 计算购物项分摊的优惠减少金额
* 打折按折扣分担
* 现金按比例分摊
*
* @param cartInfoParamList
* @return
*/
private Map<String, BigDecimal> computeActivitySplitAmount(List<CartInfo> cartInfoParamList) {
Map<String, BigDecimal> activitySplitAmountMap = new HashMap<>();
//促销活动相关信息
List<CartInfoVo> cartInfoVoList = activityFeignClient.findCartActivityList(cartInfoParamList);
//活动总金额
BigDecimal activityReduceAmount = new BigDecimal(0);
if (!CollectionUtils.isEmpty(cartInfoVoList)) {
for (CartInfoVo cartInfoVo : cartInfoVoList) {
ActivityRule activityRule = cartInfoVo.getActivityRule();
List<CartInfo> cartInfoList = cartInfoVo.getCartInfoList();
if (null != activityRule) {
//优惠金额 按比例分摊
BigDecimal reduceAmount = activityRule.getReduceAmount();
activityReduceAmount = activityReduceAmount.add(reduceAmount);
if (cartInfoList.size() == 1) {
activitySplitAmountMap.put("activity:" + cartInfoList.get(0).getSkuId(), reduceAmount);
} else {
//总金额
BigDecimal originalTotalAmount = new BigDecimal(0);
for (CartInfo cartInfo : cartInfoList) {
BigDecimal skuTotalAmount = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
originalTotalAmount = originalTotalAmount.add(skuTotalAmount);
}
//记录除最后一项是所有分摊金额 最后一项=总的 - skuPartReduceAmount
BigDecimal skuPartReduceAmount = new BigDecimal(0);
if (activityRule.getActivityType() == ActivityType.FULL_REDUCTION) {
for (int i = 0, len = cartInfoList.size(); i < len; i++) {
CartInfo cartInfo = cartInfoList.get(i);
if (i < len - 1) {
BigDecimal skuTotalAmount = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
//sku分摊金额
BigDecimal skuReduceAmount = skuTotalAmount.divide(originalTotalAmount, 2, RoundingMode.HALF_UP).multiply(reduceAmount);
activitySplitAmountMap.put("activity:" + cartInfo.getSkuId(), skuReduceAmount);
skuPartReduceAmount = skuPartReduceAmount.add(skuReduceAmount);
} else {
BigDecimal skuReduceAmount = reduceAmount.subtract(skuPartReduceAmount);
activitySplitAmountMap.put("activity:" + cartInfo.getSkuId(), skuReduceAmount);
}
}
} else {
for (int i = 0, len = cartInfoList.size(); i < len; i++) {
CartInfo cartInfo = cartInfoList.get(i);
if (i < len - 1) {
BigDecimal skuTotalAmount = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
//sku分摊金额
BigDecimal skuDiscountTotalAmount = skuTotalAmount.multiply(activityRule.getBenefitDiscount().divide(new BigDecimal("10")));
BigDecimal skuReduceAmount = skuTotalAmount.subtract(skuDiscountTotalAmount);
activitySplitAmountMap.put("activity:" + cartInfo.getSkuId(), skuReduceAmount);
skuPartReduceAmount = skuPartReduceAmount.add(skuReduceAmount);
} else {
BigDecimal skuReduceAmount = reduceAmount.subtract(skuPartReduceAmount);
activitySplitAmountMap.put("activity:" + cartInfo.getSkuId(), skuReduceAmount);
}
}
}
}
}
}
}
activitySplitAmountMap.put("activity:total", activityReduceAmount);
return activitySplitAmountMap;
}
//优惠卷优惠金额
private Map<String, BigDecimal> computeCouponInfoSplitAmount(List<CartInfo> cartInfoList, Long couponId) {
Map<String, BigDecimal> couponInfoSplitAmountMap = new HashMap<>();
if (null == couponId) return couponInfoSplitAmountMap;
CouponInfo couponInfo = activityFeignClient.findRangeSkuIdList(cartInfoList, couponId);
if (null != couponInfo) {
//sku对应的订单明细
Map<Long, CartInfo> skuIdToCartInfoMap = new HashMap<>();
for (CartInfo cartInfo : cartInfoList) {
skuIdToCartInfoMap.put(cartInfo.getSkuId(), cartInfo);
}
//优惠券对应的skuId列表
List<Long> skuIdList = couponInfo.getSkuIdList();
if (CollectionUtils.isEmpty(skuIdList)) {
return couponInfoSplitAmountMap;
}
//优惠券优化总金额
BigDecimal reduceAmount = couponInfo.getAmount();
if (skuIdList.size() == 1) {
//sku的优化金额
couponInfoSplitAmountMap.put("coupon:" + skuIdToCartInfoMap.get(skuIdList.get(0)).getSkuId(), reduceAmount);
} else {
//总金额
BigDecimal originalTotalAmount = new BigDecimal(0);
for (Long skuId : skuIdList) {
CartInfo cartInfo = skuIdToCartInfoMap.get(skuId);
BigDecimal skuTotalAmount = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
originalTotalAmount = originalTotalAmount.add(skuTotalAmount);
}
//记录除最后一项是所有分摊金额 最后一项=总的 - skuPartReduceAmount
BigDecimal skuPartReduceAmount = new BigDecimal(0);
if (couponInfo.getCouponType() == CouponType.CASH || couponInfo.getCouponType() == CouponType.FULL_REDUCTION) {
for (int i = 0, len = skuIdList.size(); i < len; i++) {
CartInfo cartInfo = skuIdToCartInfoMap.get(skuIdList.get(i));
if (i < len - 1) {
BigDecimal skuTotalAmount = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
//sku分摊金额
BigDecimal skuReduceAmount = skuTotalAmount.divide(originalTotalAmount, 2, RoundingMode.HALF_UP).multiply(reduceAmount);
couponInfoSplitAmountMap.put("coupon:" + cartInfo.getSkuId(), skuReduceAmount);
skuPartReduceAmount = skuPartReduceAmount.add(skuReduceAmount);
} else {
BigDecimal skuReduceAmount = reduceAmount.subtract(skuPartReduceAmount);
couponInfoSplitAmountMap.put("coupon:" + cartInfo.getSkuId(), skuReduceAmount);
}
}
}
}
couponInfoSplitAmountMap.put("coupon:total", couponInfo.getAmount());
}
return couponInfoSplitAmountMap;
}
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.service.impl;
import com.atguigu.ssyx.model.order.OrderItem;
import com.atguigu.ssyx.order.mapper.OrderItemMapper;
import com.atguigu.ssyx.order.service.OrderItemService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 订单项信息 服务实现类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@Service
public class OrderItemServiceImpl extends ServiceImpl<OrderItemMapper, OrderItem> implements OrderItemService {
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.service.impl;
import com.atguigu.ssyx.model.order.OrderLog;
import com.atguigu.ssyx.order.mapper.OrderLogMapper;
import com.atguigu.ssyx.order.service.OrderLogService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 订单操作日志记录 服务实现类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@Service
public class OrderLogServiceImpl extends ServiceImpl<OrderLogMapper, OrderLog> implements OrderLogService {
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.service.impl;
import com.atguigu.ssyx.model.order.OrderReturnApply;
import com.atguigu.ssyx.order.mapper.OrderReturnApplyMapper;
import com.atguigu.ssyx.order.service.OrderReturnApplyService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 订单退货申请 服务实现类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@Service
public class OrderReturnApplyServiceImpl extends ServiceImpl<OrderReturnApplyMapper, OrderReturnApply> implements OrderReturnApplyService {
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.service.impl;
import com.atguigu.ssyx.model.order.OrderReturnReason;
import com.atguigu.ssyx.order.mapper.OrderReturnReasonMapper;
import com.atguigu.ssyx.order.service.OrderReturnReasonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 退货原因表 服务实现类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@Service
public class OrderReturnReasonServiceImpl extends ServiceImpl<OrderReturnReasonMapper, OrderReturnReason> implements OrderReturnReasonService {
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.service.impl;
import com.atguigu.ssyx.model.order.OrderSet;
import com.atguigu.ssyx.order.mapper.OrderSetMapper;
import com.atguigu.ssyx.order.service.OrderSetService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 订单设置表 服务实现类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@Service
public class OrderSetServiceImpl extends ServiceImpl<OrderSetMapper, OrderSet> implements OrderSetService {
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.service.impl;
import com.atguigu.ssyx.model.order.PaymentInfo;
import com.atguigu.ssyx.order.mapper.PaymentInfoMapper;
import com.atguigu.ssyx.order.service.PaymentInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 支付信息表 服务实现类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@Service
public class PaymentInfoServiceImpl extends ServiceImpl<PaymentInfoMapper, PaymentInfo> implements PaymentInfoService {
}

View File

@ -0,0 +1,20 @@
package com.atguigu.ssyx.order.service.impl;
import com.atguigu.ssyx.model.order.RefundInfo;
import com.atguigu.ssyx.order.mapper.RefundInfoMapper;
import com.atguigu.ssyx.order.service.RefundInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 退款信息表 服务实现类
* </p>
*
* @author atguigu
* @since 2023-10-12
*/
@Service
public class RefundInfoServiceImpl extends ServiceImpl<RefundInfoMapper, RefundInfo> implements RefundInfoService {
}

View File

@ -0,0 +1,52 @@
server:
port: 8209
mybatis-plus:
type-enums-package: com.atguigu.ssyx.enums
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:mapper/*.xml
feign:
sentinel:
enabled: true
client:
config:
default: #配置全局的feign的调用超时时间 如果 有指定的服务配置 默认的配置不会生效
connectTimeout: 30000 # 指定的是 消费者 连接服务提供者的连接超时时间 是否能连接 单位是毫秒
readTimeout: 50000 # 指定的是调用服务提供者的 服务 的超时时间() 单位是毫秒
spring:
main:
allow-bean-definition-overriding: true #当遇到同样名字的时候,是否允许覆盖注册
rabbitmq:
host: 43.143.164.194
port: 5672
username: guest
password: guest
redis:
host: 82.157.68.223
port: 6379
database: 0
timeout: 1800000
password:
lettuce:
pool:
max-active: 20 #最大连接数
max-wait: -1 #最大阻塞等待时间(负数表示没限制)
max-idle: 5 #最大空闲
min-idle: 0 #最小空闲
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://82.157.68.223:3306/shequ-order?characterEncoding=utf-8&useSSL=false
username: shequ-order
password: shequ-order
hikari:
connection-test-query: SELECT 1
connection-timeout: 60000
idle-timeout: 500000
max-lifetime: 540000
maximum-pool-size: 5
minimum-idle: 3
pool-name: GuliHikariPool
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8

View File

@ -0,0 +1,11 @@
spring:
application:
name: service-order
profiles:
active: dev
cloud:
nacos:
discovery:
server-addr: 82.157.68.223:8848
username: nacos
password: nacos

View File

@ -5,6 +5,7 @@ import com.atguigu.ssyx.model.product.SkuInfo;
import com.atguigu.ssyx.product.service.CategoryService;
import com.atguigu.ssyx.product.service.SkuInfoService;
import com.atguigu.ssyx.vo.product.SkuInfoVo;
import com.atguigu.ssyx.vo.product.SkuStockLockVo;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@ -75,4 +76,11 @@ public class ProductInnnerController {
public SkuInfoVo getSkuInfoVo(@PathVariable Long skuId) {
return skuInfoService.getSkuInfoVo(skuId);
}
@ApiOperation(value = "验证和锁定库存")
@PostMapping("inner/checkAndLock/{orderNo}")
public Boolean checkAndLock(@RequestBody List<SkuStockLockVo> skuStockLockVoList,
@PathVariable String orderNo) {
return skuInfoService.checkAndLock(skuStockLockVoList, orderNo);
}
}

View File

@ -2,6 +2,7 @@ package com.atguigu.ssyx.product.mapper;
import com.atguigu.ssyx.model.product.SkuInfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
/**
* <p>
@ -13,4 +14,29 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
*/
public interface SkuInfoMapper extends BaseMapper<SkuInfo> {
/**
* 验证库存
*
* @param skuId
* @param skuNum
* @return
*/
SkuInfo checkStock(@Param("skuId") Long skuId, @Param("skuNum") Integer skuNum);
/**
* 锁订库存
*
* @param skuId
* @param skuNum
* @return
*/
Integer lockStock(@Param("skuId") Long skuId, @Param("skuNum") Integer skuNum);
/**
* 解锁库存
*
* @param skuId
* @param skuNum
*/
void unlockStock(@Param("skuId") Long skuId, @Param("skuNum") Integer skuNum);
}

View File

@ -3,6 +3,7 @@ package com.atguigu.ssyx.product.service;
import com.atguigu.ssyx.model.product.SkuInfo;
import com.atguigu.ssyx.vo.product.SkuInfoQueryVo;
import com.atguigu.ssyx.vo.product.SkuInfoVo;
import com.atguigu.ssyx.vo.product.SkuStockLockVo;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
@ -105,4 +106,13 @@ public interface SkuInfoService extends IService<SkuInfo> {
* @return
*/
List<SkuInfo> findNewPersonSkuInfoList();
/**
* 验证和锁定库存
*
* @param skuStockLockVoList
* @param orderNo
* @return
*/
Boolean checkAndLock(List<SkuStockLockVo> skuStockLockVoList, String orderNo);
}

View File

@ -1,5 +1,8 @@
package com.atguigu.ssyx.product.service.impl;
import com.atguigu.ssyx.common.constant.RedisConst;
import com.atguigu.ssyx.common.exception.SsyxException;
import com.atguigu.ssyx.common.result.ResultCodeEnum;
import com.atguigu.ssyx.model.product.SkuAttrValue;
import com.atguigu.ssyx.model.product.SkuImage;
import com.atguigu.ssyx.model.product.SkuInfo;
@ -13,12 +16,16 @@ import com.atguigu.ssyx.product.service.SkuInfoService;
import com.atguigu.ssyx.product.service.SkuPosterService;
import com.atguigu.ssyx.vo.product.SkuInfoQueryVo;
import com.atguigu.ssyx.vo.product.SkuInfoVo;
import com.atguigu.ssyx.vo.product.SkuStockLockVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
@ -46,6 +53,12 @@ public class SkuInfoServiceImpl extends ServiceImpl<SkuInfoMapper, SkuInfo> impl
@Autowired
private RabbitService rabbitService;
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private RedissonClient redissonClient;
/**
* 获取sku分页列表
*
@ -134,6 +147,7 @@ public class SkuInfoServiceImpl extends ServiceImpl<SkuInfoMapper, SkuInfo> impl
/**
* 修改商品sku信息
*
* @param skuInfoVo
*/
@Override
@ -304,6 +318,75 @@ public class SkuInfoServiceImpl extends ServiceImpl<SkuInfoMapper, SkuInfo> impl
return skuInfoPage.getRecords();
}
/**
* 验证和锁定库存
*
* @param skuStockLockVoList
* @param orderNo
* @return
*/
@Override
public Boolean checkAndLock(List<SkuStockLockVo> skuStockLockVoList, String orderNo) {
//1 判断skuStockLockVoList集合是否为空
if (CollectionUtils.isEmpty(skuStockLockVoList)) {
throw new SsyxException(ResultCodeEnum.DATA_ERROR);
}
//2 遍历skuStockLockVoList得到每个商品验证库存并锁定库存具备原子性
skuStockLockVoList.stream()
.forEach(skuStockLockVo -> {
this.checkLock(skuStockLockVo);
});
//3 只要有一个商品锁定失败所有锁定成功的商品都解锁
boolean flag = skuStockLockVoList.stream()
.anyMatch(skuStockLockVo -> !skuStockLockVo.getIsLock());
if (flag) {
//所有锁定成功的商品都解锁
skuStockLockVoList.stream()
.filter(SkuStockLockVo::getIsLock)
.forEach(skuStockLockVo -> {
baseMapper.unlockStock(skuStockLockVo.getSkuId(), skuStockLockVo.getSkuNum());
});
//返回失败的状态
return false;
}
//4 如果所有商品都锁定成功了redis缓存相关数据为了方便后面解锁和减库存
redisTemplate.opsForValue()
.set(RedisConst.SROCK_INFO + orderNo, skuStockLockVoList);
return true;
}
//2 遍历skuStockLockVoList得到每个商品验证库存并锁定库存具备原子性
private void checkLock(SkuStockLockVo skuStockLockVo) {
//获取锁
//公平锁
RLock rLock = this.redissonClient.getFairLock(RedisConst.SKUKEY_PREFIX + skuStockLockVo.getSkuId());
//加锁
rLock.lock();
try {
//验证库存
SkuInfo skuInfo = baseMapper.checkStock(skuStockLockVo.getSkuId(), skuStockLockVo.getSkuNum());
//判断没有满足条件商品设置isLock值false返回
if (skuInfo == null) {
skuStockLockVo.setIsLock(false);
return;
}
//有满足条件商品
//锁定库存:update
Integer rows = baseMapper.lockStock(skuStockLockVo.getSkuId(), skuStockLockVo.getSkuNum());
if (rows == 1) {
skuStockLockVo.setIsLock(true);
}
} finally {
//解锁
rLock.unlock();
}
}
//获取商品sku信息
private SkuInfoVo getSkuInfoDB(Long skuId) {
SkuInfoVo skuInfoVo = new SkuInfoVo();

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.ssyx.product.mapper.SkuInfoMapper">
<resultMap id="skuInfoMap" type="com.atguigu.ssyx.model.product.SkuInfo" autoMapping="true"></resultMap>
<!--//验证库存-->
<select id="checkStock" resultMap="skuInfoMap">
select id,
category_id,
sku_type,
sku_name,
img_url,
per_limit,
publish_status,
check_status,
is_new_person,
sort,
sku_code,
price,
market_price,
stock,
lock_stock,
low_stock,
sale,
ware_id,
create_time,
update_time,
is_deleted
from sku_info
where id = #{skuId}
and stock - lock_stock > #{skuNum} for
update
</select>
<!--//锁定库存:update-->
<update id="lockStock">
update sku_info
set lock_stock = lock_stock + #{skuNum}
where id = #{skuId}
</update>
<!--//解锁库存-->
<update id="unlockStock">
update sku_info
set lock_stock = lock_stock - #{skuNum}
where id = #{skuId}
</update>
<!--减库存-->
<update id="minusStock">
update sku_info
set stock = stock - #{skuNum},
lock_stock = lock_stock - #{skuNum},
sale = sale + #{skuNum}
where id = #{skuId}
</update>
</mapper>

View File

@ -24,7 +24,18 @@ spring:
prefetch: 1
concurrency: 3
acknowledge-mode: manual #消费端手动确认
redis:
host: 82.157.68.223
port: 6379
database: 0
timeout: 1800000
password:
lettuce:
pool:
max-active: 20 #最大连接数
max-wait: -1 #最大阻塞等待时间(负数表示没限制)
max-idle: 5 #最大空闲
min-idle: 0 #最小空闲
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8

View File

@ -12,7 +12,18 @@ spring:
url: jdbc:mysql://82.157.68.223:3306/shequ-sys?characterEncoding=utf-8&useSSL=false
username: shequ-sys
password: shequ-sys
redis:
host: 82.157.68.223
port: 6379
database: 0
timeout: 1800000
password:
lettuce:
pool:
max-active: 20 #最大连接数
max-wait: -1 #最大阻塞等待时间(负数表示没限制)
max-idle: 5 #最大空闲
min-idle: 0 #最小空闲
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8