食堂数据总览

This commit is contained in:
jjLv 2025-05-16 09:24:35 +08:00
parent 804946dad1
commit 62ce2a9810
10 changed files with 441 additions and 4 deletions

View File

@ -0,0 +1,80 @@
package com.bonus.canteen.core.dataScreening.controller;
import com.bonus.canteen.core.dataScreening.dto.DataScreenDTO;
import com.bonus.canteen.core.dataScreening.service.DataScreeningService;
import com.bonus.canteen.core.device.domain.DeviceBind;
import com.bonus.canteen.core.device.service.IDeviceBindService;
import com.bonus.common.core.utils.poi.ExcelUtil;
import com.bonus.common.core.web.controller.BaseController;
import com.bonus.common.core.web.domain.AjaxResult;
import com.bonus.common.core.web.page.TableDataInfo;
import com.bonus.common.log.annotation.SysLog;
import com.bonus.common.log.enums.OperaType;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 设备绑定多档口子Controller
*
* @author xsheng
* @date 2025-05-13
*/
@Api(tags = "数据总览")
@RestController
@RequestMapping("/dataScreening")
public class DataScreeningController extends BaseController {
@Autowired
private DataScreeningService service;
@ApiOperation(value = "获取数据总览(今日营业额、订单等)")
@PostMapping("/getDataScreeningModel")
public AjaxResult getDataScreeningModel() {
try {
return success(service.getDataScreeningModel());
} catch (Exception e) {
return error("获取数据总览错误");
}
}
@ApiOperation(value = "获取食堂订单及销量趋势")
@PostMapping("/getCanteenOrdersAndSalesTrends")
public AjaxResult getCanteenOrdersAndSalesTrends(@RequestBody DataScreenDTO dataScreenDTO) {
try {
return service.getCanteenOrdersAndSalesTrends(dataScreenDTO);
} catch (Exception e) {
return error("获取食堂订单及销量趋势错误");
}
}
@ApiOperation(value = "获取商超订单及销量趋势")
@PostMapping("/getSupermarketOrderAndSalesTrend")
public AjaxResult getSupermarketOrderAndSalesTrend(@RequestBody DataScreenDTO dataScreenDTO) {
try {
return service.getSupermarketOrderAndSalesTrend(dataScreenDTO);
} catch (Exception e) {
return error("获取商超订单及销量趋势错误");
}
}
@ApiOperation(value = "获取本月菜品销量排名")
@PostMapping("/getThisMonthSDishSalesRanking")
public AjaxResult getThisMonthDishSalesRanking() {
try {
return service.getThisMonthDishSalesRanking();
} catch (Exception e) {
return error("获取本月菜品销量排名错误");
}
}
@ApiOperation(value = "订单类型占比")
@PostMapping("/getProportionOfOrderTypes")
public AjaxResult getProportionOfOrderTypes(@RequestBody DataScreenDTO dataScreenDTO) {
try {
return service.getProportionOfOrderTypes(dataScreenDTO);
} catch (Exception e) {
return error("获取订单类型占比错误");
}
}
}

View File

@ -0,0 +1,13 @@
package com.bonus.canteen.core.dataScreening.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author tqzhang
*/
@Data
public class DataScreenDTO {
@ApiModelProperty(value = "类型1-最近7天2-最近30天")
private String type;
}

View File

@ -0,0 +1,26 @@
package com.bonus.canteen.core.dataScreening.mapper;
import com.bonus.canteen.core.dataScreening.vo.EchartsVO;
import com.bonus.canteen.core.dataScreening.vo.SingleModelVO;
import com.bonus.common.core.web.domain.AjaxResult;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface DataScreeningMapper {
SingleModelVO selectTurnover(@Param("date") String date,@Param("type") int type);
SingleModelVO selectOrder(@Param("date") String date,@Param("type") int type);
SingleModelVO selectPerson(@Param("date") String date,@Param("type") int type);
SingleModelVO selectDish(@Param("date")String date);
List<EchartsVO> getCanteenOrdersAndSalesTrends(@Param("startDate")String startDate, @Param("endDate")String endDate);
List<EchartsVO> getSupermarketOrderAndSalesTrend(@Param("startDate")String startDate, @Param("endDate")String endDate);
List<SingleModelVO> getThisMonthDishSalesRanking(@Param("startDate")String startDate, @Param("endDate")String endDate);
SingleModelVO selectOrderByDate(@Param("startDate")String startDate, @Param("endDate")String endDate, @Param("type") int type);
}

