redis
-
keys *
-
flushdb
-
键值对的健是string类型才可以获取到.
-
HashMap是一个key:value对象
-
set是一个数组对象.
-
HashMap的entrySet
Iterator<Map.Entry<Long, Set<Long>>> iterator = activityIdToSkuIdListMap.entrySet().iterator();
// 是哟nentry使用为可以遍历,在HashMap是不可以遍历的,只能通过GetKey()获得value.
List<CartInfo> currentActivityCartInfoList = null;
while (iterator.hasNext()) {
Map.Entry<Long, Set<Long>> next = iterator.next();
// key
Long activityId = next.getKey();
// value
Set<Long> value = next.getValue();
//一个是购物车数据 一个是有活动的数据.
currentActivityCartInfoList = cartInfoList.stream().filter(cartInfo -> value.contains(cartInfo.getSkuId()))
.collect(Collectors.toList());
}
//
import lombok.Data;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
public class demo1 {
public static void main(String[] args) {
List<User> list = new ArrayList();
User user = new User(new BigDecimal(100));
User user1 = new User(new BigDecimal(200));
list.add(user);
list.add(user1);
BigDecimal bigDecimal = list.stream().map(User::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println(bigDecimal);
}
}
@Data
class User {
private BigDecimal money;
public User(BigDecimal money) {
this.money = money;
}
public BigDecimal getMoney() {
return this.money;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
}
BigDecimal reduceAmount = cartActivityList.stream().filter(item -> item.getActivityRule() != null)
.map(cartActivity -> cartActivity.getActivityRule().getReduceAmount())
.reduce(BigDecimal.ZERO, BigDecimal::add);
Map<Long, CartInfo> ShopcartIdToInfoMap = null;
// 获得所有的购物车数据
if (!CollectionUtils.isEmpty(collect)) {
ShopcartIdToInfoMap = cartInfoList.stream().collect(Collectors.toMap(CartInfo::getSkuId, CartInfo -> CartInfo));
}
map的需要collect(Collectors.toMap()).
优惠券
- 检查库存(判断),之后锁定库存。
- 两个请求都检查了库存,都检查为1,之后一起执行操作。导致库存直接变为-1,导致超卖。
怎么解决?加锁。
- 可是加锁之后。只是对应着系统层面而已。系统一多就不起作用了。
- 所以使用分布式锁。解决超卖问题。
springboot配置类。
- 分布式锁会把检查库存和锁定库存和更新库存.加分布式锁.
- 分布式锁比锁可以适用更多系统集群上,
- 加锁就是在操作购物车的数据会加上锁.在操作这三个步骤的时候,别的请求操作不了.
redission
在产品列表的数据库有,stock和lock_stock(锁定库存)两个字段,我们锁定库存数量写道lock_stock中的。
订单
- 确认订单 --》 生成订单 --》
1.1 确认订单生成订单号,存储到redis中。
1.2 查询地址信息。和优惠券信息。
2.1 订单号查重
2.2 验证库存
优惠券
优惠券_info:有三种类型,sku和分类和通用.任选一种.
优惠券规则:有多个可以是多个商品或者是多个分类.或者是一个分类.
优惠券info与规则是一对多关系.
private Map<Long, List<Long>> findCouponIdToSkuIdMap(List<CartInfo> cartInfoList, List<CouponRange> couponRangesList) {
Map<Long, List<Long>> couponIdToSkuIdMap = new HashMap<>();
// 根据优惠券id分组优惠券range信息.
Map<Long, List<CouponRange>> couponRangeGroupList = couponRangesList.stream().collect(Collectors.groupingBy(CouponRange::getCouponId));
Iterator<Map.Entry<Long, List<CouponRange>>> iterator = couponRangeGroupList.entrySet().iterator();
// 优惠券分组. value是优惠券的信息(类型和包含种类)
// Map 优惠券id -- 优惠券list规则。
while (iterator.hasNext()) {
Map.Entry<Long, List<CouponRange>> entry = iterator.next();
// 相当于给mao一个迭代指针.
Long couponId = entry.getKey();
List<CouponRange> couponRangeList = entry.getValue();
//把有优惠券的加入集合.
Set<Long> skuIdSet = new HashSet<>();
// 遍历每一个商品
for (CartInfo cartInfo : cartInfoList) {
// 遍历每个优惠规则。
for (CouponRange couponRange : couponRangeList) {
if (CouponRangeType.SKU == couponRange.getRangeType()
// 优惠券是sku的
&& couponRange.getRangeId().longValue() == cartInfo.getSkuId().intValue()) {
skuIdSet.add(cartInfo.getSkuId());
} else if (CouponRangeType.CATEGORY == couponRange.getRangeType()
&& couponRange.getRangeId().longValue() == cartInfo.getCategoryId().intValue()) {
// 优惠券是分类券。
skuIdSet.add(cartInfo.getSkuId());
} else {
}
}
}
// 这是优惠券id对应的有优惠的skuId集合.
// 这个优惠券可以用在这些商品上.已经把没有的排除掉了.
couponIdToSkuIdMap.put(couponId, new ArrayList<>(skuIdSet));
// 收集到了这张优惠券info可以用的商品.
}
return couponIdToSkuIdMap;
}
- 得到每个优惠券可以使用的商品
- 之后可以对比每个优惠券的优惠力度. 选择最优惠的优惠券.
- 只有得到这个优惠券info可以使用的购物商品的集合.实现了优惠券与商品的对应关系.
CouponInfo couponInfo = baseMapper.selectById(couponId);
couponInfo.setSkuIdList(value);
- 在生成订单的时候应该保存好最优优惠券的id及其相关信息.之后确认订单需要重新计算金额.
分摊优惠:
商品A的分摊优惠:10元(100/180 * 20)
商品B的分摊优惠:8元(80/180 * 20)
实际支付:商品A = 90元,商品B = 72元,总计162元。
这样,消费者可以清楚地看到每个商品的实际价格和节省的金额。
总之,优惠分摊提供了一种更细致、透明和公平的方式来处理优惠,有助于提高消费者信任,符合会计和税务要求,并为商家提供更准确的销售数据。
- 订单项的金额相加和优惠金额相加 , 还有订单id关联订单项目order_id.
订单问题需要事务处理,如果订单项有一个插入失败,那么都失败.
@EnableTransactionManagement
public class MybatisPlusConfig {
/**
@Transactional(rollbackFor = {Exception.class})
public Long saveOrder(OrderSubmitVo orderParamVo, List<CartInfo> cartInfoList) {
订单确认—> 生成订单 --> 订单详情.
- 订单和订单项目,订单项目需要根据活动和优惠进行减少订单项目的金额。
- 订单需要一共减少了多少金额,填写信息。之后更新优惠券使用信息。
- 订单项目放在redis中
- 生成订单之后要删除购物车部分。
- MQ是使用在模块的之间的,如果一个模块的请求的数据或者操作不是立马使用的话(feign),就可以使用mq,因为订单模块删除购物车数据,不是订单模块立马需要的,所以使用mq.
Rabbit
- 使用RabbitServic文件,有交换机和路由器和信息参数。需要在yml配置文件设置好mq的链接账号密码。
- rabbit有队列和交换机和路由器.
发送端
rabbitService.sendMessage(MqConst.EXCHANGE_ORDER_DIRECT, MqConst.ROUTING_DELETE_CART, orderParamVo.getUserId());
package com.atguigu.ssyx.mq.service;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
//发送消息
@Service
public class RabbirService {
@Autowired
private RabbitTemplate rabbitTemlate;
/**
*
* @param exchange 交换机
* @param routingKey 队列名字key
* @param message 消息
* @return
*/
public boolean sendMessage(String exchange,String routingKey,Object message){
rabbitTemlate.convertAndSend(exchange,routingKey,message);
return true;
}
}
- 这是mq的通用模块文件.
接收端
- 在接收模块写一个receive文件
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;
@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) {
return;
}
cartInfoService.deleteCartCheck(userId);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}
- value是队列 exchange是一个交换机 key是路由器.
- 拿到数据之后开始发给service层.