苍穹外卖项目日记(day11)

发布于:2025-07-18 ⋅ 阅读:(21) ⋅ 点赞:(0)

苍穹外卖|项目日记(day11)

​ 前言: 今天的任务不算难, 但如何把代码接口写的像老师那样优雅才是难度.
在这里插入图片描述

今日收获:

1.欣赏了"雅"代码的写法(stram流, 封装查询)

2.加深了对动态sql的使用(group by, order by, limit)

一.stream流的使用

1. Stream的基本概念

Stream(流)代表着一系列元素,支持顺序和并行聚合操作。它不是数据结构,而是从数据源(如集合、数组或I/O资源)获取数据的管道。

主要特点:

  • 不是数据结构:不存储数据,只是从源获取数据
  • 函数式编程:支持lambda表达式和方法引用
  • 惰性执行:中间操作是惰性的,只有终端操作才会触发实际计算
  • 可消费性:流只能被消费一次

创建stream流的方式:

// 从集合创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream1 = list.stream();

// 从数组创建
String[] array = {"a", "b", "c"};
Stream<String> stream2 = Arrays.stream(array);

// 直接创建
Stream<String> stream3 = Stream.of("a", "b", "c");

// 创建无限流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2);
Stream<Double> randomStream = Stream.generate(Math::random);

3.Stream的操作类型

中间操作(Intermediate Operations)

返回新的Stream,可以链式调用:

.filter(Predicate<T>)   // 过滤
.map(Function<T,R>)     // 转换
.flatMap(Function<T,Stream<R>>) // 扁平化转换
.distinct()             // 去重
.sorted()               // 排序
.limit(long)            // 限制元素数量
.skip(long)             // 跳过前n个元素
终端操作(Terminal Operations)

触发实际计算,返回非Stream结果:

.forEach(Consumer<T>)   // 遍历
.collect(Collector)     // 收集为集合
.toArray()              // 转为数组
.reduce(BinaryOperator<T>) // 归约
.min(Comparator<T>)     // 最小值
.max(Comparator<T>)     // 最大值
.count()                // 计数
.anyMatch(Predicate<T>) // 任意匹配
.allMatch(Predicate<T>) // 全部匹配
.noneMatch(Predicate<T>) // 无匹配
.findFirst()            // 第一个元素
.findAny()              // 任意元素

项目中的使用:

示例:

LocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);
LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);

List<GoodsSalesDTO> salesTop10 = orderMapper.getSalesTop10(beginTime, endTime);

List<String> names = salesTop10.stream()
        .map(GoodsSalesDTO::getName)
        .collect(Collectors.toList());
String nameList = StringUtils.join(names, ",");

List<Integer> numbers = salesTop10.stream()
        .map(GoodsSalesDTO::getNumber)
        .collect(Collectors.toList());
String numberList = StringUtils.join(numbers, ",");

对查询条件用map封装:

1. 动态参数传递

使用Map可以灵活地传递不定数量、不定类型的参数。当查询条件可能变化时(比如有时需要添加更多条件),不需要修改方法签名,只需往Map中添加新的键值对即可。

2. 与ORM框架的兼容性

大多数ORM框架(如MyBatis)都支持Map作为参数:

  • MyBatis示例:可以直接在XML映射文件中使用#{beginTime}#{endTime}等表达式引用Map中的值
  • 避免了为每个查询创建专门的参数对象

3. 参数命名清晰

Map的键值对形式使得参数意义明确:

map.put("beginTime", beginTime);  // 比直接传beginTime更明确其用途
map.put("endTime", endTime);
map.put("status", Orders.COMPLETED);

4. 便于参数重用

同一个Map可以在多个查询中复用,或者添加/删除某些参数后用于不同的查询。

5. 简化方法签名

// 相比定义包含多个参数的方法:
Double sumByParams(LocalDateTime begin, LocalDateTime end, OrderStatus status);
    
// 使用Map只需一个参数:
Double sumByMap(Map<String, Object> params)

项目中使用示例:

 // select sum(amount) from orders where order_time > beginTime and order_time < endTime and status = 5
            Map map = new HashMap();
            map.put("beginTime", beginTime);
            map.put("endTime", endTime);
            map.put("status", Orders.COMPLETED);
            Double turnover =  orderMapper.sumByMap(map);
            turnover = turnover == null ? 0.0 : turnover;
            turnoverList.add(turnover);

二. 销量排名统计的动态sql(Top10)

​ 在最开始, 我不了解如何获取排名前10的菜品, 想法是获取所有订单, 然后遍历获取前10的菜品, 但看了老师的代码后, 才知道对待数据的条件查询, 都可以在sql中实现.

代码示例:

<select id="getSalesTop10" resultType="com.sky.dto.GoodsSalesDTO">
        select od.name, sum(od.number) as number
        from order_detail od, orders o
        where od.order_id = o.id
        and o.status = 5  // 指定订单的状态是已完成
        <if test="begin != null">
            and o.order_time &gt; #{begin}
        </if>
        <if test="end != null">
            and o.order_time &lt; #{end}
        </if>
        group by od.name  // 按订单菜品的名字分组
        order by number desc // 进行降序
        limit 10 // 获取前10的菜品
    </select>

不足:

1.对LocalDate与LocalDateTime的认识不足

2.对stram流不熟悉


网站公告

今日签到

点亮在社区的每一天
去签到