View File

@ -0,0 +1,16 @@
package com.bonus.canteen.core.dataScreening.service;
import com.bonus.canteen.core.dataScreening.dto.DataScreenDTO;
import com.bonus.common.core.web.domain.AjaxResult;
public interface DataScreeningService {
AjaxResult getDataScreeningModel();
AjaxResult getCanteenOrdersAndSalesTrends(DataScreenDTO dataScreenDTO);
AjaxResult getSupermarketOrderAndSalesTrend(DataScreenDTO dataScreenDTO);
AjaxResult getThisMonthDishSalesRanking();
AjaxResult getProportionOfOrderTypes(DataScreenDTO dataScreenDTO);
}

View File

@ -0,0 +1,171 @@
package com.bonus.canteen.core.dataScreening.service;
import com.bonus.canteen.core.dataScreening.dto.DataScreenDTO;
import com.bonus.canteen.core.dataScreening.mapper.DataScreeningMapper;
import com.bonus.canteen.core.dataScreening.vo.EchartsVO;
import com.bonus.canteen.core.dataScreening.vo.SingleModelVO;
import com.bonus.common.core.utils.DateUtils;
import com.bonus.common.core.web.domain.AjaxResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class DataScreeningServiceImpl implements DataScreeningService {
@Autowired
private DataScreeningMapper mapper;
@Override
public AjaxResult getDataScreeningModel() {
List<SingleModelVO> list = new ArrayList<>();
String currentDate = DateUtils.getDate();
String yesterday = getLastDate(-1);
// 查询各项数据
addSingleItem("今日食堂营业额(元)", currentDate, yesterday, list, 11, "Turnover");
addSingleItem("今日食堂订单量(个)", currentDate, yesterday, list, 11, "Order");
addSingleItem("今日就餐人数(个)", currentDate, yesterday, list, 11, "Person");
addSingleItem("今日菜品数量(个)", currentDate, yesterday, list, -1, "Dish");
addSingleItem("今日超市营业额(元)", currentDate, yesterday, list, 4, "Turnover");
addSingleItem("今日超市订单量(个)", currentDate, yesterday, list, 4, "Order");
return AjaxResult.success(list);
}
private void addSingleItem(String name, String currentDate, String yesterdayData, List<SingleModelVO> list, int orderType, String type) {
SingleModelVO today = fetchDataByType(currentDate, orderType, type);
SingleModelVO yesterday = fetchDataByType(yesterdayData, orderType, type);
today.setName(name);
calculateRate(today, yesterday, type);
list.add(today);
}
private SingleModelVO fetchDataByType(String date, int orderType, String type) {
switch (type) {
case "Order":
return mapper.selectOrder(date, orderType);
case "Turnover":
return mapper.selectTurnover(date, orderType);
case "Person":
return mapper.selectPerson(date, orderType);
case "Dish":
return mapper.selectDish(date);
default:
return new SingleModelVO();
}
}
private void calculateRate(SingleModelVO today, SingleModelVO yesterday, String type) {
BigDecimal yesterdayNum = Optional.ofNullable(yesterday.getNum()).orElse(BigDecimal.ZERO);
BigDecimal todayNum = Optional.ofNullable(today.getNum()).orElse(BigDecimal.ZERO);
if (yesterdayNum.compareTo(BigDecimal.ZERO) == 0) {
today.setRate(todayNum.compareTo(BigDecimal.ZERO) == 0 ? "0.0" : "100.0");
} else {
BigDecimal rate = todayNum.subtract(yesterdayNum)
.divide(yesterdayNum, 4, BigDecimal.ROUND_HALF_UP)
.multiply(BigDecimal.valueOf(100));
today.setRate(String.format("%.2f", rate));
}
if ("Turnover".equals(type)) {
today.setNum(todayNum.divide(BigDecimal.valueOf(100)));
}
}
@Override
public AjaxResult getCanteenOrdersAndSalesTrends(DataScreenDTO dataScreenDTO) {
return getOrdersAndSalesTrends(dataScreenDTO, true);
}
@Override
public AjaxResult getSupermarketOrderAndSalesTrend(DataScreenDTO dataScreenDTO) {
return getOrdersAndSalesTrends(dataScreenDTO, false);
}
private AjaxResult getOrdersAndSalesTrends(DataScreenDTO dataScreenDTO, boolean isCanteen) {
String date = getLastDate(dataScreenDTO.getType().equals("2") ? -29 : -6);
List<EchartsVO> list = isCanteen
? mapper.getCanteenOrdersAndSalesTrends(date, DateUtils.getDate())
: mapper.getSupermarketOrderAndSalesTrend(date, DateUtils.getDate());
List<String> dateList = getDatesBetween(date, DateUtils.getDate(), "yyyy-MM-dd");
Map<String, EchartsVO> dataMap = list.stream()
.collect(Collectors.toMap(EchartsVO::getOrderDate, vo -> vo, (a, b) -> a));
List<EchartsVO> result = dateList.stream().map(dateStr -> {
EchartsVO echartsVO = dataMap.getOrDefault(dateStr, new EchartsVO());
echartsVO.setOrderDate(dateStr);
echartsVO.setSalesValue(Optional.ofNullable(echartsVO.getSalesValue())
.map(value -> new BigDecimal(value).divide(BigDecimal.valueOf(100)).toString())
.orElse("0"));
echartsVO.setOrderValue(Optional.ofNullable(echartsVO.getOrderValue()).orElse("0"));
return echartsVO;
}).sorted(Comparator.comparing(EchartsVO::getOrderDate)).collect(Collectors.toList());
return AjaxResult.success(result);
}
@Override
public AjaxResult getThisMonthDishSalesRanking() {
String date = getLastDate(-29);
return AjaxResult.success(mapper.getThisMonthDishSalesRanking(date, DateUtils.getDate()));
}
@Override
public AjaxResult getProportionOfOrderTypes(DataScreenDTO dataScreenDTO) {
String date = getLastDate(dataScreenDTO.getType().equals("2") ? -29 : -6);
SingleModelVO superMarketOrder = mapper.selectOrderByDate(date,DateUtils.getDate(), 4);
SingleModelVO canteenOrder = mapper.selectOrderByDate(date,DateUtils.getDate(), 11);
SingleModelVO preOrderMealOrder = mapper.selectOrderByDate(date,DateUtils.getDate(), 2);
BigDecimal total = superMarketOrder.getNum().add(canteenOrder.getNum()).add(preOrderMealOrder.getNum());
calculateProportion(superMarketOrder, total, "超市订单");
calculateProportion(canteenOrder, total, "食堂订单");
calculateProportion(preOrderMealOrder, total, "外卖订单");
return AjaxResult.success(Arrays.asList(superMarketOrder, canteenOrder, preOrderMealOrder));
}
private void calculateProportion(SingleModelVO order, BigDecimal total, String name) {
order.setName(name);
if (total.compareTo(BigDecimal.ZERO) == 0) {
order.setRate("0.0");
} else {
BigDecimal rate = order.getNum().divide(total, 4, BigDecimal.ROUND_HALF_UP)
.multiply(BigDecimal.valueOf(100));
order.setRate(String.format("%.2f", rate));
}
}
public static String getLastDate(int day) {
Calendar calendar = Calendar.getInstance();
// 日期减去一天
calendar.add(Calendar.DATE, day);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(calendar.getTime());
}
public static List<String> getDatesBetween(String startStr, String endStr, String pattern) {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
List<String> dates = new ArrayList<>();
try {
Date startDate = sdf.parse(startStr);
Date endDate = sdf.parse(endStr);
Calendar calendar = Calendar.getInstance();
calendar.setTime(startDate);
while (!calendar.getTime().after(endDate)) {
dates.add(sdf.format(calendar.getTime()));
calendar.add(Calendar.DATE, 1);
}
} catch (Exception e) {
e.printStackTrace();
}
return dates;
}
}

