|
|
|
@ -0,0 +1,364 @@
|
|
|
|
|
package com.sky.service.impl;
|
|
|
|
|
|
|
|
|
|
import com.sky.entity.Orders;
|
|
|
|
|
import com.sky.mapper.ReportMapper;
|
|
|
|
|
import com.sky.properties.ReportExcelProperties;
|
|
|
|
|
import com.sky.result.Result;
|
|
|
|
|
import com.sky.service.ReportService;
|
|
|
|
|
import com.sky.vo.*;
|
|
|
|
|
import io.swagger.models.auth.In;
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
|
import org.apache.http.HttpResponse;
|
|
|
|
|
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
|
|
|
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
|
|
|
|
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
|
|
|
|
import org.apache.poi.openxml4j.opc.OPCPackage;
|
|
|
|
|
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
|
|
|
|
import org.apache.poi.ss.extractor.ExcelExtractor;
|
|
|
|
|
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
|
|
|
|
import org.apache.poi.xssf.usermodel.*;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
import org.springframework.format.annotation.DateTimeFormat;
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
|
|
import javax.servlet.ServletOutputStream;
|
|
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
|
|
import java.io.*;
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
|
import java.nio.file.Files;
|
|
|
|
|
import java.time.LocalDate;
|
|
|
|
|
import java.time.LocalDateTime;
|
|
|
|
|
import java.time.LocalTime;
|
|
|
|
|
import java.time.Period;
|
|
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
|
|
import java.time.temporal.TemporalAdjusters;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
|
|
|
|
|
@Service
|
|
|
|
|
@Slf4j
|
|
|
|
|
public class ReportServiceImpl implements ReportService {
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
ReportMapper reportMapper;
|
|
|
|
|
@Autowired
|
|
|
|
|
ReportExcelProperties reportExcelProperties;
|
|
|
|
|
@Autowired
|
|
|
|
|
WorkspaceServiceImpl workspaceService;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 营业额统计
|
|
|
|
|
*
|
|
|
|
|
* @param begin
|
|
|
|
|
* @param end
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public Result<TurnoverReportVO> getTurnoverStatistics(LocalDate begin, LocalDate end) {
|
|
|
|
|
LocalDate p = begin;
|
|
|
|
|
ArrayList<LocalDate> dateTimes = new ArrayList<>();
|
|
|
|
|
while (!p.equals(end)) {
|
|
|
|
|
dateTimes.add(p);
|
|
|
|
|
p = p.plusDays(1);
|
|
|
|
|
}
|
|
|
|
|
ArrayList<Double> count = new ArrayList<>();
|
|
|
|
|
for (LocalDate dateTime : dateTimes) {
|
|
|
|
|
HashMap<String, Object> map = new HashMap<>();
|
|
|
|
|
map.put("status", 5);
|
|
|
|
|
map.put("begin", LocalDateTime.of(dateTime, LocalTime.MIN));
|
|
|
|
|
map.put("end", LocalDateTime.of(dateTime, LocalTime.MAX));
|
|
|
|
|
Double sum = reportMapper.sumByMap(map);
|
|
|
|
|
if (sum == null) {
|
|
|
|
|
sum = 0D;
|
|
|
|
|
}
|
|
|
|
|
count.add(sum);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Result.success(TurnoverReportVO.builder()
|
|
|
|
|
.dateList(StringUtils.join(dateTimes, ","))
|
|
|
|
|
.turnoverList(StringUtils.join(count, ","))
|
|
|
|
|
.build());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 用户统计
|
|
|
|
|
*
|
|
|
|
|
* @param begin
|
|
|
|
|
* @param end
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public UserReportVO userStatistics(LocalDate begin, LocalDate end) {
|
|
|
|
|
LocalDate p = begin;
|
|
|
|
|
ArrayList<LocalDate> dateTimes = new ArrayList<>();
|
|
|
|
|
while (!p.equals(end)) {
|
|
|
|
|
dateTimes.add(p);
|
|
|
|
|
p = p.plusDays(1);
|
|
|
|
|
}
|
|
|
|
|
ArrayList<Integer> user = new ArrayList<>();
|
|
|
|
|
ArrayList<Integer> newUser = new ArrayList<>();
|
|
|
|
|
for (LocalDate dateTime : dateTimes) {
|
|
|
|
|
HashMap<String, Object> map = new HashMap<>();
|
|
|
|
|
map.put("begin", LocalDateTime.of(dateTime, LocalTime.MIN));
|
|
|
|
|
map.put("end", LocalDateTime.of(dateTime, LocalTime.MAX));
|
|
|
|
|
Integer sum = reportMapper.sumUserByDay(map);
|
|
|
|
|
Integer sumUser = reportMapper.sumUser(map);
|
|
|
|
|
if (sum == null) {
|
|
|
|
|
sum = 0;
|
|
|
|
|
}
|
|
|
|
|
if (sumUser == null) {
|
|
|
|
|
sumUser = 0;
|
|
|
|
|
}
|
|
|
|
|
newUser.add(sum);
|
|
|
|
|
user.add(sumUser);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return UserReportVO.builder()
|
|
|
|
|
.dateList(StringUtils.join(dateTimes, ","))
|
|
|
|
|
.totalUserList(StringUtils.join(user, ","))
|
|
|
|
|
.newUserList(StringUtils.join(newUser, ","))
|
|
|
|
|
.build();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 订单统计
|
|
|
|
|
*
|
|
|
|
|
* @param begin
|
|
|
|
|
* @param end
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public OrderReportVO orderStatistics(LocalDate begin, LocalDate end) {
|
|
|
|
|
// 初始化日期列表,用于存储开始和结束日期之间的每一天
|
|
|
|
|
LocalDate p = begin;
|
|
|
|
|
ArrayList<LocalDate> dateTimes = new ArrayList<>();
|
|
|
|
|
while (!p.equals(end)) {
|
|
|
|
|
dateTimes.add(p);
|
|
|
|
|
p = p.plusDays(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 初始化订单数量和新订单数量的列表
|
|
|
|
|
ArrayList<Integer> order = new ArrayList<>();
|
|
|
|
|
ArrayList<Integer> newOrder = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
// 遍历每一天,统计订单数量和新订单数量
|
|
|
|
|
for (LocalDate dateTime : dateTimes) {
|
|
|
|
|
HashMap<String, Object> map = new HashMap<>();
|
|
|
|
|
map.put("begin", LocalDateTime.of(dateTime, LocalTime.MIN)); // 当天的最小时间
|
|
|
|
|
map.put("end", LocalDateTime.of(dateTime, LocalTime.MAX)); // 当天的最大时间
|
|
|
|
|
map.put("status", Orders.COMPLETED); // 完成的订单状态
|
|
|
|
|
|
|
|
|
|
// 调用reportMapper查询新订单和订单总数
|
|
|
|
|
Integer sum = reportMapper.sumNewOrder(map);
|
|
|
|
|
Integer sumOrder = reportMapper.sumOrder(map);
|
|
|
|
|
|
|
|
|
|
// 处理查询结果为null的情况
|
|
|
|
|
if (sum == null) {
|
|
|
|
|
sum = 0;
|
|
|
|
|
}
|
|
|
|
|
if (sumOrder == null) {
|
|
|
|
|
sumOrder = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 将查询结果添加到列表中
|
|
|
|
|
order.add(sumOrder);
|
|
|
|
|
newOrder.add(sum);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 计算订单完成率
|
|
|
|
|
Double rate;
|
|
|
|
|
if (sumArrayList(order) == 0) {
|
|
|
|
|
rate = 1.0; // 如果订单总数为0,则完成率为1.0
|
|
|
|
|
} else {
|
|
|
|
|
rate = sumArrayList(newOrder) / sumArrayList(order) * 1.0; // 计算完成率
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 构建并返回订单统计报告对象
|
|
|
|
|
return OrderReportVO.builder()
|
|
|
|
|
.dateList(StringUtils.join(dateTimes, ","))
|
|
|
|
|
// 日期列表,以逗号分隔
|
|
|
|
|
.orderCountList(StringUtils.join(newOrder, ","))
|
|
|
|
|
// 新订单数量列表,以逗号分隔
|
|
|
|
|
.validOrderCountList(StringUtils.join(order, ","))
|
|
|
|
|
// 订单数量列表,以逗号分隔
|
|
|
|
|
.totalOrderCount(sumArrayList(newOrder))
|
|
|
|
|
// 新订单总数
|
|
|
|
|
.validOrderCount(sumArrayList(order))
|
|
|
|
|
// 订单总数
|
|
|
|
|
.orderCompletionRate(rate)
|
|
|
|
|
// 订单完成率
|
|
|
|
|
.build();
|
|
|
|
|
// 构建报告对象
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 销量排行前十
|
|
|
|
|
*
|
|
|
|
|
* @param begin
|
|
|
|
|
* @param end
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public SalesTop10ReportVO salesTop10Report(LocalDate begin, LocalDate end) {
|
|
|
|
|
// 初始化商品名称和销售数量的列表
|
|
|
|
|
ArrayList<String> name = new ArrayList<>();
|
|
|
|
|
ArrayList<Integer> number = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
// 创建映射,用于传递查询参数
|
|
|
|
|
HashMap<String, Object> map = new HashMap<>();
|
|
|
|
|
// 查询结果集
|
|
|
|
|
ArrayList<HashMap<String, Object>> result;
|
|
|
|
|
|
|
|
|
|
// 设置查询参数
|
|
|
|
|
map.put("begin", LocalDateTime.of(begin, LocalTime.MIN)); // 查询开始时间(当天的最小时间)
|
|
|
|
|
map.put("end", LocalDateTime.of(end, LocalTime.MAX)); // 查询结束时间(当天的最大时间)
|
|
|
|
|
map.put("status", Orders.COMPLETED); // 订单状态,这里为已完成的订单
|
|
|
|
|
|
|
|
|
|
// 调用reportMapper查询销售排行前十的商品
|
|
|
|
|
result = reportMapper.salesTop10Report(map);
|
|
|
|
|
|
|
|
|
|
// 遍历查询结果,提取商品名称和销售数量
|
|
|
|
|
for (HashMap<String, Object> hashMap : result) {
|
|
|
|
|
name.add((String) hashMap.get("name")); // 获取商品名称
|
|
|
|
|
number.add(((BigDecimal) hashMap.get("number")).intValue()); // 获取销售数量并转换为整数
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 构建并返回销售排行报告对象
|
|
|
|
|
return SalesTop10ReportVO.builder()
|
|
|
|
|
.nameList(StringUtils.join(name, ",")) // 将商品名称列表转换为逗号分隔的字符串
|
|
|
|
|
.numberList(StringUtils.join(number, ",")) // 将销售数量列表转换为逗号分隔的字符串
|
|
|
|
|
.build(); // 构建报告对象
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 导出excel表格
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public void export(HttpServletResponse httpResponse) throws IOException, InvalidFormatException {
|
|
|
|
|
File file = new File(reportExcelProperties.getFilePath());
|
|
|
|
|
OPCPackage opcPackage = OPCPackage.open(file);
|
|
|
|
|
//获取工作薄
|
|
|
|
|
XSSFWorkbook workbook = new XSSFWorkbook(opcPackage);
|
|
|
|
|
String s = reportExcelProperties.getSheet()[0];
|
|
|
|
|
//获取工作表
|
|
|
|
|
XSSFSheet sheet = workbook.getSheet(s);
|
|
|
|
|
//填写日期
|
|
|
|
|
XSSFRow row = sheet.getRow(1);
|
|
|
|
|
XSSFCellStyle dataStyle = workbook.createCellStyle();
|
|
|
|
|
//设置日期的字体
|
|
|
|
|
XSSFFont font = workbook.createFont();
|
|
|
|
|
font.setFontHeight(16);
|
|
|
|
|
font.setFontName("宋体");
|
|
|
|
|
dataStyle.setAlignment(HorizontalAlignment.RIGHT);
|
|
|
|
|
XSSFCell cell0 = row.getCell(1);
|
|
|
|
|
dataStyle.setFont(font);
|
|
|
|
|
cell0.setCellStyle(dataStyle);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//获取营业概览数据
|
|
|
|
|
LocalDateTime begin = LocalDateTime.now().with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
|
|
|
|
|
LocalDateTime end = LocalDateTime.now().with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
|
|
|
|
|
cell0.setCellValue(begin.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss")) + "——" + end.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss")));
|
|
|
|
|
BusinessDataVO businessData = workspaceService.getBusinessData(begin, end);
|
|
|
|
|
XSSFRow row3 = sheet.getRow(3);
|
|
|
|
|
//营业额
|
|
|
|
|
XSSFCell cell = row3.getCell(2);
|
|
|
|
|
cell.setCellValue(businessData.getTurnover());
|
|
|
|
|
//订单完成率
|
|
|
|
|
XSSFCell cell1 = row3.getCell(4);
|
|
|
|
|
cell1.setCellValue(businessData.getOrderCompletionRate());
|
|
|
|
|
//新增用户数
|
|
|
|
|
XSSFCell cell2 = row3.getCell(6);
|
|
|
|
|
cell2.setCellValue(businessData.getNewUsers());
|
|
|
|
|
//有效订单
|
|
|
|
|
XSSFRow row1 = sheet.getRow(4);
|
|
|
|
|
XSSFCell cell3 = row1.getCell(2);
|
|
|
|
|
cell3.setCellValue(businessData.getValidOrderCount());
|
|
|
|
|
//平均客单价
|
|
|
|
|
XSSFCell cell4 = row1.getCell(4);
|
|
|
|
|
cell4.setCellValue(businessData.getValidOrderCount());
|
|
|
|
|
int dayOfMonth = Period.between(begin.toLocalDate(), end.toLocalDate()).getDays();
|
|
|
|
|
System.out.println("dayOfMonth:" + dayOfMonth);
|
|
|
|
|
//获取明细数据
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < dayOfMonth; i++) {
|
|
|
|
|
XSSFRow row2 = sheet.getRow(i + 7);
|
|
|
|
|
LocalDateTime localDateTimeBegin = begin.plusDays(i);
|
|
|
|
|
LocalDateTime localDateTimeEnd = localDateTimeBegin.with(LocalTime.MAX);
|
|
|
|
|
BusinessDataVO data = workspaceService.getBusinessData(localDateTimeBegin, localDateTimeEnd);
|
|
|
|
|
//设置日期
|
|
|
|
|
row2.getCell(1).setCellValue(localDateTimeBegin.toLocalDate().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
|
|
|
|
|
//营业额
|
|
|
|
|
row2.getCell(2).setCellValue(data.getTurnover());
|
|
|
|
|
//有效订单
|
|
|
|
|
row2.getCell(3).setCellValue(data.getValidOrderCount());
|
|
|
|
|
//订单完成率
|
|
|
|
|
row2.getCell(4).setCellValue(data.getOrderCompletionRate());
|
|
|
|
|
//平均客单价
|
|
|
|
|
row2.getCell(5).setCellValue(data.getUnitPrice());
|
|
|
|
|
//新增用户数
|
|
|
|
|
row2.getCell(6).setCellValue(data.getNewUsers());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取Servlet输出流,用于写入响应数据。
|
|
|
|
|
* 这个输出流允许我们将二进制数据直接写入HTTP响应中。
|
|
|
|
|
*/
|
|
|
|
|
ServletOutputStream outputStream = httpResponse.getOutputStream();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 重置HTTP响应状态和头部信息。
|
|
|
|
|
* 调用reset()方法会清除任何存在的响应信息,包括状态码和头部信息,
|
|
|
|
|
* 这样我们可以设置新的响应内容和头部信息。
|
|
|
|
|
*/
|
|
|
|
|
httpResponse.reset();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 设置响应内容类型为Excel文件。
|
|
|
|
|
* 通过设置ContentType为"application/vnd.ms-excel",我们告诉客户端响应的内容类型是Excel文件。
|
|
|
|
|
*/
|
|
|
|
|
httpResponse.setContentType("application/vnd.ms-excel");
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 添加响应头部,指示浏览器这是一个附件,并为其指定文件名。
|
|
|
|
|
* Content-disposition头部用于指定响应的用途,这里我们设置为"attachment"表示附件,
|
|
|
|
|
* 并指定下载时的默认文件名为"template.xlsx"。
|
|
|
|
|
*/
|
|
|
|
|
httpResponse.addHeader("Content-disposition", "attachment;filename=template.xlsx");
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 将Excel工作簿写入Servlet输出流。
|
|
|
|
|
* 使用Apache POI库的write方法,我们将Excel工作簿(workbook)的内容写入到前面获取的输出流中。
|
|
|
|
|
*/
|
|
|
|
|
workbook.write(outputStream);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 刷新输出流,确保所有数据都被写入。
|
|
|
|
|
* 调用flush()方法可以确保输出流中的所有数据都被推送到客户端。
|
|
|
|
|
*/
|
|
|
|
|
outputStream.flush();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 关闭输出流,释放资源。
|
|
|
|
|
* 完成数据写入后,我们需要关闭输出流以释放系统资源。
|
|
|
|
|
*/
|
|
|
|
|
outputStream.close();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 计算整数列表的总和。
|
|
|
|
|
* 这个方法接受一个ArrayList<Integer>类型的参数,遍历列表中的每个元素,并将它们相加。
|
|
|
|
|
*
|
|
|
|
|
* @param arrayList 要计算总和的整数列表
|
|
|
|
|
* @return 返回列表中所有整数的总和
|
|
|
|
|
*/
|
|
|
|
|
private Integer sumArrayList(ArrayList<Integer> arrayList) {
|
|
|
|
|
Integer sum = 0; // 初始化总和为0
|
|
|
|
|
for (Integer integer : arrayList) { // 遍历列表中的每个整数
|
|
|
|
|
sum += integer; // 将当前整数加到总和上
|
|
|
|
|
}
|
|
|
|
|
return sum; // 返回计算出的总和
|
|
|
|
|
}
|
|
|
|
|
}
|