#苍穹外卖# day 10-11
目录
day10订单状态定时处理、来电提醒和客户催单
1 SpringTask
2 订单状态自动处理
3 WebSocket
4 外卖来单提醒
day11 图形报表
1 ApacheECharts
2 营业额统计
3 用户统计
4 订单统计
5 销量排名TOP10
day10订单状态定时处理、来电提醒和客户催单
1 SpringTask
概念:是Spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑。
作用:定时自动执行某段代码
core表达式
2 订单状态自动处理
代码实现:
package com.sky;import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;@SpringBootApplication
@EnableTransactionManagement //开启注解方式的事务管理
@Slf4j//开启日志注解
@EnableCaching//开启缓存注解
@EnableScheduling//开启定时任务
public class SkyApplication {public static void main(String[] args) {SpringApplication.run(SkyApplication.class, args);log.info("server started");}
}
package com.sky.task;import com.sky.entity.Orders;
import com.sky.mapper.OrderMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;import java.time.LocalDateTime;
import java.util.List;/*** 定时任务类,定时处理任务状态*/@Slf4j
@Component
public class OrderTask {@Autowiredprivate OrderMapper orderMapper;/*** 处理超时订单*/@Scheduled(cron = "0 * * * * ?")//每分钟执行一次public void processTimeoutOrder() {log.info("处理超时订单{}", LocalDateTime.now());LocalDateTime time = LocalDateTime.now().plusMinutes(-15);//处理超时订单List<Orders> ordersList = orderMapper.getByStatusAndOrderTimeLT(Orders.PENDING_PAYMENT, time.toString());if (ordersList != null && !ordersList.isEmpty()) {for (Orders orders : ordersList) {orders.setStatus(Orders.CANCELLED);//设置订单状态为已取消orders.setCancelReason("订单超时,自动取消");//设置取消原因orders.setCancelTime(LocalDateTime.now());//设置取消时间orderMapper.update(orders);//更新订单}}}@Scheduled(cron = "0 0 1 * * ?")//每天凌晨1点执行一次public void processCompletedOrder() {log.info("处理待派送订单{}", LocalDateTime.now());LocalDateTime time = LocalDateTime.now().plusMinutes(-60);List<Orders> ordersList = orderMapper.getByStatusAndOrderTimeLT(Orders.CONFIRMED, time.toString());if (ordersList != null && !ordersList.isEmpty()) {for (Orders orders : ordersList) {orders.setStatus(Orders.COMPLETED);//设置订单状态为已完成orderMapper.update(orders);//更新订单}}}}
Mapper接口实现
/*** 根据状态和下单时间查询订单* @param status* @param orderTime*/@Select("select * from orders where status =#{status} and order_time <#{orderTime}")List<Orders> getByStatusAndOrderTimeLT(Integer status, String orderTime);
3 WebSocket
4 外卖来单提醒
day11 图形报表
1 ApacheECharts
快速上手 - 使用手册 - Apache ECharts
2 营业额统计
代码开发
package com.sky.controller.admin;import com.sky.result.Result;
import com.sky.vo.TurnoverReportVO;
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.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.sky.service.ReportService;import java.time.LocalDate;@RequestMapping("/admin/report")
@RestController
@Api(tags = "数据统计相关接口")
@Slf4j
public class ReportController {@Autowiredprivate ReportService reportService;@GetMapping("/turnoverStatistics")@ApiOperation("营业额统计")public Result<TurnoverReportVO> turnoverStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {TurnoverReportVO turnoverReportVO = reportService.getTurnoverStatistics(begin, end);return Result.success(turnoverReportVO);}
}
Service业务层
package com.sky.service;import com.sky.vo.TurnoverReportVO;import java.time.LocalDate;public interface ReportService {/*** 营业额统计** @param begin* @param end* @return*/TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end);}
接口实现类
package com.sky.service.impl;import com.sky.entity.Orders;
import com.sky.mapper.OrderMapper;
import com.sky.service.ReportService;
import com.sky.vo.TurnoverReportVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;@Slf4j
@Service
public class ReportServiceImpl implements ReportService {@Autowiredprivate OrderMapper orderMapper;/*** 营业额统计** @param begin* @param end* @return*/@Overridepublic TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end) {List<LocalDate> dateList = new ArrayList<>();LocalDate currentDate = begin;while (!currentDate.isAfter(end)) {dateList.add(currentDate);currentDate = currentDate.plusDays(1);}List<Double> turnoverList = new ArrayList<>();for (LocalDate date : dateList) {LocalDateTime startOfDay = date.atStartOfDay(); // 等价于 LocalTime.MINLocalDateTime endOfDay = date.atTime(LocalTime.MAX);Map<String, Object> params = new HashMap<>();params.put("begin", startOfDay);params.put("end", endOfDay);params.put("status", Orders.COMPLETED);Double turnover = orderMapper.sumByMap(params);turnoverList.add(turnover != null ? turnover : 0.0);}// 格式化日期和营业额DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");List<String> formattedDates = dateList.stream().map(date -> date.format(formatter)).collect(Collectors.toList());String dateStr = String.join(",", formattedDates);List<String> formattedTurnovers = turnoverList.stream().map(amount -> String.format("%.2f", amount)).collect(Collectors.toList());String turnoverStr = String.join(",", formattedTurnovers);return TurnoverReportVO.builder().dateList(dateStr).turnoverList(turnoverStr).build();}
}
Order.Mapper
/*** 根据动态条件统计数量* @param map*/Double sumByMap(Map map);
XML文件
<select id="sumByMap" resultType="java.lang.Double">select sum(amount) from orders<where><if test="status != null">and status = #{status}</if><if test="begin != null">and order_time >= #{begin}</if><if test="end != null">and order_time <= #{end}</if></where></select>
3 用户统计
代码实现:
controller控制层
/*** 用户统计** @param begin* @param end* @return*/@GetMapping("/userStatistics")@ApiOperation("用户统计")public Result<UserReportVO> userStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {UserReportVO userReportVO = reportService.getUserStatistics(begin, end);return Result.success(userReportVO);}
Service业务层
/*** 用户统计** @param begin* @param end* @return*/UserReportVO getUserStatistics(LocalDate begin, LocalDate end);
接口实现类
/*** 用户统计** @param begin* @param end* @return*/@Overridepublic UserReportVO getUserStatistics(LocalDate begin, LocalDate end) {List<LocalDate> dateList = new ArrayList<>();// 用于存储日期的列表(存从begin 到 end)LocalDate currentDate = begin;while (!currentDate.isAfter(end)) {// 循环判断是否大于结束日期dateList.add(currentDate);currentDate = currentDate.plusDays(1);}//统计用户新增数量(select count(id) from user where create_time < ? and create_time > ? )// 统计用户总数(select count(id) from user from user where create _time < ? )List<Integer> newUserList = new ArrayList<>();List<Integer> totalUserList = new ArrayList<>();for (LocalDate date : dateList) {LocalDateTime startOfDay = date.atStartOfDay();LocalDateTime endOfDay = date.atTime(LocalTime.MAX);Map<String, Object> params = new HashMap<>();// 统计用户新增数量params.put("end", endOfDay);Integer newUser = userMapper.countByMap(params);// 统计用户总数params.put("begin", startOfDay);Integer totalUser = userMapper.countByMap(params);newUserList.add(newUser);totalUserList.add(totalUser);}// 格式化日期和用户数量(先将日期格式化为字符串,并将用户的数据新增与总的进行拼接用于展示)List<String> formattedDates = dateList.stream().map(date -> date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))).collect(Collectors.toList());String dateStr = String.join(",", formattedDates);String newUserStr = newUserList.stream().map(String::valueOf).collect(Collectors.joining(","));String totalUserStr = totalUserList.stream().map(String::valueOf).collect(Collectors.joining(","));return UserReportVO.builder().dateList(dateStr).newUserList(newUserStr).totalUserList(totalUserStr).build();}
Mapper接口
/*** 根据动态条件统计数量** @param params*/Integer countByMap(Map<String, Object> params);
XML文件
<select id="countByMap" resultType="java.lang.Integer">select count(id) from user<where><if test="begin != null">and create_time >= #{begin}</if><if test="end != null">and create_time <= #{end}</if></where></select>
展示
4 订单统计
代码实现:
/*** 订单统计** @param begin* @param end* @return*/@GetMapping("/ordersStatistics")@ApiOperation("订单统计")public Result<OrderReportVO> ordersStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {OrderReportVO orderReportVO = reportService.getOrdersStatistics(begin, end);log.info("订单统计:{}",orderReportVO);return Result.success(orderReportVO);}
Service业务层接口
/*** 订单统计** @param begin* @param end* @return*/OrderReportVO getOrdersStatistics(LocalDate begin, LocalDate end);
接口实现类
/*** 订单统计** @param begin* @param end* @return*/@Overridepublic OrderReportVO getOrdersStatistics(LocalDate begin, LocalDate end) {// 用于存储日期的列表List<LocalDate> dateList = new ArrayList<>();LocalDate currentDate = begin;while (!currentDate.isAfter(end)) {dateList.add(currentDate);currentDate = currentDate.plusDays(1);}//查询每一天的订单数据(总的和有效的)List<Integer> ValidOrderCountList = new ArrayList<>();List<Integer> totalOrderCountList = new ArrayList<>();for (LocalDate date : dateList) {LocalDateTime startOfDay = date.atStartOfDay();LocalDateTime endOfDay = date.atTime(LocalTime.MAX);Map<String, Object> params = new HashMap<>();params.put("begin", startOfDay);params.put("end", endOfDay);// 查询每天的订单总数Integer totalOrderCount = orderMapper.countByMap(params);// 查询每天的有效订单总数params.put("status", Orders.COMPLETED);Integer validOrderCount = orderMapper.countByMap(params);ValidOrderCountList.add(validOrderCount);totalOrderCountList.add(totalOrderCount);}//计算时间区间内的订单总数与有效订单总数,直接对应的List进行求和即可Integer totalSum = totalOrderCountList.stream().reduce(Integer::sum).get();Integer validSum = ValidOrderCountList.stream().reduce(Integer::sum).get();// 计算订单完成率Double orderCompletionRate= 0.0;if(totalSum!=0){orderCompletionRate = validSum / (double) totalSum;}// 将日期列表转换为字符串List<String> formattedDates = dateList.stream().map(date -> date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))).collect(Collectors.toList());String dateStr = String.join(",", formattedDates);// 将订单数量转换为字符串String totalOrderCountStr = totalOrderCountList.stream().map(String::valueOf).collect(Collectors.joining(","));String validOrderCountStr = ValidOrderCountList.stream().map(String::valueOf).collect(Collectors.joining(","));return OrderReportVO.builder().dateList(dateStr).orderCountList(totalOrderCountStr).validOrderCountList(validOrderCountStr).totalOrderCount(totalSum).validOrderCount(validSum).orderCompletionRate(orderCompletionRate).build();}
Mapper接口
/*** 根据动态条件统计数量** @param params*/Integer countByMap(Map<String, Object> params);
MapperXML文件
<select id="countByMap" resultType="java.lang.Integer">select count(id) from orders<where><if test="status != null">and status = #{status}</if><if test="begin != null">and order_time >= #{begin}</if><if test="end != null">and order_time <= #{end}</if></where></select>
5 销量排名TOP10
代码实现:
Controller控制层
/*** 销量排名** @param begin* @param end* @return*/@GetMapping("/top10")@ApiOperation("销量排名top10")public Result<SalesTop10ReportVO> top10(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {SalesTop10ReportVO salesTop10ReportVO = reportService.getSalesTop10(begin, end);log.info("销量排名:{}", salesTop10ReportVO);return Result.success(salesTop10ReportVO);}
Service业务层接口
/*** 销量排名top10** @param begin* @param end* @return*/SalesTop10ReportVO getSalesTop10(LocalDate begin, LocalDate end);
接口实现类
}/*** 销量排名top10** @param begin* @param end* @return*/@Overridepublic SalesTop10ReportVO getSalesTop10(LocalDate begin, LocalDate end) {LocalDateTime beginTime = begin.atStartOfDay();LocalDateTime endTime = end.atTime(LocalTime.MAX);List<GoodsSalesDTO> salesTop10 = orderMapper.getSalesTop10(beginTime, endTime);List<String> names = salesTop10.stream().map(GoodsSalesDTO::getName).collect(Collectors.toList());String nameList = String.join(",", names);List<Integer> numbers = salesTop10.stream().map(GoodsSalesDTO::getNumber).collect(Collectors.toList());String numberList = numbers.stream().map(String::valueOf).collect(Collectors.joining(","));return SalesTop10ReportVO.builder().nameList(nameList).numberList(numberList).build();}
Mapper持久层接口
/*** 查询销量排名top10** @param begin* @param end* @return*/List<GoodsSalesDTO> getSalesTop10(LocalDateTime begin, LocalDateTime end);
Mapper持久层XML文件
<select id="getSalesTop10" resultType="com.sky.dto.GoodsSalesDTO">select og.name, sum(og.number) as number from orders oleft join order_detail og on o.id = og.order_idwhere o.status = 5<if test="begin != null">and o.order_time >= #{begin}</if><if test="end != null">and o.order_time <= #{end}</if>group by og.nameorder by number desclimit 0,10</select>
效果展示