View File

@ -0,0 +1,11 @@
package com.bonus.canteen.core.dataScreening.vo;
import lombok.Data;
@Data
public class EchartsVO {
private String name;
private String salesValue;
private String orderValue;
private String orderDate;
}

View File

@ -0,0 +1,12 @@
package com.bonus.canteen.core.dataScreening.vo;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class SingleModelVO {
private BigDecimal num;
private String rate;
private String name;
}

View File

@ -8,7 +8,6 @@ import javax.validation.Valid;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bonus.canteen.core.common.utils.RedisUtil; import com.bonus.canteen.core.common.utils.RedisUtil;
import com.bonus.canteen.core.common.utils.TenantContextHolder;
import com.bonus.canteen.core.menu.dto.*; import com.bonus.canteen.core.menu.dto.*;
import com.bonus.canteen.core.menu.service.IMenuRecipeService; import com.bonus.canteen.core.menu.service.IMenuRecipeService;
import com.bonus.canteen.core.menu.vo.AllocRecipeStallVO; import com.bonus.canteen.core.menu.vo.AllocRecipeStallVO;

View File

@ -40,7 +40,6 @@ import com.bonus.canteen.core.menu.domain.MenuAppRecipe;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bonus.canteen.core.common.utils.RedisUtil; import com.bonus.canteen.core.common.utils.RedisUtil;
import com.bonus.canteen.core.common.utils.TenantContextHolder;
import com.bonus.canteen.core.menu.domain.*; import com.bonus.canteen.core.menu.domain.*;
import com.bonus.canteen.core.menu.dto.*; import com.bonus.canteen.core.menu.dto.*;
import com.bonus.canteen.core.menu.enums.*; import com.bonus.canteen.core.menu.enums.*;
@ -66,6 +65,7 @@ import com.bonus.common.core.exception.ServiceException;
import com.bonus.common.core.utils.StringUtils; import com.bonus.common.core.utils.StringUtils;
import com.bonus.common.core.web.domain.AjaxResult; import com.bonus.common.core.web.domain.AjaxResult;
import com.bonus.common.houqin.constant.DelFlagEnum; import com.bonus.common.houqin.constant.DelFlagEnum;
import com.bonus.common.houqin.constant.GlobalConstants;
import com.bonus.common.houqin.i18n.I18n; import com.bonus.common.houqin.i18n.I18n;
import com.bonus.common.houqin.mq.constant.LeMqConstant; import com.bonus.common.houqin.mq.constant.LeMqConstant;
import com.bonus.common.houqin.utils.LeBeanUtil; import com.bonus.common.houqin.utils.LeBeanUtil;
@ -527,7 +527,7 @@ public class MenuRecipeServiceImpl extends ServiceImpl<MenuRecipeMapper, MenuRec
this.menuRecipeMapper.deleteMenuRecipeDetailByIds(detailIdList); this.menuRecipeMapper.deleteMenuRecipeDetailByIds(detailIdList);
this.menuRecipeMapper.deleteMenuRecipeDishesByIds(detailIdList); this.menuRecipeMapper.deleteMenuRecipeDishesByIds(detailIdList);
} }
String redisKey = String.format("yst:%s:recipe:%s:detail:%s", TenantContextHolder.getTenantId(), recipeId, "*"); String redisKey = String.format("yst:%s:recipe:%s:detail:%s", GlobalConstants.TENANT_ID, recipeId, "*");
RedisUtil.deleteByPattern(redisKey); RedisUtil.deleteByPattern(redisKey);
MqUtil.sendDataChange(recipeId, LeMqConstant.DataChangeType.REMOVE, LeMqConstant.Topic.DATA_CHANGE_RECIPE); MqUtil.sendDataChange(recipeId, LeMqConstant.DataChangeType.REMOVE, LeMqConstant.Topic.DATA_CHANGE_RECIPE);
@ -745,7 +745,7 @@ private IMenuRecipeDetailService menuRecipeDetailService;
} }
MqUtil.sendDataChange(menuRecipe, type, LeMqConstant.Topic.DATA_CHANGE_RECIPE); MqUtil.sendDataChange(menuRecipe, type, LeMqConstant.Topic.DATA_CHANGE_RECIPE);
redisKey = String.format("yst:%s:recipe:%s:detail:%s", TenantContextHolder.getTenantId(), result.getRecipeId(), "*"); redisKey = String.format("yst:%s:recipe:%s:detail:%s", GlobalConstants.TENANT_ID, result.getRecipeId(), "*");
RedisUtil.deleteByPattern(redisKey); RedisUtil.deleteByPattern(redisKey);
//sendMq(menuRecipe); //sendMq(menuRecipe);
} }

