16. 带你实现后台数据统计功能
商品的编辑出现 请求失败


修改 GoodsService 的 update 方法
public void updateById(Goods goods) {
goodsMapper.updateById(goods);
if ("下架".equals(goods.getStatus())) {
// 删除购物车的对应的商品
cartMapper.deleteByGoodsId(goods.getId());
}
}
Echarts 笔记
https://www.yuque.com/xiaqing-en2ii/skflxg/ynkqw9ra0gufhdb7
本节课前端代码 DataManager.vue
<template>
<div>
<div style="display: flex; grid-gap: 10px">
<div class="card" style="padding: 20px; flex: 1; display: flex">
<div style="flex: 1; font-size: 20px;">销售总额</div>
<div style="flex: 1; font-size: 20px; font-weight: bold; color: red">¥{{ data.count.total }}</div>
</div>
<div class="card" style="padding: 20px; flex: 1; display: flex">
<div style="flex: 1; font-size: 20px;">今日销售额</div>
<div style="flex: 1; font-size: 20px; font-weight: bold; color: #ff8200">¥{{ data.count.today }}</div>
</div>
<div class="card" style="padding: 20px; flex: 1; display: flex">
<div style="flex: 1; font-size: 20px;">商品总数</div>
<div style="flex: 1; font-size: 20px; font-weight: bold; color: #00b0ef">{{ data.count.goods }}</div>
</div>
<div class="card" style="padding: 20px; flex: 1; display: flex">
<div style="flex: 1; font-size: 20px;">注册用户</div>
<div style="flex: 1; font-size: 20px; font-weight: bold; color: #9b3cfd">{{ data.count.user }}</div>
</div>
</div>
<div style="margin-top: 10px; display: flex; grid-gap: 10px">
<div id="line" style="flex: 1; padding: 20px; height: 500px" class="card"></div>
<div id="pie" style="flex: 1; padding: 20px; height: 500px" class="card"></div>
</div>
</div>
</template>
<script setup>
import { reactive, onMounted } from "vue";
import request from "@/utils/request";
import * as echarts from 'echarts'
const data = reactive({
count: {}
})
const lineOption = {
title: {
text: '近一周订单销售的趋势图',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
legend: {
left: 'left'
},
xAxis: {
name: '日期',
type: 'category',
data: []
},
yAxis: {
name: '销售额(元)',
type: 'value'
},
grid: {
top: '20%',
bottom:'10%'
},
series: [
{
data: [],
type: 'line',
smooth: true,
areaStyle: {
opacity: 0.8, // 阴影的透明度
color: 'rgb(185,190,255)' // 阴影的颜色和透明度
},
markPoint: {
data: [
{ type: 'max', name: 'Max' },
{ type: 'min', name: 'Min' }
]
},
markLine: {
data: [{ type: 'average', name: 'Avg' }]
}
},
]
}
const pieOption = {
title: {
text: '分类商品销售额统计',
subtext: '比例图',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c}元 ({d}%)'
},
legend: {
top: 0,
orient: 'vertical',
left: 'left'
},
series: [
{
name: '销售额',
type: 'pie',
center: ['50%', '60%'],
radius: '50%',
data: [],
label: {
show: true,
formatter(param) {
return param.name + ' (' + param.percent + '%)';
}
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
request.get('/count').then(res => {
data.count = res.data
})
// 等页面所有元素加载完成后再设置 echarts图表
onMounted(() => {
// 折线图
let lineDom = document.getElementById('line')
let lineChart = echarts.init(lineDom)
// 请求数据 初始化图表
request.get('/selectLine').then(res => {
lineOption.xAxis.data = res.data.date
lineOption.series[0].data = res.data.count
lineChart.setOption(lineOption)
})
// 饼图
let pieDom = document.getElementById('pie')
let pieChart = echarts.init(pieDom)
// 请求数据 初始化图表
request.get('/selectPie').then(res => {
pieOption.series[0].data = res.data
pieChart.setOption(pieOption)
})
})
</script>
后端代码 WebController
package com.example.controller;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.example.common.Result;
import com.example.entity.*;
import com.example.mapper.OrderDetailMapper;
import com.example.service.*;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.*;
@RestController
public class WebController {
@Resource
private AdminService adminService;
@Resource
private UserService userService;
@Resource
OrdersService ordersService;
@Resource
GoodsService goodsService;
@Resource
CategoryService categoryService;
@Resource
OrderDetailMapper orderDetailMapper;
/**
* 默认请求接口
*/
@GetMapping("/")
public Result hello() {
return Result.success();
}
/**
* 登录
*/
@PostMapping("/login")
public Result login(@RequestBody Account account) {
Account ac = null;
if ("管理员".equals(account.getRole())) {
ac = adminService.login(account);
}
if ("普通用户".equals(account.getRole())) {
ac = userService.login(account);
}
if (ac == null) {
return Result.error("登录失败,用户不存在");
}
return Result.success(ac);
}
/**
* 注册
*/
@PostMapping("/register")
public Result register(@RequestBody User user) {
if (!user.getPassword().equals(user.getNewPassword())) {
return Result.error("两次输入密码不一致");
}
userService.add(user);
return Result.success();
}
/**
* 修改密码
*/
@PutMapping("/updatePassword")
public Result updatePassword(@RequestBody Account account) {
if ("管理员".equals(account.getRole())) {
adminService.updatePassword(account);
}
if ("普通用户".equals(account.getRole())) {
userService.updatePassword(account);
}
return Result.success();
}
// 数据统计使用的接口
@GetMapping("/count")
public Result count() {
List<Orders> ordersList = ordersService.selectAll(null).stream().filter(orders -> !orders.getStatus().equals("已取消")).toList();
BigDecimal total = ordersList.stream().map(Orders::getTotal).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
String todayDate = DateUtil.today(); // 2025-12-05
BigDecimal today = ordersList.stream().filter(orders -> orders.getTime().contains(todayDate))
.map(Orders::getTotal).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
Integer goods = goodsService.selectAll(null).size();
Integer user = userService.selectByAll(null).size();
Map<String, Object> map = new HashMap<>();
map.put("total", total);
map.put("today", today);
map.put("goods", goods);
map.put("user", user);
return Result.success(map);
}
@GetMapping("/selectLine")
public Result selectLine() {
Date date = new Date();
DateTime start = DateUtil.offsetDay(date, -6);
List<DateTime> dateTimes = DateUtil.rangeToList(start, date, DateField.DAY_OF_YEAR);
List<String> dateStrList = dateTimes.stream().map(dateTime -> DateUtil.format(dateTime, "MM-dd")).sorted().toList();
List<Orders> ordersList = ordersService.selectAll(null).stream().filter(orders -> !orders.getStatus().equals("已取消")).toList();
int year = DateUtil.year(date);
ArrayList<BigDecimal> countList = new ArrayList<>();
for (String day : dateStrList) {
// 包含 年月日的 订单
BigDecimal total = ordersList.stream().filter(o -> o.getTime().contains(String.valueOf(year)) && o.getTime().contains(day))
.map(Orders::getTotal).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
countList.add(total);
}
Map<String, Object> map = new HashMap<>();
map.put("date", dateStrList);
map.put("count", countList);
return Result.success(map);
}
@GetMapping("/selectPie")
public Result selectPie() {
List<Map<String, Object>> list = new ArrayList<>();
List<Category> categoryList = categoryService.selectAll(null);
Map<String, Object> map;
for (Category category : categoryList) {
map = new HashMap<>();
map.put("name", category.getName());
BigDecimal total = BigDecimal.ZERO;
List<OrderDetail> orderDetailList = orderDetailMapper.selectAll(null);
for (OrderDetail orderDetail : orderDetailList) {
Integer orderId = orderDetail.getOrderId();
Orders orders = ordersService.selectById(orderId);
if (!orders.getStatus().equals("已取消")) {
Integer goodsId = orderDetail.getGoodsId();
Goods goods = goodsService.selectById(goodsId);
if (goods.getCategoryId().equals(category.getId())) {
total = total.add(orders.getTotal());
}
}
}
map.put("value", total);
if (total.compareTo(BigDecimal.ZERO) > 0) {
list.add(map);
}
}
return Result.success(list);
}
}