View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bonus.canteen.core.dataScreening.mapper.DataScreeningMapper">
<select id="selectTurnover" resultType="com.bonus.canteen.core.dataScreening.vo.SingleModelVO">
select
IFNULL(sum( real_amount ),0) AS num
from order_info
where order_date = #{date} and pay_state = '3' and order_state in (1,2)
<if test="type != null">
<choose>
<when test="type == 11">
and order_type in (2,11)
</when>
<otherwise>
and order_type = #{type}
</otherwise>
</choose>
</if>
</select>
<select id="selectOrder" resultType="com.bonus.canteen.core.dataScreening.vo.SingleModelVO">
select
IFNULL(count( 1 ),0) AS num
from order_info
where order_date = #{date} and pay_state = '3' and order_state in (1,2)
<if test="type != null">
<choose>
<when test="type == 11">
and order_type in (2,11)
</when>
<otherwise>
and order_type = #{type}
</otherwise>
</choose>
</if>
</select>
<select id="selectPerson" resultType="com.bonus.canteen.core.dataScreening.vo.SingleModelVO">
select
count( DISTINCT user_id ) AS num
from order_info
where order_date = #{date} and pay_state = '3' and order_state in (1,2)
<if test="type != null">
<choose>
<when test="type == 11">
and order_type in (2,11)
</when>
<otherwise>
and order_type = #{type}
</otherwise>
</choose>
</if>
</select>
<select id="selectDish" resultType="com.bonus.canteen.core.dataScreening.vo.SingleModelVO">
select
count( DISTINCT mrd2.dishes_id ) AS num
from menu_recipe_detail mrd
left join menu_recipe_dishes mrd2 on mrd.detail_id = mrd2.detail_id
where mrd.apply_date = #{date} and mrd.detail_type = '2' and mrd.del_flag = '2'
</select>
<select id="getCanteenOrdersAndSalesTrends" resultType="com.bonus.canteen.core.dataScreening.vo.EchartsVO">
SELECT
IFNULL( count( 1 ), 0 ) AS orderValue,
IFNULL(sum( real_amount ),0) AS salesValue,
order_date as orderDate
FROM
order_info
WHERE
order_date BETWEEN #{startDate} and #{endDate} and pay_state = '3' and order_state in (1,2) and order_type in (2,11)
GROUP BY order_date
order by order_date
</select>
<select id="getSupermarketOrderAndSalesTrend"
resultType="com.bonus.canteen.core.dataScreening.vo.EchartsVO">
SELECT
IFNULL( count( 1 ), 0 ) AS orderValue,
IFNULL(sum( real_amount ),0) AS salesValue,
order_date as orderDate
FROM
order_info
WHERE
order_date BETWEEN #{startDate} and #{endDate} and pay_state = '3' and order_state in (1,2) and order_type = '4'
GROUP BY order_date
order by order_date
</select>
<select id="getThisMonthDishSalesRanking" resultType="com.bonus.canteen.core.dataScreening.vo.SingleModelVO">
SELECT
sum(od.quantity) AS num,
md.dishes_name as name
FROM
order_detail od
left join order_info oi on oi.order_id = od.order_id
left join menu_dishes md on md.dishes_id = od.goods_id
WHERE
oi.order_date between #{startDate} and #{endDate} and md.dishes_name is not null
group by od.goods_id
order by num desc
</select>
<select id="selectOrderByDate" resultType="com.bonus.canteen.core.dataScreening.vo.SingleModelVO">
select
IFNULL(count( 1 ),0) AS num
from order_info
where order_date between #{startDate} and #{endDate} and pay_state = '3' and order_state in (1,2)
<if test="type != null and type != ''">
and order_type = #{type}
</if>
</select>
</mapper>