This commit is contained in:
2025-11-22 14:02:20 +08:00
parent 98a04f8fbe
commit 381ae80eeb
59 changed files with 1930 additions and 1817 deletions

View File

@@ -1,54 +0,0 @@
//package com.czg.controller.admin;
//
//import com.czg.account.dto.ShopShareDTO;
//import com.czg.account.service.ShopShareService;
//import com.czg.account.vo.ShopShareRecordVO;
//import com.czg.account.vo.ShopShareVO;
//import com.czg.annotation.SaAdminCheckPermission;
//import com.czg.resp.CzgResult;
//import com.czg.sa.StpKit;
//import com.mybatisflex.core.paginate.Page;
//import jakarta.annotation.Resource;
//import org.springframework.validation.annotation.Validated;
//import org.springframework.web.bind.annotation.*;
//
///**
// * 小程序分享奖励管理
// * @author Administrator
// */
//@RestController
//@RequestMapping("/admin/shopShare")
//public class ShopShareController {
// @Resource
// private ShopShareService shopShareService;
//
// /**
// * 获取分享奖励配置
// */
// @SaAdminCheckPermission(value = "shopShare:list", name = "分享好友信息")
// @GetMapping
// public CzgResult<ShopShareVO> get() {
// return CzgResult.success(shopShareService.get(StpKit.USER.getShopId()));
// }
//
// /**
// * 修改分享奖励配置
// */
// @SaAdminCheckPermission(value = "shopShare:add", name = "分享好友信息添加")
// @PostMapping
// public CzgResult<Boolean> add(@RequestBody @Validated ShopShareDTO shopShareDTO) {
// return CzgResult.success(shopShareService.add(StpKit.USER.getShopId(), shopShareDTO));
// }
//
// /**
// * 分享奖励记录
// * @param key 邀请人/被邀请人手机号或昵称
// * @param status 0 非新用户 1 未领取 2 已领取 3 已使用 不传递为全部
// * @return 分页数据
// */
// @SaAdminCheckPermission(value = "shopShare:record", name = "分享邀请记录")
// @GetMapping("/record")
// public CzgResult<Page<ShopShareRecordVO>> record(String key, Integer status) {
// return CzgResult.success(shopShareService.recordPage(StpKit.USER.getShopId(), key, status));
// }
//}

View File

@@ -1,24 +0,0 @@
package com.czg.controller.admin;
import com.czg.account.service.ShopProdStatisticService;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Administrator
*/
@RestController
@RequestMapping("/admin/statistic")
public class ShopStatisticController {
@Resource
private ShopProdStatisticService shopProdStatisticService;
@GetMapping("/prod")
public CzgResult<?> getProduct(String name, String categoryId, String startTime, String endTime) {
return CzgResult.success(shopProdStatisticService.pageInfo(StpKit.USER.getShopId(), name, categoryId, startTime, endTime));
}
}

View File

@@ -176,7 +176,7 @@ public class OrderPayController {
/**
* 获取订单状态
* unpaid-待支付;in-production 制作中;wait-out 待取餐;;done-订单完成;refunding-申请退单;refund-退单;part-refund 部分退单;cancelled-取消订单
* unpaid-待支付;in-production 制作中;wait_out 待取餐;;done-订单完成;refunding-申请退单;refund-退单;part_refund 部分退单;cancelled-取消订单
*/
@GetMapping("/queryOrderStatus")
public CzgResult<String> queryOrderStatus(Long orderId) {

View File

@@ -0,0 +1,41 @@
package com.czg.controller;
import com.czg.task.StatisticTask;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDate;
/**
* @author ww
* @description
*/
@RestController
@RequestMapping("/task")
public class StatisticTaskController {
@Resource
private StatisticTask statisticTask;
/**
* 基础统计
*
* @param date 日期yyyy-MM-dd
*/
@GetMapping("/base")
public String baseStatistic(@RequestParam LocalDate date, @RequestParam Long shopId, @RequestParam String type) {
if ("order".equals(type)) {
statisticTask.statisticAndInsertOrder(shopId, date);
} else if ("prod".equals(type)) {
statisticTask.statisticAndInsertProd(shopId, date);
} else if ("table".equals(type)) {
statisticTask.statisticAndInsertTable(shopId, date);
} else {
return "未知错误";
}
return "success";
}
}

View File

@@ -1,27 +1,29 @@
package com.czg.controller.admin;
import com.czg.annotation.SaAdminCheckPermission;
import com.czg.annotation.SaStaffCheckPermission;
import com.czg.log.annotation.OperationLog;
import com.czg.order.entity.ShopOrderStatistic;
import com.czg.order.param.DataSummaryProductSaleParam;
import com.czg.order.entity.ShopProdStatistic;
import com.czg.order.param.DataSummaryTradeParam;
import com.czg.order.service.DataSummaryService;
import com.czg.order.vo.DataSummaryDateAmountVo;
import com.czg.order.vo.DataSummaryPayTypeVo;
import com.czg.order.vo.DataSummaryProductSaleRankingVo;
import com.czg.order.service.ShopOrderStatisticService;
import com.czg.order.service.ShopProdStatisticService;
import com.czg.order.vo.CountPayTypeVo;
import com.czg.order.vo.TotalVo;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit;
import com.czg.service.RedisService;
import com.czg.utils.AssertUtil;
import com.czg.validator.ValidatorUtil;
import com.czg.validator.group.DefaultGroup;
import com.mybatisflex.core.paginate.Page;
import jakarta.annotation.Resource;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 数据统计
*
@@ -33,22 +35,31 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/admin/data/summary")
public class DataSummaryController {
private final DataSummaryService dataSummaryService;
@Resource
private RedisService redisService;
@Resource
private ShopOrderStatisticService orderStatisticService;
@Resource
private ShopProdStatisticService prodStatisticService;
/**
* 营业板块-上半部分
*/
@GetMapping("trade")
@OperationLog("营业板块-上半部分")
// @SaStaffCheckPermission("yun_xu_cha_kan_jing_ying_shu_ju")
@SaAdminCheckPermission(value = "dataSummary:trade", name = "营业板块-上半部分")
public CzgResult<ShopOrderStatistic> getTradeData(DataSummaryTradeParam param) {
Boolean hasKey = redisService.hasKey("task:statistic:date:");
if (hasKey) {
return CzgResult.failure("数据统计任务正在运行中,请稍后再试");
}
ValidatorUtil.validateEntity(param, DefaultGroup.class);
Long shopId = StpKit.USER.getShopId();
if (param.getShopId() == null) {
param.setShopId(shopId);
}
ShopOrderStatistic data = dataSummaryService.getArchiveTradeData(param);
ShopOrderStatistic data = orderStatisticService.getArchiveTradeData(param.getShopId(), param.getRangeType(), param.getBeginDate(), param.getEndDate());
return CzgResult.success(data);
}
@@ -57,15 +68,14 @@ public class DataSummaryController {
*/
@GetMapping("productSaleDate")
@OperationLog("商品销售-右下")
// @SaStaffCheckPermission("yun_xu_cha_kan_jing_ying_shu_ju")
@SaAdminCheckPermission(value = "dataSummary:productSaleData", name = "商品销售-右下")
public CzgResult<Page<DataSummaryProductSaleRankingVo>> getProductSaleData(DataSummaryProductSaleParam param) {
public CzgResult<List<ShopProdStatistic>> getProductSaleData(DataSummaryTradeParam param) {
ValidatorUtil.validateEntity(param, DefaultGroup.class);
Long shopId = StpKit.USER.getShopId();
if (param.getShopId() == null) {
param.setShopId(shopId);
}
Page<DataSummaryProductSaleRankingVo> data = dataSummaryService.getProductSaleRankingPage(param);
List<ShopProdStatistic> data = prodStatisticService.getArchiveTradeDataBy20(param.getShopId(), param.getRangeType(), param.getBeginDate(), param.getEndDate());
return CzgResult.success(data);
}
@@ -77,32 +87,30 @@ public class DataSummaryController {
*/
@GetMapping("dateAmount")
@OperationLog("销售趋势柱状图 左下")
// @SaStaffCheckPermission("yun_xu_cha_kan_jing_ying_shu_ju")
@SaAdminCheckPermission(value = "dataSummary:dateAmount", name = "销售趋势柱状图 左下")
public CzgResult<DataSummaryDateAmountVo> getDateAmount(@RequestParam Integer day, @RequestParam(required = false) Long shopId) {
public CzgResult<List<TotalVo>> getDateAmount(@RequestParam Integer day, @RequestParam(required = false) Long shopId) {
AssertUtil.isNull(day, "天数不能为空");
if (shopId == null) {
shopId = StpKit.USER.getShopId();
}
DataSummaryDateAmountVo data = dataSummaryService.getSummaryAmountData(shopId, day);
List<TotalVo> data = orderStatisticService.getDateAmount(shopId, day);
return CzgResult.success(data);
}
/**
* 支付占比饼图 左下
*
* @param day 天数
* @param day 天数
* @param shopId 店铺id
*/
@GetMapping("datePayType")
@OperationLog("支付占比饼图 左下")
// @SaStaffCheckPermission("yun_xu_cha_kan_jing_ying_shu_ju")
@SaAdminCheckPermission(value = "dataSummary:datePayType", name = "支付占比饼图 左下2")
public CzgResult<DataSummaryPayTypeVo> shopSummaryPayType(@RequestParam Integer day, @RequestParam(required = false) Long shopId) {
public CzgResult<List<CountPayTypeVo>> shopSummaryPayType(@RequestParam Integer day, @RequestParam(required = false) Long shopId) {
if (shopId == null) {
shopId = StpKit.USER.getShopId(0L);
shopId = StpKit.USER.getShopId();
}
DataSummaryPayTypeVo data = dataSummaryService.getSummaryPayTypeData(shopId, day);
List<CountPayTypeVo> data = orderStatisticService.getSummaryPayTypeData(shopId, day);
return CzgResult.success(data);
}
}

View File

@@ -1,14 +1,13 @@
package com.czg.controller.admin;
import com.czg.log.annotation.OperationLog;
import com.czg.order.entity.ShopProdStatistic;
import com.czg.order.param.SaleSummaryCountParam;
import com.czg.order.service.SaleSummaryService;
import com.czg.order.service.ShopProdStatisticService;
import com.czg.order.vo.SaleSummaryCountVo;
import com.czg.order.vo.SaleSummaryInfoVo;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit;
import com.mybatisflex.core.paginate.Page;
import com.pig4cloud.plugin.excel.annotation.ResponseExcel;
import jakarta.annotation.Resource;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
@@ -28,7 +27,8 @@ import java.util.List;
@RestController
@RequestMapping("/admin/sale/summary")
public class SaleSummaryController {
private final SaleSummaryService saleSummaryService;
@Resource
private ShopProdStatisticService prodStatisticService;
/**
* 统计
@@ -37,11 +37,12 @@ public class SaleSummaryController {
@OperationLog("统计")
//@SaAdminCheckPermission("saleSummary:count")
public CzgResult<SaleSummaryCountVo> summaryCount(SaleSummaryCountParam param) {
Long shopId = StpKit.USER.getShopId(0L);
Long shopId = StpKit.USER.getShopId();
if (param.getShopId() == null) {
param.setShopId(shopId);
}
SaleSummaryCountVo data = saleSummaryService.summaryCount(param);
SaleSummaryCountVo data = prodStatisticService.summaryCount(
param.getShopId(), param.getProductName(), param.getRangeType(), param.getBeginDate(), param.getEndDate());
return CzgResult.success(data);
}
@@ -51,28 +52,14 @@ public class SaleSummaryController {
@GetMapping("page")
@OperationLog("分页")
//@SaAdminCheckPermission("saleSummary:page")
public CzgResult<Page<SaleSummaryInfoVo>> summaryPage(SaleSummaryCountParam param) {
Long shopId = StpKit.USER.getShopId(0L);
public CzgResult<List<ShopProdStatistic>> summaryPage(SaleSummaryCountParam param) {
Long shopId = StpKit.USER.getShopId();
if (param.getShopId() == null) {
param.setShopId(shopId);
}
Page<SaleSummaryInfoVo> page = saleSummaryService.summaryPage(param);
return CzgResult.success(page);
}
/**
* 导出
*/
@ResponseExcel(name = "销售统计明细")
@GetMapping("/export")
@OperationLog("导出")
//@SaAdminCheckPermission("saleSummary:export")
public List<SaleSummaryInfoVo> summaryExport(SaleSummaryCountParam param) {
Long shopId = StpKit.USER.getShopId(0L);
if (param.getShopId() == null) {
param.setShopId(shopId);
}
return saleSummaryService.summaryList(param);
List<ShopProdStatistic> list = prodStatisticService.getArchiveTradeData(
param.getShopId(),param.getProductName(), param.getRangeType(), param.getBeginDate(), param.getEndDate());
return CzgResult.success(list);
}
}

View File

@@ -3,13 +3,16 @@ package com.czg.controller.admin;
import com.czg.handel.ExcelMergeHandler;
import com.czg.handel.TableRefundCellHandel;
import com.czg.log.annotation.OperationLog;
import com.czg.order.entity.ShopTableOrderStatistic;
import com.czg.order.param.DataSummaryTradeParam;
import com.czg.order.param.TableSummaryParam;
import com.czg.order.service.ShopTableOrderStatisticService;
import com.czg.order.service.TableSummaryService;
import com.czg.order.vo.TableSummaryExportVo;
import com.czg.order.vo.TableSummaryInfoVo;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit;
import com.pig4cloud.plugin.excel.annotation.ResponseExcel;
import jakarta.annotation.Resource;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -29,19 +32,22 @@ import java.util.List;
public class TableSummaryController {
private final TableSummaryService tableSummaryService;
@Resource
private ShopTableOrderStatisticService tableOrderStatisticService;
/**
* 统计
*/
@GetMapping("list")
@OperationLog("统计")
//@SaAdminCheckPermission("tableSummary:list")
public CzgResult<List<TableSummaryInfoVo>> summaryList(TableSummaryParam param) {
Long shopId = StpKit.USER.getShopId(0L);
public CzgResult<List<ShopTableOrderStatistic>> summaryList(DataSummaryTradeParam param) {
Long shopId = StpKit.USER.getShopId();
if (param.getShopId() == null) {
param.setShopId(shopId);
}
List<TableSummaryInfoVo> data = tableSummaryService.summaryList(param);
return CzgResult.success(data);
List<ShopTableOrderStatistic> archiveTradeData = tableOrderStatisticService.getArchiveTradeData(param.getShopId(), param.getRangeType(), param.getBeginDate(), param.getEndDate());
return CzgResult.success(archiveTradeData);
}
/**

View File

@@ -1,21 +1,14 @@
package com.czg.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.czg.order.entity.ShopOrderStatistic;
import com.czg.order.entity.ShopProdStatistic;
import com.czg.order.entity.ShopTableOrderStatistic;
import com.czg.account.service.ShopInfoService;
import com.czg.order.service.ShopOrderStatisticService;
import com.czg.order.service.ShopProdStatisticService;
import com.czg.order.service.ShopTableOrderStatisticService;
import com.czg.service.order.mapper.ShopOrderStatisticMapper;
import com.czg.service.order.mapper.ShopProdStatisticMapper;
import com.czg.service.order.mapper.ShopTableOrderStatisticMapper;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.DbChain;
import com.czg.service.RedisService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@@ -28,87 +21,73 @@ import java.util.List;
@Component
@Slf4j
public class StatisticTask {
@DubboReference
private ShopInfoService shopInfoService;
@Resource
private ShopOrderStatisticService orderStatisticService;
@Resource
private RedisService redisService;
@Resource
private ShopTableOrderStatisticService shopTableOrderStatisticService;
@Resource
private ShopProdStatisticService shopProdStatisticService;
@Resource
private ShopOrderStatisticService shopOrderStatisticService;
@Resource
private ShopOrderStatisticMapper shopOrderStatisticMapper;
@Resource
private ShopProdStatisticMapper shopProdStatisticMapper;
@Resource
private ShopTableOrderStatisticMapper shopTableOrderStatisticMapper;
//每天 00点15分 执行 开始统计数据
@Scheduled(cron = "0 15 0 * * ? ")
public void run() {
log.info("统计数据,定时任务执行");
long start = System.currentTimeMillis();
// 获取前一天
LocalDate yesterday = LocalDate.now().minusDays(1);
baseStatistic(yesterday);
log.info("统计数据,定时任务执行完毕,耗时:{}ms", start - System.currentTimeMillis());
}
/**
* 基础统计
*
* @param dateTime 日期时间
* @param date 日期yyyy-MM-dd
*/
private void baseStatistic(DateTime dateTime) {
try {
shopOrderStatisticService.statistic(dateTime);
} catch (Exception e) {
log.error("统计订单数据失败", e);
private void baseStatistic(LocalDate date) {
List<Long> shopIdList = shopInfoService.getShopIdList();
if (CollUtil.isEmpty(shopIdList)) {
return;
}
try {
shopProdStatisticService.statistic(dateTime);
} catch (Exception e) {
log.error("统计商品数据失败", e);
redisService.set("task:statistic:date:", "");
for (Long shopId : shopIdList) {
statisticAndInsertOrder(shopId, date);
statisticAndInsertProd(shopId, date);
statisticAndInsertTable(shopId, date);
}
redisService.del("task:statistic:date:");
}
public void statisticAndInsertOrder(Long shopId, LocalDate date) {
try {
shopTableOrderStatisticService.statistic(dateTime);
orderStatisticService.statisticAndInsert(shopId, date);
} catch (Exception e) {
log.error("统计桌台数据失败", e);
log.error("统计订单数据失败店铺id:{},日期:{}", shopId, date, e);
}
}
// @Scheduled(cron = "1/6 * * * * ? ")
@Scheduled(cron = "0 0 8 * * ?")
public void run() {
long start = System.currentTimeMillis();
log.info("定时任务执行,开始统计数据");
// 获取前一天
DateTime yesterday = DateUtil.yesterday();
baseStatistic(yesterday);
log.info("定时任务执行完毕,耗时:{}ms", start - System.currentTimeMillis());
}
@Scheduled(cron = "0 0,15,30,45 * * * ? ")
public void run2() {
long start = System.currentTimeMillis();
log.info("定时任务2执行开始统计数据");
// 获取当天
DateTime today = DateUtil.date();
baseStatistic(today);
log.info("定时任务2执行完毕耗时:{}ms", start - System.currentTimeMillis());
}
/**
* 统计历史数据
*/
public void statisticHistoryData() {
// 指定开始日期
LocalDate startDate = LocalDate.of(2024, 3, 1);
// 指定结束日期
LocalDate endDate = LocalDate.now();
List<Long> shopIdList = DbChain.table("tb_shop_info").select("id").objListAs(Long.class);
List<List<Long>> split = CollUtil.split(shopIdList, 10);
// 1.清除历史统计的数据
for (List<Long> splitIdList : split) {
splitIdList.parallelStream().forEach(shopId -> {
shopOrderStatisticMapper.deleteByQuery(QueryWrapper.create().eq(ShopOrderStatistic::getShopId, shopId));
shopProdStatisticMapper.deleteByQuery(QueryWrapper.create().eq(ShopProdStatistic::getShopId, shopId));
shopTableOrderStatisticMapper.deleteByQuery(QueryWrapper.create().eq(ShopTableOrderStatistic::getShopId, shopId));
});
public void statisticAndInsertProd(Long shopId, LocalDate date) {
try {
shopProdStatisticService.statisticAndInsert(shopId, date);
} catch (Exception e) {
log.error("统计商品数据失败店铺id:{},日期:{}", shopId, date, e);
}
// 2.开始从2024-3-1开始统计数据
startDate.datesUntil(endDate.plusDays(1)).forEach(date -> {
System.out.println(date.toString());
DateTime dateTime = DateUtil.parseDate(date.toString());
System.out.println(dateTime);
baseStatistic(dateTime);
});
}
public void statisticAndInsertTable(Long shopId, LocalDate date) {
try {
shopTableOrderStatisticService.statisticAndInsert(shopId, date);
} catch (Exception e) {
log.error("统计桌台数据失败店铺id:{},日期:{}", shopId, date, e);
}
}
}

View File

@@ -124,7 +124,7 @@ public class OrderDetailDTO implements Serializable {
private String discountSaleNote;
/**
* 状态: wait-pay 待支付;in-production 制作中;wait-out 待取餐;refunding 退款中; part-refund 部分退单; refund-退单; done 完成;
* 状态: wait-pay 待支付;in-production 制作中;wait_out 待取餐;refunding 退款中; part_refund 部分退单; refund-退单; done 完成;
*/
private String status;

View File

@@ -1,70 +0,0 @@
package com.czg.account.entity;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.Date;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 实体类。
*
* @author zs
* @since 2025-03-06
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Table("tb_shop_prod_statistic")
public class ShopProdStatistic implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Id(keyType = KeyType.Auto)
private Long id;
/**
* 商品id
*/
private Long prodId;
/**
* 销售数量
*/
private Long saleNum;
/**
* 销售金额
*/
private BigDecimal saleAmount;
/**
* 退单量
*/
private Long refundNum;
/**
* 退单金额
*/
private BigDecimal refundAmount;
/**
* 店铺id
*/
private Long shopId;
/**
* 创建时间
*/
private Date createDay;
}

View File

@@ -16,6 +16,7 @@ import java.util.List;
*/
public interface ShopInfoService extends IService<ShopInfo> {
@Override
ShopInfo getById(Serializable id) throws CzgException;
Page<ShopInfo> get(PageDTO pageDTO, String shopName, Integer status, Integer isHeadShop);
@@ -40,4 +41,11 @@ public interface ShopInfoService extends IService<ShopInfo> {
List<ShopInfo> getByMainIdOrList(Long mainShopId, List<Long> shopIdList, String shopName);
BigDecimal updateAmount(Long id, BigDecimal amount);
/**
* 获取所有未删除的 过期时间>三天前 店铺id列表
*
* @return 店铺id列表
*/
List<Long> getShopIdList();
}

View File

@@ -1,16 +0,0 @@
package com.czg.account.service;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.service.IService;
import com.czg.account.entity.ShopProdStatistic;
/**
* 服务层。
*
* @author zs
* @since 2025-03-06
*/
public interface ShopProdStatisticService extends IService<ShopProdStatistic> {
Page<?> pageInfo(Long shopId, String name, String classifyId, String startTime, String endTime);
}

View File

@@ -1,23 +0,0 @@
//package com.czg.account.service;
//
//import com.czg.account.dto.ShopShareDTO;
//import com.czg.account.vo.ShopShareRecordVO;
//import com.czg.account.vo.ShopShareVO;
//import com.mybatisflex.core.paginate.Page;
//import com.mybatisflex.core.service.IService;
//import com.czg.account.entity.ShopShare;
//
///**
// * 店铺分享 服务层。
// *
// * @author zs
// * @since 2025-03-05
// */
//public interface ShopShareService extends IService<ShopShare> {
//
// ShopShareVO get(Long shopId);
//
// Boolean add(Long shopId, ShopShareDTO shopShareDTO);
//
// Page<ShopShareRecordVO> recordPage(Long shopId, String key, Integer status);
//}

View File

@@ -1,14 +0,0 @@
package com.czg.account.vo;
import com.czg.account.entity.ShopProdStatistic;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author Administrator
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class ShopProdStatisticVO extends ShopProdStatistic {
private String name;
}

View File

@@ -63,6 +63,10 @@ public class OrderInfoDTO implements Serializable {
* 抹零金额 减免多少钱
*/
private BigDecimal roundAmount;
/**
* 优惠总金额
*/
private BigDecimal discountAllAmount;
/**
* 订单金额 (扣除各类折扣)
@@ -104,10 +108,10 @@ public class OrderInfoDTO implements Serializable {
*/
private BigDecimal discountAmount;
/**
* 折扣比例
*/
private BigDecimal discountRatio;
// /**
// * 折扣比例
// */
// private BigDecimal discountRatio;
/**
* 打包费
@@ -161,7 +165,7 @@ public class OrderInfoDTO implements Serializable {
private String payType;
/**
* 状态: unpaid-待支付;in-production 制作中;wait-out 待取餐;;done-订单完成;refunding-申请退单;refund-退单;part-refund 部分退单;cancelled-取消订单
* 状态: unpaid-待支付;in-production 制作中;wait_out 待取餐;;done-订单完成;refunding-申请退单;refund-退单;part_refund 部分退单;cancelled-取消订单
*/
private String status;

View File

@@ -70,7 +70,7 @@ public class OrderInfoQueryDTO implements Serializable {
/**
* 状态: unpaid-待支付;in-production 制作中;wait-out 待取餐;;done-订单完成;refunding-申请退单;refund-退单;part-refund 部分退单;cancelled-取消订单
* 状态: unpaid-待支付;in-production 制作中;wait_out 待取餐;;done-订单完成;refunding-申请退单;refund-退单;part_refund 部分退单;cancelled-取消订单
*/
private String status;

View File

@@ -128,7 +128,7 @@ public class OrderDetail implements Serializable {
private String discountSaleNote;
/**
* 状态: in-production 制作中;wait-out 待取餐;refunding 退款中; part-refund 部分退单; refund-退单; done 完成;
* 状态: in-production 制作中;wait_out 待取餐;refunding 退款中; part_refund 部分退单; refund-退单; done 完成;
*/
private String status;

View File

@@ -73,6 +73,10 @@ public class OrderInfo implements Serializable {
* 订单金额 (扣除各类折扣)
*/
private BigDecimal orderAmount;
/**
* 优惠总金额
*/
private BigDecimal discountAllAmount;
/**
* 实际支付金额
@@ -122,10 +126,10 @@ public class OrderInfo implements Serializable {
*/
private BigDecimal discountAmount;
/**
* 折扣比例
*/
private BigDecimal discountRatio;
// /**
// * 折扣比例
// */
// private BigDecimal discountRatio;
/**
* 打包费
@@ -186,7 +190,7 @@ public class OrderInfo implements Serializable {
/**
* {@link com.czg.service.order.enums.OrderStatusEnums}
* 状态: unpaid-待支付;in-production 制作中;wait-out 待取餐;;done-订单完成;refunding-申请退单;refund-退单;part-refund 部分退单;cancelled-取消订单
* 状态: unpaid-待支付;in-production 制作中;wait_out 待取餐;;done-订单完成;refunding-申请退单;refund-退单;part_refund 部分退单;cancelled-取消订单
*/
private String status;
@@ -319,4 +323,21 @@ public class OrderInfo implements Serializable {
public BigDecimal getPackFee() {
return packFee == null ? BigDecimal.ZERO : packFee;
}
/**
* 计算并设置所有优惠金额的总和
*/
public void initDiscountAllAmount() {
this.discountAllAmount = BigDecimal.ZERO
.add(this.getProductCouponDiscountAmount() != null ? this.getProductCouponDiscountAmount() : BigDecimal.ZERO)
.add(this.getOtherCouponDiscountAmount() != null ? this.getOtherCouponDiscountAmount() : BigDecimal.ZERO)
.add(this.getPointsDiscountAmount() != null ? this.getPointsDiscountAmount() : BigDecimal.ZERO)
.add(this.getNewCustomerDiscountAmount() != null ? this.getNewCustomerDiscountAmount() : BigDecimal.ZERO)
.add(this.getDiscountAmount() != null ? this.getDiscountAmount() : BigDecimal.ZERO)
.add(this.getDiscountActAmount() != null ? this.getDiscountActAmount() : BigDecimal.ZERO)
.add(this.getVipDiscountAmount() != null ? this.getVipDiscountAmount() : BigDecimal.ZERO);
// 如果需要加上抹零金额,可以取消下面这行注释
// .add(this.getRoundAmount() != null ? this.getRoundAmount() : BigDecimal.ZERO);
}
}

View File

@@ -1,10 +1,11 @@
package com.czg.order.entity;
import com.alibaba.fastjson2.annotation.JSONField;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -15,13 +16,13 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* 实体类。
* 店铺订单统计报表 实体类。
*
* @author zs
* @since 2025-03-07
* @author ww
* @since 2025-11-20
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("tb_shop_order_statistic")
@@ -30,151 +31,469 @@ public class ShopOrderStatistic implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 检查订单统计数据是否有效
*
* @param stat 订单统计数据
* @return 是否有效
*/
public boolean hasValidData(ShopOrderStatistic stat) {
if (stat == null) {
return false;
}
// 检查BigDecimal类型字段 检查Long类型字段
return checkBigDecimalFields(stat) && checkLongFields(stat);
}
/**
*
* 初始化空白数据 标识为 无效数据 valid = false
*/
public void init() {
this.valid = false;
this.originAmount = zero;
this.payAmount = zero;
this.refundAmount = zero;
this.onlineRefundAmount = zero;
this.cashRefundAmount = zero;
this.memberRefundAmount = zero;
this.memberPayAmount = zero;
this.memberPayCount = 0L;
this.rechargeAmount = zero;
this.onlineRechargeAmount = zero;
this.cashRechargeAmount = zero;
this.giveAmount = zero;
this.rechargeRefundAmount = zero;
this.onlineRechargeRefundAmount = zero;
this.cashRechargeRefundAmount = zero;
this.newMemberCount = 0L;
this.customerCount = 0L;
this.orderCount = 0L;
this.tableCount = 0L;
this.avgPayAmount = zero;
this.turnoverRate = zero;
this.profitAmount = zero;
this.productCostAmount = zero;
this.profitRate = zero;
this.netProfitAmount = zero;
this.netProfitRate = zero;
this.discountAmount = zero;
this.discountCount = 0L;
this.newCustomerDiscountAmount = zero;
this.fullDiscountAmount = zero;
this.couponDiscountAmount = zero;
this.pointDiscountAmount = zero;
this.backDiscountAmount = zero;
this.memberDiscountAmount = zero;
this.orderPriceDiscountAmount = zero;
this.onlinePayAmount = zero;
this.cashPayAmount = zero;
this.wechatPayAmount = zero;
this.alipayPayAmount = zero;
this.backScanPayAmount = zero;
this.mainScanPayAmount = zero;
this.creditPayAmount = zero;
this.cashPayCount = 0L;
this.wechatPayCount = 0L;
this.alipayPayCount = 0L;
this.backScanPayCount = 0L;
this.mainScanPayCount = 0L;
this.creditPayCount = 0L;
}
/**
* 主键ID
*/
@Id(keyType = KeyType.Auto)
private Integer id;
/**
* 销售额
* 店铺id
*/
private BigDecimal saleAmount = BigDecimal.ZERO;
private Long shopId;
/**
* 统计日期 yyyy-MM-dd
*/
private LocalDate statisticDate;
//*********************以下为订单金额********************************************************************************
/**
* 销售数量
* 订单金额 订单原金额
*/
private Long saleCount = 0L;
private BigDecimal originAmount;
/**
* 实付金额 (线上付款 现金支付 会员支付 挂账)
*/
private BigDecimal payAmount;
/**
* 线上付款金额
*/
private BigDecimal onlinePayAmount;
/**
* 订单退款 退款金额(原路返回 现金退款)
*/
private BigDecimal refundAmount;
/**
* 订单退款 线上退款金额
*/
private BigDecimal onlineRefundAmount;
/**
* 订单退款 线下退款金额/现金退款金额
*/
private BigDecimal cashRefundAmount;
/**
* 订单退款 会员退款金额
*/
private BigDecimal memberRefundAmount;
/**
* 优惠金额
* 会员消费 会员订单支付金额
*/
private BigDecimal discountAmount = BigDecimal.ZERO;
private BigDecimal memberPayAmount;
/**
* 会员消费笔数
*/
private Long memberPayCount;
//*********************以下为充值金额********************************************************************************
/**
* 会员充值 充值金额(包含现金支付) 不包括赠送 多店 按 主店算
*/
private BigDecimal rechargeAmount;
/**
* 会员充值 线上充值金额
*/
private BigDecimal onlineRechargeAmount;
/**
* 会员充值 现金充值金额
*/
private BigDecimal cashRechargeAmount;
/**
* 会员充值 充值赠送金额
*/
private BigDecimal giveAmount;
/**
* 优惠笔数
* 会员充值退款 充值退款金额(线上退款+现金退款)
*/
private Long discountCount = 0L;
private BigDecimal rechargeRefundAmount;
/**
* 退款金额
* 会员充值退款 线上退款
*/
private BigDecimal refundAmount = BigDecimal.ZERO;
private BigDecimal onlineRechargeRefundAmount;
/**
* 退款笔数
* 会员充值退款 现金退款金额
*/
private Long refundCount = 0L;
/**
* 微信支付笔数
*/
private Long wechatPayCount = 0L;
/**
* 微信支付金额
*/
private BigDecimal wechatPayAmount = BigDecimal.ZERO;
/**
* 支付宝支付笔数
*/
private Long aliPayCount = 0L;
/**
* 支付宝支付金额
*/
private BigDecimal aliPayAmount = BigDecimal.ZERO;
/**
* 挂账支付笔数
*/
private Long creditPayCount = 0L;
/**
* 挂账支付金额
*/
private BigDecimal creditPayAmount = BigDecimal.ZERO;
/**
* 会员支付笔数
*/
private Long memberPayCount = 0L;
/**
* 会员支付金额
*/
private BigDecimal memberPayAmount = BigDecimal.ZERO;
/**
* 主扫支付笔数
*/
private Long scanPayCount = 0L;
/**
* 主扫支付金额
*/
private BigDecimal scanPayAmount = BigDecimal.ZERO;
/**
* 被扫支付笔数
*/
private Long backScanPayCount = 0L;
/**
* 被扫支付金额
*/
private BigDecimal backScanPayAmount = BigDecimal.ZERO;
/**
* 被扫支付笔数
*/
private Long h5PayCount = 0L;
/**
* 被扫支付金额
*/
private BigDecimal h5PayAmount = BigDecimal.ZERO;
/**
* 现金支付笔数
*/
private Long cashPayCount = 0L;
/**
* 现金支付金额
*/
private BigDecimal cashPayAmount = BigDecimal.ZERO;
/**
* 充值金额
*/
private BigDecimal rechargeAmount = BigDecimal.ZERO;
/**
* 充值退款金额
*/
private BigDecimal rechargeRefundAmount = BigDecimal.ZERO;
/**
* 客单价
*/
private BigDecimal customerUnitPrice = BigDecimal.ZERO;
/**
* 翻台率
*/
private BigDecimal tableTurnoverRate = BigDecimal.ZERO;
private BigDecimal cashRechargeRefundAmount;
/**
* 新增会员数
*/
private Long newMemberCount = 0L;
private Long newMemberCount;
//*********************以下为客单价 翻台率 的计算********************************************************************************
/**
* 店铺id
* 就餐人数
*/
@JSONField(serialize = false)
private Long shopId;
private Long customerCount;
/**
* 订单数
*/
private Long orderCount;
/**
* 桌台数
*/
private Long tableCount;
/**
* 客单价
* 实付金额(包含现金支付 包含会员支付 包含挂账)/就餐人数
* 没有具体人数时默认一桌按照1人计算
*/
private BigDecimal avgPayAmount;
/**
* 翻台率
* (订单数-桌台数)/桌台数*100%
*/
private BigDecimal turnoverRate;
/**
* 毛利润(订单实付金额-商品成本)
*/
private BigDecimal profitAmount;
/**
* 商品成本
*/
private BigDecimal productCostAmount;
/**
* 毛利率(订单实付金额-商品成本)/订单实付金额*100%
*/
private BigDecimal profitRate;
/**
* 净利润
*/
private BigDecimal netProfitAmount;
/**
* 净利润率
*/
private BigDecimal netProfitRate;
//*********************以下为优惠金额********************************************************************************
/**
* 优惠总金额
*/
private BigDecimal discountAmount;
/**
* 优惠笔数
*/
private Long discountCount;
/**
* 新客立减金额
*/
private BigDecimal newCustomerDiscountAmount;
/**
* 满减活动金额
*/
private BigDecimal fullDiscountAmount;
/**
* 优惠券抵扣金额
*/
private BigDecimal couponDiscountAmount;
/**
* 积分抵扣金额
*/
private BigDecimal pointDiscountAmount;
/**
* 霸王餐金额
*/
private BigDecimal backDiscountAmount;
/**
* 会员整单折扣金额
*/
private BigDecimal memberDiscountAmount;
/**
* 订单改价金额
*/
private BigDecimal orderPriceDiscountAmount;
//*********************以下为支付金额********************************************************************************
/**
* 现金支付金额
*/
private BigDecimal cashPayAmount;
/**
* 现金支付笔数
*/
private Long cashPayCount;
/**
* 微信小程序支付金额
*/
private BigDecimal wechatPayAmount;
/**
* 微信小程序支付笔数
*/
private Long wechatPayCount;
/**
* 支付宝小程序支付金额
*/
private BigDecimal alipayPayAmount;
/**
* 支付宝小程序支付笔数
*/
private Long alipayPayCount;
/**
* 被扫收款金额
*/
private BigDecimal backScanPayAmount;
/**
* 被扫收款笔数
*/
private Long backScanPayCount;
/**
* 主扫收款金额
*/
private BigDecimal mainScanPayAmount;
/**
* 主扫收款笔数
*/
private Long mainScanPayCount;
/**
* 挂账支付金额
*/
private BigDecimal creditPayAmount;
/**
* 挂账支付笔数
*/
private Long creditPayCount;
/**
* 创建时间
*/
@JSONField(serialize = false, format = "yyyy-MM-dd")
private LocalDate createDay;
@Column(onInsertValue = "now()")
private LocalDateTime createTime;
/**
* 最近一次统计时间
* 更新时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
@Column(onInsertValue = "now()", onUpdateValue = "now()")
private LocalDateTime updateTime;
@Column(ignore = true)
BigDecimal zero = BigDecimal.ZERO;
/**
* 是否有效
*/
@Column(ignore = true)
private Boolean valid = true;
private static boolean checkBigDecimalFields(ShopOrderStatistic stat) {
return
isGreaterThanZero(stat.getOriginAmount()) ||
isGreaterThanZero(stat.getPayAmount()) ||
isGreaterThanZero(stat.getRefundAmount()) ||
isGreaterThanZero(stat.getOnlineRefundAmount()) ||
isGreaterThanZero(stat.getCashRefundAmount()) ||
isGreaterThanZero(stat.getMemberRefundAmount()) ||
isGreaterThanZero(stat.getMemberPayAmount()) ||
isGreaterThanZero(stat.getRechargeAmount()) ||
isGreaterThanZero(stat.getOnlineRechargeAmount()) ||
isGreaterThanZero(stat.getCashRechargeAmount()) ||
isGreaterThanZero(stat.getGiveAmount()) ||
isGreaterThanZero(stat.getRechargeRefundAmount()) ||
isGreaterThanZero(stat.getOnlineRechargeRefundAmount()) ||
isGreaterThanZero(stat.getCashRechargeRefundAmount()) ||
isGreaterThanZero(stat.getAvgPayAmount()) ||
isGreaterThanZero(stat.getTurnoverRate()) ||
isGreaterThanZero(stat.getProfitAmount()) ||
isGreaterThanZero(stat.getProductCostAmount()) ||
isGreaterThanZero(stat.getProfitRate()) ||
isGreaterThanZero(stat.getNetProfitAmount()) ||
isGreaterThanZero(stat.getNetProfitRate()) ||
isGreaterThanZero(stat.getDiscountAmount()) ||
isGreaterThanZero(stat.getNewCustomerDiscountAmount()) ||
isGreaterThanZero(stat.getFullDiscountAmount()) ||
isGreaterThanZero(stat.getCouponDiscountAmount()) ||
isGreaterThanZero(stat.getPointDiscountAmount()) ||
isGreaterThanZero(stat.getBackDiscountAmount()) ||
isGreaterThanZero(stat.getMemberDiscountAmount()) ||
isGreaterThanZero(stat.getOrderPriceDiscountAmount()) ||
isGreaterThanZero(stat.getOnlinePayAmount()) ||
isGreaterThanZero(stat.getCashPayAmount()) ||
isGreaterThanZero(stat.getWechatPayAmount()) ||
isGreaterThanZero(stat.getAlipayPayAmount()) ||
isGreaterThanZero(stat.getBackScanPayAmount()) ||
isGreaterThanZero(stat.getMainScanPayAmount()) ||
isGreaterThanZero(stat.getCreditPayAmount());
}
private static boolean checkLongFields(ShopOrderStatistic stat) {
return (stat.getMemberPayCount() != null && stat.getMemberPayCount() > 0) ||
(stat.getNewMemberCount() != null && stat.getNewMemberCount() > 0) ||
(stat.getCustomerCount() != null && stat.getCustomerCount() > 0) ||
(stat.getOrderCount() != null && stat.getOrderCount() > 0) ||
(stat.getTableCount() != null && stat.getTableCount() > 0) ||
(stat.getDiscountCount() != null && stat.getDiscountCount() > 0) ||
(stat.getCashPayCount() != null && stat.getCashPayCount() > 0) ||
(stat.getWechatPayCount() != null && stat.getWechatPayCount() > 0) ||
(stat.getAlipayPayCount() != null && stat.getAlipayPayCount() > 0) ||
(stat.getBackScanPayCount() != null && stat.getBackScanPayCount() > 0) ||
(stat.getMainScanPayCount() != null && stat.getMainScanPayCount() > 0) ||
(stat.getCreditPayCount() != null && stat.getCreditPayCount() > 0);
}
/**
* 合并两个统计对象
*/
public static ShopOrderStatistic mergeStatistics(ShopOrderStatistic stat1, ShopOrderStatistic stat2) {
if (stat2 == null) return stat1;
if (stat1 == null) return stat2;
if(!stat2.getValid()) return stat1;
if(!stat1.getValid()) return stat2;
ShopOrderStatistic result = new ShopOrderStatistic();
// 合并BigDecimal字段
result.setOriginAmount(safeAdd(stat1.getOriginAmount(), stat2.getOriginAmount()));
result.setPayAmount(safeAdd(stat1.getPayAmount(), stat2.getPayAmount()));
result.setRefundAmount(safeAdd(stat1.getRefundAmount(), stat2.getRefundAmount()));
result.setOnlineRefundAmount(safeAdd(stat1.getOnlineRefundAmount(), stat2.getOnlineRefundAmount()));
result.setCashRefundAmount(safeAdd(stat1.getCashRefundAmount(), stat2.getCashRefundAmount()));
result.setMemberRefundAmount(safeAdd(stat1.getMemberRefundAmount(), stat2.getMemberRefundAmount()));
result.setMemberPayAmount(safeAdd(stat1.getMemberPayAmount(), stat2.getMemberPayAmount()));
result.setRechargeAmount(safeAdd(stat1.getRechargeAmount(), stat2.getRechargeAmount()));
result.setOnlineRechargeAmount(safeAdd(stat1.getOnlineRechargeAmount(), stat2.getOnlineRechargeAmount()));
result.setCashRechargeAmount(safeAdd(stat1.getCashRechargeAmount(), stat2.getCashRechargeAmount()));
result.setGiveAmount(safeAdd(stat1.getGiveAmount(), stat2.getGiveAmount()));
result.setRechargeRefundAmount(safeAdd(stat1.getRechargeRefundAmount(), stat2.getRechargeRefundAmount()));
result.setOnlineRechargeRefundAmount(safeAdd(stat1.getOnlineRechargeRefundAmount(), stat2.getOnlineRechargeRefundAmount()));
result.setCashRechargeRefundAmount(safeAdd(stat1.getCashRechargeRefundAmount(), stat2.getCashRechargeRefundAmount()));
result.setAvgPayAmount(safeAdd(stat1.getAvgPayAmount(), stat2.getAvgPayAmount()));
result.setTurnoverRate(safeAdd(stat1.getTurnoverRate(), stat2.getTurnoverRate()));
result.setProfitAmount(safeAdd(stat1.getProfitAmount(), stat2.getProfitAmount()));
result.setProductCostAmount(safeAdd(stat1.getProductCostAmount(), stat2.getProductCostAmount()));
result.setProfitRate(safeAdd(stat1.getProfitRate(), stat2.getProfitRate()));
result.setNetProfitAmount(safeAdd(stat1.getNetProfitAmount(), stat2.getNetProfitAmount()));
result.setNetProfitRate(safeAdd(stat1.getNetProfitRate(), stat2.getNetProfitRate()));
result.setDiscountAmount(safeAdd(stat1.getDiscountAmount(), stat2.getDiscountAmount()));
result.setNewCustomerDiscountAmount(safeAdd(stat1.getNewCustomerDiscountAmount(), stat2.getNewCustomerDiscountAmount()));
result.setFullDiscountAmount(safeAdd(stat1.getFullDiscountAmount(), stat2.getFullDiscountAmount()));
result.setCouponDiscountAmount(safeAdd(stat1.getCouponDiscountAmount(), stat2.getCouponDiscountAmount()));
result.setPointDiscountAmount(safeAdd(stat1.getPointDiscountAmount(), stat2.getPointDiscountAmount()));
result.setBackDiscountAmount(safeAdd(stat1.getBackDiscountAmount(), stat2.getBackDiscountAmount()));
result.setMemberDiscountAmount(safeAdd(stat1.getMemberDiscountAmount(), stat2.getMemberDiscountAmount()));
result.setOrderPriceDiscountAmount(safeAdd(stat1.getOrderPriceDiscountAmount(), stat2.getOrderPriceDiscountAmount()));
result.setOnlinePayAmount(safeAdd(stat1.getOnlinePayAmount(), stat2.getOnlinePayAmount()));
result.setCashPayAmount(safeAdd(stat1.getCashPayAmount(), stat2.getCashPayAmount()));
result.setWechatPayAmount(safeAdd(stat1.getWechatPayAmount(), stat2.getWechatPayAmount()));
result.setAlipayPayAmount(safeAdd(stat1.getAlipayPayAmount(), stat2.getAlipayPayAmount()));
result.setBackScanPayAmount(safeAdd(stat1.getBackScanPayAmount(), stat2.getBackScanPayAmount()));
result.setMainScanPayAmount(safeAdd(stat1.getMainScanPayAmount(), stat2.getMainScanPayAmount()));
result.setCreditPayAmount(safeAdd(stat1.getCreditPayAmount(), stat2.getCreditPayAmount()));
// 合并Long字段
result.setMemberPayCount(safeAdd(stat1.getMemberPayCount(), stat2.getMemberPayCount()));
result.setNewMemberCount(safeAdd(stat1.getNewMemberCount(), stat2.getNewMemberCount()));
result.setCustomerCount(safeAdd(stat1.getCustomerCount(), stat2.getCustomerCount()));
result.setOrderCount(safeAdd(stat1.getOrderCount(), stat2.getOrderCount()));
result.setTableCount(safeAdd(stat1.getTableCount(), stat2.getTableCount()));
result.setDiscountCount(safeAdd(stat1.getDiscountCount(), stat2.getDiscountCount()));
result.setCashPayCount(safeAdd(stat1.getCashPayCount(), stat2.getCashPayCount()));
result.setWechatPayCount(safeAdd(stat1.getWechatPayCount(), stat2.getWechatPayCount()));
result.setAlipayPayCount(safeAdd(stat1.getAlipayPayCount(), stat2.getAlipayPayCount()));
result.setBackScanPayCount(safeAdd(stat1.getBackScanPayCount(), stat2.getBackScanPayCount()));
result.setMainScanPayCount(safeAdd(stat1.getMainScanPayCount(), stat2.getMainScanPayCount()));
result.setCreditPayCount(safeAdd(stat1.getCreditPayCount(), stat2.getCreditPayCount()));
return result;
}
/**
* 安全的BigDecimal相加
*/
private static BigDecimal safeAdd(BigDecimal num1, BigDecimal num2) {
if (num1 == null) return num2;
if (num2 == null) return num1;
return num1.add(num2);
}
/**
* 安全的Long相加
*/
private static Long safeAdd(Long num1, Long num2) {
if (num1 == null) return num2;
if (num2 == null) return num1;
return num1 + num2;
}
private static boolean isGreaterThanZero(BigDecimal value) {
return value != null && value.compareTo(BigDecimal.ZERO) > 0;
}
}

View File

@@ -1,5 +1,6 @@
package com.czg.order.entity;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
@@ -35,6 +36,11 @@ public class ShopProdStatistic implements Serializable {
* 商品id
*/
private Long prodId;
/**
* 商品名称
*/
@Column(ignore = true)
private String productName;
/**
* 销售数量
@@ -66,4 +72,25 @@ public class ShopProdStatistic implements Serializable {
*/
private Date createDay;
// 在 ShopProdStatistic.java 中添加以下方法
/**
* 判断当前统计数据是否有效销售数量、金额退单数量、金额均有值且大于0
* @return true=有效false=无效
*/
public boolean isValid() {
// 1. 校验所有字段是否为 null
if (saleCount == null || saleAmount == null || refundCount == null || refundAmount == null) {
return false;
}
// 2. 校验所有字段是否大于 0BigDecimal 需用 compareTo 比较,不能直接用 >
if (saleCount.compareTo(BigDecimal.ZERO) <= 0 ||
saleAmount.compareTo(BigDecimal.ZERO) <= 0 ||
refundCount.compareTo(BigDecimal.ZERO) <= 0 ||
refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
return false;
}
// 3. 所有条件满足,返回 true
return true;
}
}

View File

@@ -10,7 +10,9 @@ import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 台桌订单统计表 实体类。
@@ -18,6 +20,7 @@ import java.util.Date;
* @author zs
* @since 2025-03-07
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Table("tb_shop_table_order_statistic")
@@ -74,92 +77,58 @@ public class ShopTableOrderStatistic implements Serializable {
private BigDecimal refundAmount;
public Long getId() {
return id;
/**
* 判断是否有有效数据
*/
public boolean hasValidData() {
return (orderCount != null && orderCount > 0) ||
(orderAmount != null && orderAmount.compareTo(BigDecimal.ZERO) > 0) ||
(refundCount != null && refundCount > 0) ||
(refundAmount != null && refundAmount.compareTo(BigDecimal.ZERO) > 0);
}
public void setId(Long id) {
this.id = id;
public static List<ShopTableOrderStatistic> mergeWithStream(
List<ShopTableOrderStatistic> realTimeData,
List<ShopTableOrderStatistic> historyData) {
// 最简单的合并方式
return Stream.concat(realTimeData.stream(), historyData.stream())
.filter(item -> item != null && item.getTableId() != null)
.collect(Collectors.toMap(
ShopTableOrderStatistic::getTableId,
item -> item,
(item1, item2) -> {
ShopTableOrderStatistic merged = new ShopTableOrderStatistic();
merged.setTableId(item1.getTableId());
merged.setTableCode(item1.getTableCode());
merged.setTableName(item1.getTableName());
merged.setAreaName(item1.getAreaName());
// 数值字段求和
merged.setOrderCount(addLong(item1.getOrderCount(), item2.getOrderCount()));
merged.setOrderAmount(addBigDecimal(item1.getOrderAmount(), item2.getOrderAmount()));
merged.setRefundCount(addLong(item1.getRefundCount(), item2.getRefundCount()));
merged.setRefundAmount(addBigDecimal(item1.getRefundAmount(), item2.getRefundAmount()));
return merged;
}
))
.values()
.stream()
.sorted(Comparator.comparing(
(ShopTableOrderStatistic item) -> item.getOrderCount() != null ? item.getOrderCount() : 0L,
// 降序排列
Comparator.reverseOrder()
))
.toList();
}
public Long getTableId() {
return tableId;
// 辅助方法
private static Long addLong(Long a, Long b) {
return (a != null ? a : 0L) + (b != null ? b : 0L);
}
public void setTableId(Long tableId) {
this.tableId = tableId;
}
public String getTableCode() {
return tableCode;
}
public void setTableCode(String tableCode) {
this.tableCode = tableCode;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getAreaName() {
return areaName;
}
public void setAreaName(String areaName) {
this.areaName = areaName;
}
public Long getOrderCount() {
return orderCount;
}
public void setOrderCount(Long orderCount) {
this.orderCount = orderCount;
}
public BigDecimal getOrderAmount() {
return orderAmount;
}
public void setOrderAmount(BigDecimal orderAmount) {
this.orderAmount = orderAmount;
}
public Long getShopId() {
return shopId;
}
public void setShopId(Long shopId) {
this.shopId = shopId;
}
public Date getCreateDay() {
return createDay;
}
public void setCreateDay(Date createDay) {
this.createDay = createDay;
}
public Long getRefundCount() {
return refundCount;
}
public void setRefundCount(Long refundCount) {
this.refundCount = refundCount;
}
public BigDecimal getRefundAmount() {
return refundAmount;
}
public void setRefundAmount(BigDecimal refundAmount) {
this.refundAmount = refundAmount;
private static BigDecimal addBigDecimal(BigDecimal a, BigDecimal b) {
return (a != null ? a : BigDecimal.ZERO).add(b != null ? b : BigDecimal.ZERO);
}
}

View File

@@ -42,6 +42,10 @@ public enum PayEnums {
* 挂账支付
*/
CREDIT_PAY("credit_pay", "挂账支付"),
/**
* 霸王餐支付
*/
FREE_PAY("free_pay", "霸王餐支付"),
/**
* h5支付
*/
@@ -56,12 +60,4 @@ public enum PayEnums {
return Arrays.stream(values()).map(PayEnums::getValue).toList();
}
public static String getText(String value) {
PayEnums item = Arrays.stream(values()).filter(obj -> value.equals(obj.getValue())).findFirst().orElse(null);
if (item != null) {
return item.getMsg();
}
return "未知支付方式";
}
}

View File

@@ -1,49 +0,0 @@
package com.czg.order.param;
import com.alibaba.fastjson2.annotation.JSONField;
import com.czg.validator.group.DefaultGroup;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 数据统计-营业-产品销量
*
* @author tankaikai
* @since 2025-03-07 15:35
*/
@Data
public class DataSummaryProductSaleParam implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 天数
*/
@NotNull(message = "天数不能为空", groups = DefaultGroup.class)
private Integer day;
/**
* 天数列表
*/
@JSONField(serialize = false)
private List<String> days;
/**
* 店铺id
*/
private Long shopId;
/**
* 开始日期
*/
@JSONField(serialize = false)
private String beginDate;
/**
* 结束日期
*/
@JSONField(serialize = false)
private String endDate;
}

View File

@@ -6,6 +6,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDate;
/**
* 数据统计-营业-上半部分
@@ -20,15 +21,26 @@ public class DataSummaryTradeParam implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 开始时间 格式yyyy-MM-dd HH:mm:ss
* 开始时间 格式yyyy-MM-dd
*/
@NotBlank(message = "开始日期不能为空", groups = DefaultGroup.class)
private String beginDate;
private LocalDate beginDate;
/**
* 结束时间 格式yyyy-MM-dd HH:mm:ss
* 结束时间 格式yyyy-MM-dd
*/
@NotBlank(message = "结束日期不能为空", groups = DefaultGroup.class)
private String endDate;
private LocalDate endDate;
/**
* 时间范围类型
* TODAY, 今天
* YESTERDAY, 昨天
* LAST_7_DAYS, 最近7天
* LAST_30_DAYS,最近30天
* THIS_WEEK, 本周
* THIS_MONTH 本月
* CUSTOM 自定义时间范围
*/
private String rangeType;
/**
* 店铺id
*/

View File

@@ -1,9 +1,12 @@
package com.czg.order.param;
import com.czg.validator.group.DefaultGroup;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDate;
/**
* 销售统计Count入参
@@ -16,24 +19,34 @@ public class SaleSummaryCountParam implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 店铺id
*/
private Long shopId;
/**
* 商品名称
*/
private String productName;
/**
* 商品分类id
* 开始时间 格式yyyy-MM-dd
*/
private Long prodCategoryId;
@NotBlank(message = "开始日期不能为空", groups = DefaultGroup.class)
private LocalDate beginDate;
/**
* 开始日期 格式yyyy-MM-dd HH:mm:ss
* 结束时间 格式yyyy-MM-dd
*/
private String beginDate;
@NotBlank(message = "结束日期不能为空", groups = DefaultGroup.class)
private LocalDate endDate;
/**
* 结束日期 格式yyyy-MM-dd HH:mm:ss
* 时间范围类型
* TODAY, 今天
* YESTERDAY, 昨天
* LAST_7_DAYS, 最近7天
* LAST_30_DAYS,最近30天
* THIS_WEEK, 本周
* THIS_MONTH 本月
* CUSTOM 自定义时间范围
*/
private String endDate;
private String rangeType;
/**
* 店铺id
*/
private Long shopId;
}

View File

@@ -1,32 +0,0 @@
package com.czg.order.service;
import com.czg.order.entity.ShopOrderStatistic;
import com.czg.order.param.DataSummaryProductSaleParam;
import com.czg.order.param.DataSummaryTradeParam;
import com.czg.order.vo.DataSummaryDateAmountVo;
import com.czg.order.vo.DataSummaryPayTypeVo;
import com.czg.order.vo.DataSummaryProductSaleRankingVo;
import com.mybatisflex.core.paginate.Page;
import java.util.List;
/**
* 数据统计Service接口
*
* @author tankaikai
* @since 2025-03-07 15:31
*/
public interface DataSummaryService {
ShopOrderStatistic getArchiveTradeData(DataSummaryTradeParam param);
ShopOrderStatistic getRealTimeTradeData(DataSummaryTradeParam param);
Page<DataSummaryProductSaleRankingVo> getProductSaleRankingPage(DataSummaryProductSaleParam param);
DataSummaryDateAmountVo getSummaryAmountData(Long shopId,Integer day);
DataSummaryPayTypeVo getSummaryPayTypeData(Long shopId, Integer day);
List<Long> getShopIdList();
}

View File

@@ -1,23 +0,0 @@
package com.czg.order.service;
import com.czg.order.param.SaleSummaryCountParam;
import com.czg.order.vo.SaleSummaryCountVo;
import com.czg.order.vo.SaleSummaryInfoVo;
import com.mybatisflex.core.paginate.Page;
import java.util.List;
/**
* 销量统计Service接口
*
* @author tankaikai
* @since 2025-03-07 15:31
*/
public interface SaleSummaryService {
SaleSummaryCountVo summaryCount(SaleSummaryCountParam param);
Page<SaleSummaryInfoVo> summaryPage(SaleSummaryCountParam param);
List<SaleSummaryInfoVo> summaryList(SaleSummaryCountParam param);
}

View File

@@ -1,17 +1,73 @@
package com.czg.order.service;
import cn.hutool.core.date.DateTime;
import com.czg.order.entity.ShopOrderStatistic;
import com.czg.order.vo.CountPayTypeVo;
import com.czg.order.vo.TotalVo;
import com.mybatisflex.core.service.IService;
import com.czg.order.entity.ShopOrderStatistic;
import java.time.LocalDate;
import java.util.List;
/**
* 服务层。
* 店铺订单统计报表 服务层。
*
* @author zs
* @since 2025-03-07
* @author ww
* @since 2025-11-20
*/
public interface ShopOrderStatisticService extends IService<ShopOrderStatistic> {
void statistic(DateTime dateTime);
/**
* 数据统计 营业板块上半部分
* @param shopId 店铺id
* @param rangeType 时间范围类型
* TODAY, // 今天
* YESTERDAY, // 昨天
* LAST_7_DAYS, // 最近7天
* LAST_30_DAYS,// 最近30天
* THIS_WEEK, // 本周
* THIS_MONTH // 本月
* CUSTOM // 自定义时间范围
* @param start 开始时间 格式yyyy-MM-dd 今天/昨天不用传
* @param end 结束时间 格式yyyy-MM-dd 今天/昨天不用传
*/
ShopOrderStatistic getArchiveTradeData(Long shopId, String rangeType, LocalDate start, LocalDate end);
/**
* 获取多少天的数据 30/90天
*/
List<TotalVo> getDateAmount(Long shopId, Integer day);
/**
* 获取支付方式数据
*/
List<CountPayTypeVo> getSummaryPayTypeData(Long shopId, Integer day);
//------------------------------------------------------------下列为 后台使用------------------------------------------------------------
/**
* 统计某天数据并插入数据库
*
* @param day 日期
*/
void statisticAndInsert(Long shopId, LocalDate day);
/**
* 实时统计某天数据
*/
ShopOrderStatistic getRealTimeDataByDay(Long shopId, LocalDate day);
/**
* 获取某一天的数据 历史数据
* 从ShopOrderStatistic 表中查询
*/
ShopOrderStatistic getStatSingleDate(Long shopId, LocalDate day);
/**
* 统计 某时间段数据 总和 不包括当日实时数据
*
* @param start 开始时间
* @param end 结束时间
*/
ShopOrderStatistic getStatDateRange(Long shopId, LocalDate start, LocalDate end);
}

View File

@@ -1,17 +1,72 @@
package com.czg.order.service;
import cn.hutool.core.date.DateTime;
import com.czg.order.entity.ShopProdStatistic;
import com.czg.order.vo.SaleSummaryCountVo;
import com.mybatisflex.core.service.IService;
import java.time.LocalDate;
import java.util.List;
/**
* 服务层。
* 服务层。
*
* @author zs
* @since 2025-03-07
*/
public interface ShopProdStatisticService extends IService<ShopProdStatistic> {
void statistic(DateTime dateTime);
/**
* 统计一段时间内的商品交易数据
*
* @param param 入参
* @return 商品数据
*/
SaleSummaryCountVo summaryCount(Long shopId, String productName, String rangeType, LocalDate start, LocalDate end);
/**
* 获取某一段时间的商品交易数据
*
* @param shopId 店铺id
* @param rangeType 时间范围类型
* TODAY, // 今天
* YESTERDAY, // 昨天
* LAST_7_DAYS, // 最近7天
* LAST_30_DAYS,// 最近30天
* THIS_WEEK, // 本周
* THIS_MONTH // 本月
* CUSTOM // 自定义时间范围
* @param start 开始时间 格式yyyy-MM-dd 今天/昨天不用传
* @param end 结束时间 格式yyyy-MM-dd 今天/昨天不用传
* @return 商品数据
*/
List<ShopProdStatistic> getArchiveTradeData(Long shopId, String productName, String rangeType, LocalDate start, LocalDate end);
List<ShopProdStatistic> getArchiveTradeDataBy20(Long shopId, String rangeType, LocalDate start, LocalDate end);
//------------------------------------------------------------下列为 后台使用------------------------------------------------------------
/**
* 统计某天数据并插入数据库
*
* @param day
*/
void statisticAndInsert(Long shopId, LocalDate day);
/**
* 实时统计某天数据
*/
List<ShopProdStatistic> getRealTimeDataByDay(Long shopId, LocalDate day, String productName);
/**
* 获取某一天的数据 历史数据
* 从ShopOrderStatistic 表中查询
*/
List<ShopProdStatistic> getProdStatSingleDate(Long shopId, LocalDate day, String productName);
/**
* 统计 某时间段数据 总和 不包括当日实时数据
*
* @param start 开始时间
* @param end 结束时间
*/
List<ShopProdStatistic> getProdStatDateRange(Long shopId, LocalDate start, LocalDate end, String productName);
}

View File

@@ -1,11 +1,10 @@
package com.czg.order.service;
import cn.hutool.core.date.DateTime;
import com.czg.order.entity.ShopTableOrderStatistic;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.service.IService;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
/**
* 台桌订单统计表 服务层。
@@ -14,17 +13,47 @@ import java.math.BigDecimal;
* @since 2025-03-07
*/
public interface ShopTableOrderStatisticService extends IService<ShopTableOrderStatistic> {
Page<ShopTableOrderStatistic> summary(Long shopId, String startTime, String endTime);
/**
* 增加每日统计信息
* @param shopId 店铺id
* @param tableId 台桌id
* @param count 订单数量
* @param amount 订单总金额
* @return 是否成功
* @param shopId 店铺id
* @param rangeType 时间范围类型
* TODAY, // 今天
* YESTERDAY, // 昨天
* LAST_7_DAYS, // 最近7天
* LAST_30_DAYS,// 最近30天
* THIS_WEEK, // 本周
* THIS_MONTH // 本月
* CUSTOM // 自定义时间范围
* @param start 开始时间 格式yyyy-MM-dd 今天/昨天不用传
* @param end 结束时间 格式yyyy-MM-dd 今天/昨天不用传
*/
boolean addInfo(long shopId, long tableId, long count, BigDecimal amount);
List<ShopTableOrderStatistic> getArchiveTradeData(Long shopId, String rangeType, LocalDate start, LocalDate end);
void statistic(DateTime dateTime);
//------------------------------------------------------------下列为 后台使用------------------------------------------------------------
/**
* 统计某天数据并插入数据库
*
* @param day
*/
void statisticAndInsert(Long shopId, LocalDate day);
/**
* 实时统计某天数据
*/
List<ShopTableOrderStatistic> getRealTimeDataByDay(Long shopId, LocalDate day);
/**
* 获取某一天的数据 历史数据
* 从ShopOrderStatistic 表中查询
*/
List<ShopTableOrderStatistic> getStatSingleDate(Long shopId, LocalDate day);
/**
* 统计 某时间段数据 总和 不包括当日实时数据
*
* @param start 开始时间
* @param end 结束时间
*/
List<ShopTableOrderStatistic> getStatDateRange(Long shopId, LocalDate start, LocalDate end);
}

View File

@@ -2,7 +2,6 @@ package com.czg.order.service;
import com.czg.order.param.TableSummaryParam;
import com.czg.order.vo.TableSummaryExportVo;
import com.czg.order.vo.TableSummaryInfoVo;
import java.util.List;
@@ -14,7 +13,6 @@ import java.util.List;
*/
public interface TableSummaryService {
List<TableSummaryInfoVo> summaryList(TableSummaryParam param);
List<TableSummaryExportVo> summaryExportList(TableSummaryParam param);

View File

@@ -0,0 +1,76 @@
package com.czg.order.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* @author ww
* @description
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CountPayTypeVo {
/**
* 笔数
*/
private Integer count;
/**
* 支付方式
*/
private String payType;
private static final Map<String, String> PAY_TYPE_MAPPING = new LinkedHashMap<>();
static {
PAY_TYPE_MAPPING.put("memberPay", "会员支付");
PAY_TYPE_MAPPING.put("cashPay", "现金支付");
PAY_TYPE_MAPPING.put("wechatPay", "微信支付");
PAY_TYPE_MAPPING.put("alipayPay", "支付宝支付");
PAY_TYPE_MAPPING.put("mainScanPay", "主扫支付");
PAY_TYPE_MAPPING.put("backScanPay", "被扫支付");
PAY_TYPE_MAPPING.put("creditPay", "信用支付");
}
/**
* 合并实时数据和历史统计数据
*/
public static List<CountPayTypeVo> mergePayTypeData(Map<String, Integer> realTimeData,
Map<String, Integer> historyData) {
List<CountPayTypeVo> result = new ArrayList<>();
for (Map.Entry<String, String> entry : PAY_TYPE_MAPPING.entrySet()) {
String payCode = entry.getKey();
String payName = entry.getValue();
Integer realTimeCount = getSafeValue(realTimeData, payCode);
Integer historyCount = getSafeValue(historyData, payCode);
int totalCount = (realTimeCount != null ? realTimeCount : 0)
+ (historyCount != null ? historyCount : 0);
result.add(new CountPayTypeVo(totalCount, payName));
}
return result;
}
/**
* 安全获取Map中的值处理null情况
*/
private static Integer getSafeValue(Map<String, Integer> data, String key) {
if (data == null || data.isEmpty()) {
return null;
}
return data.get(key);
}
}

View File

@@ -1,28 +0,0 @@
package com.czg.order.vo;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* 销售趋势柱状图 左下
* @author tankaikai
* @since 2025-03-07 16:08
*/
@NoArgsConstructor
@Data
public class DataSummaryDateAmountVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* total
*/
private List<TotalVo> total = new ArrayList<>();
}

View File

@@ -1,44 +0,0 @@
package com.czg.order.vo;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* 支付占比饼图 左下
* @author tankaikai
* @since 2025-03-07 16:08
*/
@NoArgsConstructor
@Data
public class DataSummaryPayTypeVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* countPayType
*/
private List<CountPayTypeVo> countPayType = new ArrayList<>();
/**
* CountPayTypeVo
*/
@NoArgsConstructor
@Data
public static class CountPayTypeVo {
/**
* 笔数
*/
private Integer count;
/**
* 支付方式
*/
private String payType;
}
}

View File

@@ -102,7 +102,7 @@ public class OrderInfoVo implements Serializable {
private String payType;
/**
* 状态: unpaid-待支付;in-production 制作中;wait-out 待取餐;;done-订单完成;refunding-申请退单;refund-退单;part-refund 部分退单;cancelled-取消订单
* 状态: unpaid-待支付;in-production 制作中;wait_out 待取餐;;done-订单完成;refunding-申请退单;refund-退单;part_refund 部分退单;cancelled-取消订单
*/
private String status;

View File

@@ -1,6 +1,8 @@
package com.czg.order.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
@@ -12,6 +14,8 @@ import java.math.BigDecimal;
* @since 2025-03-07 16:22
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SaleSummaryCountVo implements Serializable {
@Serial

View File

@@ -1,65 +0,0 @@
package com.czg.order.vo;
import cn.idev.excel.annotation.ExcelIgnore;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.write.style.ColumnWidth;
import com.alibaba.fastjson2.annotation.JSONField;
import com.pig4cloud.plugin.excel.annotation.ExcelLine;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 销量统计明细
* @author tankaikai
* @since 2025-03-07 16:22
*/
@NoArgsConstructor
@Data
@ColumnWidth(30)
public class SaleSummaryInfoVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 导入时候回显行号
*/
@ExcelLine
@ExcelIgnore
@JSONField(serialize = false)
private Long lineNum;
/**
* 商品分类
*/
@ExcelProperty("商品分类")
private String categoryName;
/**
* 商品名称
*/
@ExcelProperty("商品名称")
private String productName;
/**
* 销量
*/
@ExcelProperty("销量")
private BigDecimal saleCount;
/**
* 销售金额
*/
@ExcelProperty("销售金额")
private BigDecimal saleAmount;
/**
* 退单量
*/
@ExcelProperty("退单量")
private BigDecimal refundCount;
/**
* 退单金额
*/
@ExcelProperty("退单金额")
private BigDecimal refundAmount;
}

View File

@@ -6,15 +6,18 @@ import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
/**
* 销售趋势柱状图 左下
*
* @author tankaikai
* @since 2025-03-07 16:08
*/
@NoArgsConstructor
@Data
public class TotalVo implements Serializable {
public class TotalVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@@ -34,5 +37,44 @@ public class TotalVo implements Serializable {
/**
* 日期
*/
private String tradeDay;
private LocalDate tradeDay;
public TotalVo(LocalDate tradeDay) {
this.tradeDay = tradeDay;
}
/**
* 合并实时数据和历史数据,并填充缺失的日期
*/
public static List<TotalVo> mergeAndFillData(TotalVo onlineData, List<TotalVo> historyData,
LocalDate startDate, LocalDate endDate) {
// 创建日期到数据的映射,方便查找
Map<LocalDate, TotalVo> dataMap = new HashMap<>();
// 将历史数据放入映射
for (TotalVo vo : historyData) {
if (vo.getTradeDay() != null) {
dataMap.put(vo.getTradeDay(), vo);
}
}
dataMap.put(onlineData.getTradeDay(), onlineData);
List<TotalVo> result = new ArrayList<>();
LocalDate currentDay = startDate;
while (!currentDay.isAfter(endDate)) {
if (dataMap.containsKey(currentDay)) {
result.add(dataMap.get(currentDay));
} else {
// 创建空的TotalVo填充缺失的日期
TotalVo emptyVo = new TotalVo(currentDay);
result.add(emptyVo);
}
currentDay = currentDay.plusDays(1);
}
// 按日期排序确保顺序正确
result.sort(Comparator.comparing(TotalVo::getTradeDay));
return result;
}
}

View File

@@ -1,10 +1,10 @@
package com.czg.service.account.mapper;
import com.czg.account.dto.shopinfo.ShopInfoDetailDTO;
import com.czg.account.dto.shopinfo.ShopInfoSubVO;
import com.czg.account.entity.ShopInfo;
import com.mybatisflex.core.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.math.BigDecimal;
@@ -22,4 +22,16 @@ public interface ShopInfoMapper extends BaseMapper<ShopInfo> {
@Update("update tb_shop_info set amount = amount + #{amount} where id = #{id} and amount + #{amount} >= 0")
boolean updateAmount(@Param("id") Long id, @Param("amount") BigDecimal amount);
/**
* 获取所有未删除的 过期时间>三天前 店铺id列表
*
* @return 店铺id列表
*/
@Select("SELECT id " +
"FROM tb_shop_info " +
"WHERE id > 1 " +
" AND expire_time > DATE_SUB(NOW(), INTERVAL 3 DAY) " +
" AND is_deleted = 0;")
List<Long> getShopIdList();
}

View File

@@ -1,20 +0,0 @@
package com.czg.service.account.mapper;
import com.czg.account.vo.ShopProdStatisticVO;
import com.mybatisflex.core.BaseMapper;
import com.czg.account.entity.ShopProdStatistic;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 映射层。
*
* @author zs
* @since 2025-03-06
*/
public interface ShopProdStatisticMapper extends BaseMapper<ShopProdStatistic> {
List<ShopProdStatisticVO> pageInfo(@Param("shopId") Long shopId, @Param("name") String name, @Param("classifyId") String classifyId,
@Param("startTime") String startTime, @Param("endTime") String endTime);
}

View File

@@ -465,4 +465,8 @@ public class ShopInfoServiceImpl extends ServiceImpl<ShopInfoMapper, ShopInfo> i
}
@Override
public List<Long> getShopIdList() {
return mapper.getShopIdList();
}
}

View File

@@ -1,26 +0,0 @@
package com.czg.service.account.service.impl;
import com.czg.utils.PageUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.czg.account.entity.ShopProdStatistic;
import com.czg.account.service.ShopProdStatisticService;
import com.czg.service.account.mapper.ShopProdStatisticMapper;
import org.springframework.stereotype.Service;
/**
* 服务层实现。
*
* @author zs
* @since 2025-03-06
*/
@Service
public class ShopProdStatisticServiceImpl extends ServiceImpl<ShopProdStatisticMapper, ShopProdStatistic> implements ShopProdStatisticService{
@Override
public Page<?> pageInfo(Long shopId, String name, String classifyId, String startTime, String endTime) {
PageHelper.startPage(PageUtil.buildPageHelp());
return PageUtil.convert(new PageInfo<>(mapper.pageInfo(shopId, name, classifyId, startTime, endTime)));
}
}

View File

@@ -1,12 +0,0 @@
<?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.czg.service.account.mapper.ShopProdStatisticMapper">
<select id="pageInfo" resultType="com.czg.account.vo.ShopProdStatisticVO">
select * from tb_shop_prod_statistic as a
left join tb_product as b on a.prod_id=b.id
where a.shop_id=#{shopId} and b.name like and b.category_id=
</select>
</mapper>

View File

@@ -3,8 +3,6 @@ package com.czg.service.market.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.ValidateException;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
@@ -14,7 +12,6 @@ import com.czg.account.entity.ShopUser;
import com.czg.account.service.ShopInfoService;
import com.czg.account.service.ShopUserService;
import com.czg.account.vo.ShopInfoCouponVO;
import com.czg.account.vo.UserCouponFoodVo;
import com.czg.account.vo.UserCouponVo;
import com.czg.exception.CzgException;
import com.czg.market.dto.MkShopCouponGiftDTO;
@@ -47,7 +44,6 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
@@ -84,6 +80,7 @@ public class ShopCouponServiceImpl extends ServiceImpl<ShopCouponMapper, ShopCou
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq(ShopCoupon::getShopId, param.getShopId())
.eq(ShopCoupon::getCouponType, param.getCouponType())
.like(ShopCoupon::getTitle, CzgStrUtils.getStrOrNull(param.getTitle()))
.eq(ShopCoupon::getIsDel, 0)
.orderBy(ShopCoupon::getCreateTime).desc();
return pageAs(PageUtil.buildPage(), queryWrapper, ShopCouponDTO.class);

View File

@@ -1,33 +1,269 @@
package com.czg.service.order.mapper;
import com.czg.order.entity.ShopOrderStatistic;
import com.czg.order.param.DataSummaryTradeParam;
import com.czg.order.vo.TotalVo;
import com.mybatisflex.core.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import com.czg.order.entity.ShopOrderStatistic;
import org.apache.ibatis.annotations.Select;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
/**
* 映射层。
* 店铺订单统计报表 映射层。
*
* @author zs
* @since 2025-03-07
* @author ww
* @since 2025-11-20
*/
@Mapper
public interface ShopOrderStatisticMapper extends BaseMapper<ShopOrderStatistic> {
ShopOrderStatistic getTradeData(DataSummaryTradeParam param);
long getNewMemberCount(DataSummaryTradeParam param);
List<Map<String, Object>> getPayTypeAmountCount(DataSummaryTradeParam param);
@Select("SELECT"+
" SUM(pay_amount) AS payAmount,"+
" SUM(online_pay_amount) AS onlinePayAmount,"+
" SUM(member_pay_amount) AS memberPayAmount,"+
" SUM(member_pay_count) AS memberPayCount,"+
" SUM(refund_amount) AS refundAmount,"+
" SUM(online_refund_amount) AS onlineRefundAmount,"+
" SUM(recharge_amount) AS rechargeAmount,"+
" SUM(online_recharge_amount) AS onlineRechargeAmount,"+
" SUM(give_amount) AS giveAmount,"+
" SUM(recharge_refund_amount) AS rechargeRefundAmount,"+
" SUM(online_recharge_refund_amount) AS onlineRechargeRefundAmount,"+
" SUM(cash_recharge_refund_amount) AS cashRechargeRefundAmount,"+
" SUM(cash_recharge_amount) AS cashRechargeAmount,"+
" SUM(member_refund_amount) AS memberRefundAmount,"+
" SUM(cash_refund_amount) AS cashRefundAmount,"+
" SUM(new_member_count) AS newMemberCount,"+
" SUM(customer_count) AS customerCount,"+
" SUM(order_count) AS orderCount,"+
" SUM(table_count) AS tableCount,"+
" SUM(avg_pay_amount) AS avgPayAmount,"+
" SUM(turnover_rate) AS turnoverRate,"+
" SUM(profit_amount) AS profitAmount,"+
" SUM(product_cost_amount) AS productCostAmount,"+
" SUM(profit_rate) AS profitRate,"+
" SUM(net_profit_amount) AS netProfitAmount,"+
" SUM(net_profit_rate) AS netProfitRate,"+
" SUM(discount_amount) AS discountAmount,"+
" SUM(discount_count) AS discountCount,"+
" SUM(new_customer_discount_amount) AS newCustomerDiscountAmount,"+
" SUM(full_discount_amount) AS fullDiscountAmount,"+
" SUM(coupon_discount_amount) AS couponDiscountAmount,"+
" SUM(point_discount_amount) AS pointDiscountAmount,"+
" SUM(back_discount_amount) AS backDiscountAmount,"+
" SUM(member_discount_amount) AS memberDiscountAmount,"+
" SUM(order_price_discount_amount) AS orderPriceDiscountAmount,"+
" SUM(cash_pay_amount) AS cashPayAmount,"+
" SUM(wechat_pay_amount) AS wechatPayAmount,"+
" SUM(alipay_pay_amount) AS alipayPayAmount,"+
" SUM(back_scan_pay_amount) AS backScanPayAmount,"+
" SUM(main_scan_pay_amount) AS mainScanPayAmount,"+
" SUM(credit_pay_amount) AS creditPayAmount "+
"FROM"+
" tb_shop_order_statistic " +
"WHERE " +
" shop_id = #{shopId} " +
" AND statistic_date BETWEEN #{start} AND #{end} ")
ShopOrderStatistic countStatistic(Long shopId, LocalDate start, LocalDate end);
//---------------------------------------------金额统计---------------------------------------------------
/**
* 订单金额统计 当日实时数据
*/
@Select("SELECT" +
" SUM(origin_amount) as orderAmount, " +
" SUM(order_amount) as actualAmount, " +
" SUM(origin_amount - order_amount) as discountAmount, " +
" trade_day as tradeDay" +
" FROM" +
" tb_order_info " +
" WHERE" +
" shop_id = #{shopId} " +
"and trade_day = #{tradeDay} " +
"and paid_time is not null " +
"GROUP BY trade_day desc")
TotalVo getOnlineDataAmount(Long shopId, LocalDate tradeDay);
List<Map<String, Object>> getVipRechargeAmountCount(DataSummaryTradeParam param);
/**
* 订单金额统计 按日期范围查询
*/
@Select("SELECT" +
" SUM(origin_amount) as orderAmount, " +
" SUM(order_amount) as actualAmount, " +
" SUM(origin_amount - order_amount) as discountAmount, " +
" statistic_date as tradeDay" +
" FROM" +
" tb_shop_order_statistic " +
" WHERE" +
" shop_id = #{shopId} " +
"and statistic_date >= #{start} " +
"and statistic_date <= #{end} " +
"GROUP BY tradeDay asc")
List<TotalVo> getStatDateRange(Long shopId, LocalDate start, LocalDate end);
BigDecimal getCustomerUnitPrice(DataSummaryTradeParam param);
/**
* 订单支付方式统计 当日实时数据
*/
@Select("SELECT" +
" SUM(CASE WHEN pay_type = 'main_scan' THEN pay_amount ELSE 0 END) AS mainScanPay," +
" SUM(CASE WHEN pay_type = 'back_scan' THEN pay_amount ELSE 0 END) AS backScanPay," +
" SUM(CASE WHEN pay_type = 'wechat_mini' THEN pay_amount ELSE 0 END) AS wechatPay," +
" SUM(CASE WHEN pay_type = 'alipay_mini' THEN pay_amount ELSE 0 END) AS alipayPay," +
" SUM(CASE WHEN pay_type = 'vip_pay' THEN pay_amount ELSE 0 END) AS memberPay," +
" SUM(CASE WHEN pay_type = 'cash_pay' THEN pay_amount ELSE 0 END) AS cashPay," +
" SUM(CASE WHEN pay_type = 'credit_pay' THEN pay_amount ELSE 0 END) AS creditPay" +
" FROM" +
" tb_order_info " +
" WHERE" +
" shop_id = #{shopId} " +
"and trade_day = #{tradeDay} " +
"and paid_time is not null ")
Map<String, Integer> getOnlinePayTypeDate(Long shopId, LocalDate tradeDay);
BigDecimal getTableTurnoverRate(DataSummaryTradeParam param);
/**
* 订单支付方式统计 按日期范围查询
*/
@Select("SELECT" +
" SUM(member_pay_count) as memberPay, " +
" SUM(cash_pay_count) as cashPay, " +
" SUM(wechat_pay_count) as wechatPay, " +
" SUM(alipay_pay_count) as alipayPay, " +
" SUM(main_scan_pay_count) as mainScanPay, " +
" SUM(back_scan_pay_count) as backScanPay, " +
" SUM(credit_pay_count) as creditPay " +
" FROM tb_shop_order_statistic " +
" WHERE shop_id = #{shopId} " +
" AND statistic_date >= #{start} " +
" AND statistic_date <= #{end} ")
Map<String, Integer> getPayTypeDateRangeRaw(Long shopId, LocalDate start, LocalDate end);
//*********************以下为日常统计********************************************************************************
/**
* 统计某日支付数据 当日实时数据/昨日数据落地
* order 订单支付
* refund 订单退款
* free 霸王餐
* memberIn 会员充值
* memberRefund 会员充值的退款
*/
@Select("SELECT " +
" SUM(CASE WHEN pay_type = 'order' THEN amount ELSE 0 END) AS onlinePayAmount," +
" SUM(CASE WHEN pay_type = 'refund' THEN amount ELSE 0 END) AS onlineRefundAmount," +
" SUM(CASE WHEN pay_type = 'free' THEN amount ELSE 0 END) AS backDiscountAmount," +
" SUM(CASE WHEN pay_type = 'memberIn' THEN amount ELSE 0 END) AS onlineRechargeAmount, " +
" SUM(CASE WHEN pay_type = 'memberRefund' THEN amount ELSE 0 END) AS onlineRechargeRefundAmount " +
"FROM" +
" tb_order_payment " +
"WHERE " +
" pay_status = 'success' " +
" AND shop_id = #{shopId}" +
" AND pay_type NOT IN ('memberPay', 'distribution', 'distributionRecharge')" +
" AND pay_date = #{tradeDay} ;")
ShopOrderStatistic getOnlineStatSingleDate(Long shopId, LocalDate tradeDay);
/**
* 统计某日订单数据 当日实时数据/昨日数据落地
*/
@Select("SELECT" +
" SUM(tb_order_info.origin_amount) AS originAmount," +
" SUM(CASE WHEN pay_type = 'main_scan' THEN pay_amount ELSE 0 END) AS mainScanPayAmount," +
" SUM(CASE WHEN pay_type = 'back_scan' THEN pay_amount ELSE 0 END) AS backScanPayAmount," +
" SUM(CASE WHEN pay_type = 'wechat_mini' THEN pay_amount ELSE 0 END) AS wechatPayAmount," +
" SUM(CASE WHEN pay_type = 'alipay_mini' THEN pay_amount ELSE 0 END) AS alipayPayAmount," +
" SUM(CASE WHEN pay_type = 'vip_pay' THEN pay_amount ELSE 0 END) AS memberPayAmount," +
" SUM(CASE WHEN pay_type = 'cash_pay' THEN pay_amount ELSE 0 END) AS cashPayAmount," +
" SUM(CASE WHEN pay_type = 'credit_pay' THEN pay_amount ELSE 0 END) AS creditPayAmount," +
" SUM(CASE WHEN pay_type = 'free_pay' THEN order_amount ELSE 0 END) AS backDiscountAmount," +
" " +
" SUM(CASE WHEN pay_type = 'vip_pay' THEN refund_amount ELSE 0 END) AS memberRefundAmount," +
" " +
" SUM(CASE WHEN pay_type = 'vip_pay' THEN 1 ELSE 0 END) as memberPayCount," +
" SUM(CASE WHEN pay_type = 'cash_pay' THEN 1 ELSE 0 END) as cashPayCount," +
" SUM(CASE WHEN pay_type = 'wechat_mini' THEN 1 ELSE 0 END) as wechatPayCount," +
" SUM(CASE WHEN pay_type = 'alipay_mini' THEN 1 ELSE 0 END) as alipayPayCount," +
" SUM(CASE WHEN pay_type = 'back_scan' THEN 1 ELSE 0 END) as backScanPayCount," +
" SUM(CASE WHEN pay_type = 'main_scan' THEN 1 ELSE 0 END) as mainScanPayCount," +
" SUM(CASE WHEN pay_type = 'credit_pay' THEN 1 ELSE 0 END) as creditPayCount," +
" " +
" SUM(refund_amount) as refundAmount," +
" " +
" SUM(new_customer_discount_amount) as newCustomerDiscountAmount," +
" SUM(discount_act_amount) as fullDiscountAmount," +
" SUM(product_coupon_discount_amount) + SUM(other_coupon_discount_amount) as couponDiscountAmount," +
" SUM(points_discount_amount) as pointDiscountAmount," +
" SUM(vip_discount_amount) as memberDiscountAmount," +
" SUM(discount_amount) as orderPriceDiscountAmount ," +
" count(1) as orderCount," +
" sum(CASE WHEN seat_num IS NULL OR seat_num <= 0 THEN 1 ELSE seat_num END) as customerCount " +
"FROM" +
" tb_order_info " +
"WHERE" +
" shop_id = #{shopId} " +
"and trade_day = #{tradeDay} " +
"and paid_time is not null")
ShopOrderStatistic getOrderStatSingleDate(Long shopId, LocalDate tradeDay);
/**
* 统计某日用户用户余额数据 当日实时数据/昨日数据落地
*/
@Select("SELECT " +
" SUM(CASE WHEN biz_code = 'cashIn' THEN amount ELSE 0 END) AS cashRechargeAmount, " +
" SUM(CASE WHEN biz_code = 'awardIn' THEN amount ELSE 0 END) AS giveAmount, " +
" SUM(CASE WHEN biz_code = 'cashRefund' THEN amount ELSE 0 END) AS cashRechargeRefundAmount " +
"FROM " +
" tb_shop_user_flow " +
"WHERE " +
" shop_id = #{shopId} " +
" AND trade_day = #{tradeDay} ;")
ShopOrderStatistic getShopUserFlowStatSingleDate(Long shopId, LocalDate tradeDay);
/**
* 优惠笔数 discountCount
*/
@Select("SELECT count(*) " +
"FROM tb_order_info WHERE shop_id = #{shopId} and trade_day = #{tradeDay} and paid_time is not null and discount_all_amount > 0")
Long countDiscountOrder(Long shopId, LocalDate tradeDay);
/**
* 优惠总金额 discountAmount
*/
@Select("SELECT sum(discount_all_amount) " +
"FROM tb_order_info WHERE shop_id = #{shopId} and trade_day = #{tradeDay} and paid_time is not null and discount_all_amount > 0")
BigDecimal countDiscountAmount(Long shopId, LocalDate tradeDay);
/**
* 统计店铺桌台数 tableCount
*/
@Select("SELECT count(*) FROM tb_order_info WHERE shop_id = #{shopId} and status != 'unbound'")
Long countShopTable(Long shopId);
/**
* 新增会员数 newMemberCount
*/
@Select("SELECT count(*) FROM tb_shop_user WHERE main_shop_id = #{mainShopId} and is_vip = 1 " +
"AND join_time BETWEEN CONCAT(#{tradeDay}, ' 00:00:00') AND CONCAT(#{tradeDay}, ' 23:59:59')")
Long countNewMember(Long mainShopId, LocalDate tradeDay);
/**
* 商品成本 productCostAmount
*/
@Select("SELECT " +
" SUM(CASE WHEN sku.cost_price IS NULL OR sku.cost_price <= 0 THEN 0 ELSE sku.cost_price END) AS productCostAmount " +
"FROM " +
" tb_order_info `order` " +
" INNER JOIN tb_order_detail detail ON `order`.id = detail.order_id " +
" AND is_temporary = 0 " +
" LEFT JOIN tb_prod_sku sku ON detail.sku_id = sku.id " +
"WHERE " +
" `order`.shop_id = #{shopId} " +
" and trade_day = #{tradeDay} " +
" and paid_time is not null")
BigDecimal countProductCostAmount(Long shopId, LocalDate tradeDay);
List<Long> getShopIdList();
}

View File

@@ -1,33 +1,33 @@
package com.czg.service.order.mapper;
import com.czg.order.entity.ShopProdStatistic;
import com.czg.order.param.DataSummaryProductSaleParam;
import com.czg.order.param.SaleSummaryCountParam;
import com.czg.order.vo.DataSummaryProductSaleRankingVo;
import com.czg.order.vo.SaleSummaryCountVo;
import com.czg.order.vo.SaleSummaryInfoVo;
import com.mybatisflex.core.BaseMapper;
import com.czg.order.entity.ShopProdStatistic;
import java.time.LocalDate;
import java.util.List;
/**
* 映射层。
* 商品统计表 映射层。
*
* @author zs
* @since 2025-03-07
* @author ww
* @since 2025-11-21
*/
public interface ShopProdStatisticMapper extends BaseMapper<ShopProdStatistic> {
List<DataSummaryProductSaleRankingVo> findProdRandingSummaryPage(DataSummaryProductSaleParam param);
/**
* 根据店铺id和日期 统计商品
*/
List<ShopProdStatistic> selectProStatByDay(Long shopId, LocalDate day, String productName);
List<DataSummaryProductSaleRankingVo> findProdRandingSummaryPage2(DataSummaryProductSaleParam param);
List<ShopProdStatistic> getProdStatSingleDate(Long shopId, LocalDate day, String productName);
SaleSummaryCountVo getSaleSummaryCount(SaleSummaryCountParam param);
List<ShopProdStatistic> getProdStatDateRange(Long shopId, LocalDate start, LocalDate end, String productName);
SaleSummaryCountVo getSaleSummaryCount2(SaleSummaryCountParam param);
List<SaleSummaryInfoVo> findSaleSummaryList(SaleSummaryCountParam param);
List<SaleSummaryInfoVo> findSaleSummaryList2(SaleSummaryCountParam param);
//-----------------总统计 总金额统计-----------------
SaleSummaryCountVo summaryCountByDay(Long shopId, LocalDate day, String productName);
SaleSummaryCountVo summaryCountSingleDate(Long shopId, LocalDate day, String productName);
SaleSummaryCountVo summaryCountDateRange(Long shopId, LocalDate start, LocalDate end, String productName);
}

View File

@@ -3,27 +3,88 @@ package com.czg.service.order.mapper;
import com.czg.order.entity.ShopTableOrderStatistic;
import com.czg.order.param.TableSummaryParam;
import com.czg.order.vo.TableSummaryExportVo;
import com.czg.order.vo.TableSummaryInfoVo;
import com.mybatisflex.core.BaseMapper;
import org.apache.ibatis.annotations.Select;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
/**
* 台桌订单统计表 映射层。
*
* @author zs
* @since 2025-03-07
* @author ww
* @since 2025-11-21
*/
public interface ShopTableOrderStatisticMapper extends BaseMapper<ShopTableOrderStatistic> {
List<ShopTableOrderStatistic> selectSummary(Long shopId, String startTime, String endTime);
boolean incrInfo(long shopId, long tableId, long count, BigDecimal amount, String dateStr);
List<TableSummaryInfoVo> findSummaryList(TableSummaryParam param);
/**
* 获取指定天的在线数据台桌
*
* @param shopId 店铺ID
* @param tradeDay 时间 yyyy-MM-dd
*/
@Select("SELECT " +
" `table`.id AS tableId, " +
" `table`.table_code AS tableCode, " +
" `table`.`name` AS tableName, " +
" `area`.`name` AS areaName, " +
" count( `order`.id ) AS orderCount, " +
" SUM( CASE WHEN `order`.pay_type = 'free_pay' THEN `order`.order_amount ELSE `order`.pay_amount END ) AS orderAmount, " +
" SUM( CASE WHEN `order`.refund_amount > 0 THEN 1 ELSE 0 END ) AS refundCount, " +
" SUM( `order`.refund_amount ) AS refundAmount " +
"FROM " +
" tb_order_info `order` " +
" INNER JOIN tb_shop_table `table` ON `order`.shop_id = `table`.shop_id " +
" AND `order`.table_code = `table`.table_code " +
" LEFT JOIN tb_shop_table_area `area` ON `table`.area_id = `area`.id " +
" AND `table`.shop_id = `area`.shop_id " +
"WHERE " +
" `order`.shop_id = #{shopId} " +
" AND `order`.trade_day = #{tradeDay} " +
" AND `order`.paid_time IS NOT NULL " +
" AND `order`.pay_mode != 'no-table'")
List<ShopTableOrderStatistic> getOnlineData(Long shopId, LocalDate tradeDay);
/**
* 获取指定天的台桌订单统计数据
*
* @param shopId 店铺ID
* @param tradeDay 时间 yyyy-MM-dd
*/
@Select("SELECT " +
" * " +
"FROM " +
" `tb_shop_table_order_statistic` " +
"WHERE " +
" shop_id = #{shopId} " +
" AND create_day = #{tradeDay}")
List<ShopTableOrderStatistic> getStatSingleDate(Long shopId, LocalDate tradeDay);
/**
* 获取指定时间范围内的台桌订单统计数据
*
* @param shopId 店铺ID
* @param startDate 开始时间 yyyy-MM-dd
* @param endDate 结束时间 yyyy-MM-dd
*/
@Select("SELECT" +
" id,table_id,table_code,table_name,area_name," +
" sum(order_count) as orderCount," +
" sum(order_amount) as orderAmount," +
" sum(refund_count) as refundCount," +
" sum(refund_amount) as refundAmount" +
" FROM" +
" tb_shop_table_order_statistic " +
"WHERE" +
" shop_id = #{shopId} " +
" AND create_day >=#{startDate} " +
" AND create_day <=#{endDate} " +
" GROUP BY table_id")
List<ShopTableOrderStatistic> getStatDateRange(Long shopId, LocalDate startDate, LocalDate endDate);
List<TableSummaryInfoVo> findSummaryList2(TableSummaryParam param);
List<TableSummaryExportVo> findSummaryExportList(TableSummaryParam param);
}

View File

@@ -4,7 +4,6 @@ import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.czg.account.dto.HandoverRecordDTO;
import com.czg.account.dto.PrintOrderDetailDTO;
@@ -578,7 +577,7 @@ public abstract class PrinterHandler {
* @return 是否退款
*/
protected static boolean isReturn(OrderInfo orderInfo) {
return ArrayUtil.contains(new String[]{"refunding", "part-refund", "refund"}, orderInfo.getStatus());
return ArrayUtil.contains(new String[]{"refunding", "part_refund", "refund"}, orderInfo.getStatus());
}

View File

@@ -1,220 +0,0 @@
package com.czg.service.order.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ReflectUtil;
import com.czg.order.entity.OrderInfo;
import com.czg.order.entity.ShopOrderStatistic;
import com.czg.order.enums.PayEnums;
import com.czg.order.param.DataSummaryProductSaleParam;
import com.czg.order.param.DataSummaryTradeParam;
import com.czg.order.service.DataSummaryService;
import com.czg.order.vo.DataSummaryDateAmountVo;
import com.czg.order.vo.DataSummaryPayTypeVo;
import com.czg.order.vo.DataSummaryProductSaleRankingVo;
import com.czg.order.vo.TotalVo;
import com.czg.service.order.mapper.OrderInfoMapper;
import com.czg.service.order.mapper.ShopOrderStatisticMapper;
import com.czg.service.order.mapper.ShopProdStatisticMapper;
import com.czg.utils.PageUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 数据统计Service实现类
*
* @author tankaikai
* @since 2025-03-07 15:32
*/
@Service
public class DataSummaryServiceImpl implements DataSummaryService {
@Resource
private ShopOrderStatisticMapper shopOrderStatisticMapper;
@Resource
private OrderInfoMapper orderInfoMapper;
@Resource
private ShopProdStatisticMapper shopProdStatisticMapper;
@Override
public ShopOrderStatistic getArchiveTradeData(DataSummaryTradeParam param) {
ShopOrderStatistic shopOrderStatistic = shopOrderStatisticMapper.getTradeData(param);
if(shopOrderStatistic == null){
shopOrderStatistic = new ShopOrderStatistic();
}
shopOrderStatistic.setCustomerUnitPrice(shopOrderStatistic.getCustomerUnitPrice().setScale(2, java.math.RoundingMode.HALF_UP));
shopOrderStatistic.setTableTurnoverRate(shopOrderStatistic.getTableTurnoverRate().setScale(2, java.math.RoundingMode.HALF_UP));
return shopOrderStatistic;
}
@Override
public ShopOrderStatistic getRealTimeTradeData(DataSummaryTradeParam param) {
List<String> funs = Arrays.asList("getPayTypeAmountCount", "getVipRechargeAmountCount", "getNewMemberCount", "getCustomerUnitPrice", "getTableTurnoverRate");
Map<String, Object> collect = funs.parallelStream().collect(Collectors.toMap(fun -> fun, fun ->
ReflectUtil.invoke(shopOrderStatisticMapper, fun, param)
));
/*Map<String, Object> collect = new ConcurrentHashMap<>();
for (String fun : funs) {
Object invoke = ReflectUtil.invoke(shopOrderStatisticMapper, fun, param);
collect.put(fun, ObjUtil.defaultIfNull(invoke,BigDecimal.ZERO));
}*/
ShopOrderStatistic data = new ShopOrderStatistic();
//List<Map<String, Object>> list = shopOrderStatisticMapper.getPayTypeAmountCount(param);
List<Map<String, Object>> list = (List<Map<String, Object>>) collect.get("getPayTypeAmountCount");
Map<String, BigDecimal> sum = list.stream().collect(Collectors.toMap(item -> Convert.toStr(item.get("payType")), item -> Convert.toBigDecimal(item.get("amount"))));
Map<String, Long> count = list.stream().collect(Collectors.toMap(item -> Convert.toStr(item.get("payType")), item -> Convert.toLong(item.get("count"))));
data.setWechatPayAmount(sum.getOrDefault(PayEnums.WECHAT_MINI.getValue(), BigDecimal.ZERO));
data.setWechatPayCount(count.getOrDefault(PayEnums.WECHAT_MINI.getValue(), 0L));
data.setAliPayAmount(sum.getOrDefault(PayEnums.ALIPAY_MINI.getValue(), BigDecimal.ZERO));
data.setAliPayCount(count.getOrDefault(PayEnums.ALIPAY_MINI.getValue(), 0L));
data.setScanPayAmount(sum.getOrDefault(PayEnums.MAIN_SCAN.getValue(), BigDecimal.ZERO));
data.setScanPayCount(count.getOrDefault(PayEnums.MAIN_SCAN.getValue(), 0L));
data.setCashPayAmount(sum.getOrDefault(PayEnums.CASH_PAY.getValue(), BigDecimal.ZERO));
data.setCashPayCount(count.getOrDefault(PayEnums.CASH_PAY.getValue(), 0L));
data.setCreditPayAmount(sum.getOrDefault(PayEnums.CREDIT_PAY.getValue(), BigDecimal.ZERO));
data.setCreditPayCount(count.getOrDefault(PayEnums.CREDIT_PAY.getValue(), 0L));
data.setBackScanPayAmount(sum.getOrDefault(PayEnums.BACK_SCAN.getValue(), BigDecimal.ZERO));
data.setBackScanPayCount(count.getOrDefault(PayEnums.BACK_SCAN.getValue(), 0L));
data.setH5PayAmount(sum.getOrDefault(PayEnums.H5_PAY.getValue(), BigDecimal.ZERO));
data.setH5PayCount(count.getOrDefault(PayEnums.H5_PAY.getValue(), 0L));
//List<Map<String, Object>> list1 = shopOrderStatisticMapper.getVipRechargeAmountCount(param);
List<Map<String, Object>> list1 = (List<Map<String, Object>>) collect.get("getVipRechargeAmountCount");
Map<String, BigDecimal> sum1 = list1.stream().collect(Collectors.toMap(item -> Convert.toStr(item.get("bizCode")), item -> Convert.toBigDecimal(item.get("amount"))));
Map<String, Long> count1 = list1.stream().collect(Collectors.toMap(item -> Convert.toStr(item.get("bizCode")), item -> Convert.toLong(item.get("count"))));
data.setRechargeAmount(NumberUtil.add(sum1.getOrDefault("cashIn", BigDecimal.ZERO), sum1.getOrDefault("wechatIn", BigDecimal.ZERO), sum1.getOrDefault("alipayIn", BigDecimal.ZERO)));
data.setRechargeRefundAmount(sum1.getOrDefault("rechargeRefund", BigDecimal.ZERO).abs());
data.setMemberPayAmount(sum1.getOrDefault("orderPay", BigDecimal.ZERO).abs());
data.setMemberPayCount(count1.getOrDefault("orderPay", 0L));
data.setSaleAmount(NumberUtil.add(data.getWechatPayAmount(), data.getAliPayAmount(), data.getScanPayAmount(), data.getCashPayAmount(), data.getCreditPayAmount(),data.getBackScanPayAmount(),data.getH5PayAmount()));
data.setSaleCount(NumberUtil.add(data.getWechatPayCount(), data.getAliPayCount(), data.getScanPayCount(), data.getCashPayCount(), data.getCreditPayCount(),data.getBackScanPayCount(),data.getH5PayCount()).longValue());
BigDecimal refundAmount = list.stream().filter(item -> item.get("payType") != null).map(item -> Convert.toBigDecimal(item.get("refund"), BigDecimal.ZERO)).reduce(BigDecimal.ZERO, BigDecimal::add);
data.setRefundAmount(refundAmount);
long refundCount = list.stream().filter(item -> item.get("payType") != null).map(item -> Convert.toLong(item.get("refundCount"), 0L)).reduce(0L, Long::sum);
data.setRefundCount(refundCount);
//long newMemberCount = shopOrderStatisticMapper.getNewMemberCount(param);
long newMemberCount = (long) collect.get("getNewMemberCount");
data.setNewMemberCount(newMemberCount);
//BigDecimal customerUnitPrice = shopOrderStatisticMapper.getCustomerUnitPrice(param);
BigDecimal customerUnitPrice = (BigDecimal) collect.get("getCustomerUnitPrice");
data.setCustomerUnitPrice(NumberUtil.nullToZero(customerUnitPrice).setScale(2, java.math.RoundingMode.HALF_UP));
//BigDecimal tableTurnoverRate = shopOrderStatisticMapper.getTableTurnoverRate(param);
BigDecimal tableTurnoverRate = (BigDecimal) collect.get("getTableTurnoverRate");
data.setTableTurnoverRate(tableTurnoverRate);
BigDecimal discountAmount = list.stream().filter(item -> item.get("payType") != null).map(item -> Convert.toBigDecimal(item.get("discount"), BigDecimal.ZERO)).reduce(BigDecimal.ZERO, BigDecimal::add);
long discountCount = list.stream().filter(item -> item.get("payType") != null).map(item -> Convert.toLong(item.get("discountCount"), 0L)).reduce(0L, Long::sum);
data.setDiscountAmount(discountAmount);
data.setDiscountCount(discountCount);
return data;
}
@Override
public Page<DataSummaryProductSaleRankingVo> getProductSaleRankingPage(DataSummaryProductSaleParam param) {
LocalDate now = LocalDate.now();
Integer day = param.getDay();
LocalDate beginDate = now.plusDays(-day);
List<String> days = new ArrayList<>();
for (int i = 1; i <= day; i++) {
String thisDay = beginDate.plusDays(i).format(DatePattern.NORM_DATE_FORMATTER);
days.add(thisDay);
}
param.setDays(days);
param.setBeginDate(days.getFirst() + " 00:00:00");
param.setEndDate(days.getLast() + " 23:59:59");
PageHelper.startPage(PageUtil.buildPageHelp());
PageInfo<DataSummaryProductSaleRankingVo> pageInfo = new PageInfo<>(shopProdStatisticMapper.findProdRandingSummaryPage(param));
return PageUtil.convert(pageInfo);
}
@Override
public DataSummaryDateAmountVo getSummaryAmountData(Long shopId, Integer day) {
LocalDate now = LocalDate.now();
LocalDate beginDate = now.plusDays(-day);
DataSummaryDateAmountVo data = new DataSummaryDateAmountVo();
List<TotalVo> total = new ArrayList<>();
for (int i = 1; i <= day; i++) {
String thisDay = beginDate.plusDays(i).format(DatePattern.NORM_DATE_FORMATTER);
OrderInfo orderInfo = orderInfoMapper.selectOneByQuery(QueryWrapper.create()
.select("ifnull(sum(order_amount),0) as order_amount,ifnull(sum(pay_amount),0) as pay_amount,ifnull(sum(order_amount-pay_amount),0) as discount_amount")
.eq(OrderInfo::getShopId, shopId)
.eq(OrderInfo::getTradeDay, thisDay)
.isNotNull(OrderInfo::getPaidTime)
);
TotalVo totalVo = new TotalVo();
if (orderInfo != null) {
totalVo.setOrderAmount(orderInfo.getOrderAmount());
totalVo.setActualAmount(orderInfo.getPayAmount());
totalVo.setDiscountAmount(orderInfo.getDiscountAmount());
}
totalVo.setTradeDay(thisDay);
total.add(totalVo);
}
data.setTotal(total);
return data;
}
@Override
public DataSummaryPayTypeVo getSummaryPayTypeData(Long shopId, Integer day) {
LocalDate now = LocalDate.now();
LocalDate beginDate = now.plusDays(-day);
DataSummaryPayTypeVo data = new DataSummaryPayTypeVo();
List<DataSummaryPayTypeVo.CountPayTypeVo> total = new ArrayList<>();
List<String> days = new ArrayList<>();
for (int i = 1; i <= day; i++) {
String thisDay = beginDate.plusDays(i).format(DatePattern.NORM_DATE_FORMATTER);
days.add(thisDay);
}
List<OrderInfo> orderList = orderInfoMapper.selectListByQuery(QueryWrapper.create()
.select("pay_type,count(1) as place_num")
.eq(OrderInfo::getShopId, shopId)
.in(OrderInfo::getTradeDay, days)
.isNotNull(OrderInfo::getPaidTime)
.groupBy(OrderInfo::getPayType)
);
PayEnums[] pays = PayEnums.values();
if (CollUtil.isEmpty(orderList)) {
for (PayEnums pay : pays) {
DataSummaryPayTypeVo.CountPayTypeVo payTypeVo = new DataSummaryPayTypeVo.CountPayTypeVo();
payTypeVo.setPayType(pay.getMsg());
payTypeVo.setCount(0);
total.add(payTypeVo);
}
data.setCountPayType(total);
return data;
}
for (PayEnums pay : pays) {
OrderInfo orderInfo = orderList.stream().filter(order -> pay.getValue().equals(order.getPayType())).findFirst().orElse(null);
DataSummaryPayTypeVo.CountPayTypeVo payTypeVo = new DataSummaryPayTypeVo.CountPayTypeVo();
payTypeVo.setPayType(pay.getMsg());
payTypeVo.setCount(0);
if (orderInfo != null) {
payTypeVo.setPayType(PayEnums.getText(orderInfo.getPayType()));
payTypeVo.setCount(orderInfo.getPlaceNum());
}
total.add(payTypeVo);
}
data.setCountPayType(total);
return data;
}
@Override
public List<Long> getShopIdList() {
return shopOrderStatisticMapper.getShopIdList();
}
}

View File

@@ -435,7 +435,7 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
//优惠券部分 目前规则 每个券只能用一张
couponExecute(orderDetails, param, totalAmount, tempAmount, prodCouponAmount,
oneGiftAmount, twoHalfAmount, rateAmount, fullReductionAmount);
orderInfo.setOriginAmount(param.getOriginAmount());
//总商品支付金额 不包含打包费 用来计算后续
BigDecimal newTotalAmount = totalAmount.getPrice();
@@ -465,7 +465,8 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
}
//(商品金额+打包费+餐位费)
newTotalAmount = newTotalAmount.add(packAmount.getPrice()).add(orderInfo.getSeatAmount());
//填充原价
orderInfo.setOriginAmount(totalAmount.getPrice().add(packAmount.getPrice()).add(orderInfo.getSeatAmount()));
//新客立减
if (shopUser != null) {
newTotalAmount = newTotalAmount.subtract(param.getNewCustomerDiscountAmount());
@@ -1090,6 +1091,8 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
orderInfo1.setIsFreeDine(1);
orderInfo1.setStatus(OrderStatusEnums.DONE.getCode());
orderInfo1.setPayAmount(BigDecimal.ZERO);
orderInfo1.setPaidTime(LocalDateTime.now());
orderInfo1.setPayType(PayEnums.FREE_PAY.getValue());
updateById(orderInfo1);
orderDetailService.updateOrderDetailStatus(orderInfo.getId(), OrderStatusEnums.DONE.getCode());
redisService.del(RedisCst.classKeyExpired.EXPIRED_ORDER + orderInfo.getId());
@@ -1105,6 +1108,7 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
updateChain().eq(OrderInfo::getId, orderInfo.getId())
.set(OrderInfo::getPayType, PayEnums.VIP_PAY.getValue())
.set(OrderInfo::getStatus, OrderStatusEnums.DONE.getCode())
.set(OrderInfo::getPaidTime, LocalDateTime.now())
.set(OrderInfo::getPayAmount, orderInfo.getOrderAmount())
.update();
orderDetailService.updateOrderDetailStatus(orderInfo.getId(), OrderStatusEnums.DONE.getCode());
@@ -1441,23 +1445,30 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
private void upOrderPayInfo(OrderInfo orderInfo, CheckOrderPay param) {
orderInfo.setPointsNum(param.getPointsNum());
orderInfo.setIsPrint(param.getIsPrint());
orderInfo.setRoundAmount(param.getRoundAmount());
orderInfo.setOrderAmount(param.getOrderAmount());
orderInfo.setPointsDiscountAmount(param.getPointsDiscountAmount());
// ----------------------优惠金额-------------------------
//优惠卷
orderInfo.setCouponInfoList(CollUtil.isEmpty(param.getCouponList()) ? "" : JSONObject.toJSONString(param.getCouponList()));
orderInfo.setProductCouponDiscountAmount(param.getProductCouponDiscountAmount());
orderInfo.setOtherCouponDiscountAmount(param.getOtherCouponDiscountAmount());
//积分
orderInfo.setPointsDiscountAmount(param.getPointsDiscountAmount());
//新客立减
orderInfo.setNewCustomerDiscountAmount(param.getNewCustomerDiscountAmount());
// orderInfo.setFullCouponDiscountAmount(param.getFullCouponDiscountAmount());
orderInfo.setOtherCouponDiscountAmount(param.getOtherCouponDiscountAmount());
//商家最终改价
orderInfo.setDiscountAmount(param.getDiscountAmount());
//优惠券
orderInfo.setCouponInfoList(CollUtil.isEmpty(param.getCouponList()) ? "" : JSONObject.toJSONString(param.getCouponList()));
//满减活动抵扣金额
orderInfo.setDiscountActAmount(param.getDiscountActAmount());
//会员折扣金额
orderInfo.setVipDiscountAmount(param.getVipDiscountAmount());
//抹零
// orderInfo.setRoundAmount(param.getRoundAmount());
//优惠总金额
orderInfo.initDiscountAllAmount();
//折扣信息
orderInfo.setDiscountInfo(buildDiscountInfo(orderInfo));
//0元按照现金支付处理
if (orderInfo.getOrderAmount().compareTo(BigDecimal.ZERO) == 0) {
orderInfo.setStatus(OrderStatusEnums.DONE.getCode());
@@ -1508,6 +1519,9 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
if (orderInfo.getProductCouponDiscountAmount().compareTo(BigDecimal.ZERO) > 0) {
jsonObject.put("商品券抵扣", orderInfo.getProductCouponDiscountAmount());
}
if (orderInfo.getOtherCouponDiscountAmount().compareTo(BigDecimal.ZERO) > 0) {
jsonObject.put("其它优惠券折扣", orderInfo.getOtherCouponDiscountAmount());
}
if (orderInfo.getDiscountAmount().compareTo(BigDecimal.ZERO) > 0) {
jsonObject.put("打折优惠", orderInfo.getDiscountAmount());
}
@@ -1517,9 +1531,6 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
if (orderInfo.getVipDiscountAmount() != null && orderInfo.getVipDiscountAmount().compareTo(BigDecimal.ZERO) > 0) {
jsonObject.put("会员整单折扣", orderInfo.getVipDiscountAmount());
}
if (orderInfo.getOtherCouponDiscountAmount().compareTo(BigDecimal.ZERO) > 0) {
jsonObject.put("其它优惠券折扣", orderInfo.getOtherCouponDiscountAmount());
}
if (orderInfo.getPointsDiscountAmount().compareTo(BigDecimal.ZERO) > 0) {
jsonObject.put("积分抵扣", orderInfo.getPointsDiscountAmount());
}

View File

@@ -1,48 +0,0 @@
package com.czg.service.order.service.impl;
import com.czg.order.param.SaleSummaryCountParam;
import com.czg.order.service.SaleSummaryService;
import com.czg.order.vo.SaleSummaryCountVo;
import com.czg.order.vo.SaleSummaryInfoVo;
import com.czg.service.order.mapper.ShopProdStatisticMapper;
import com.czg.utils.PageUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mybatisflex.core.paginate.Page;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 销量统计Service实现类
*
* @author tankaikai
* @since 2025-03-07 15:32
*/
@Service
public class SaleSummaryServiceImpl implements SaleSummaryService {
@Resource
private ShopProdStatisticMapper shopProdStatisticMapper;
@Override
public SaleSummaryCountVo summaryCount(SaleSummaryCountParam param) {
SaleSummaryCountVo saleSummaryCount = shopProdStatisticMapper.getSaleSummaryCount(param);
if (saleSummaryCount == null) {
saleSummaryCount = new SaleSummaryCountVo();
}
return saleSummaryCount;
}
@Override
public Page<SaleSummaryInfoVo> summaryPage(SaleSummaryCountParam param) {
PageHelper.startPage(PageUtil.buildPageHelp());
PageInfo<SaleSummaryInfoVo> pageInfo = new PageInfo<>(shopProdStatisticMapper.findSaleSummaryList(param));
return PageUtil.convert(pageInfo);
}
@Override
public List<SaleSummaryInfoVo> summaryList(SaleSummaryCountParam param) {
return shopProdStatisticMapper.findSaleSummaryList(param);
}
}

View File

@@ -1,88 +1,174 @@
package com.czg.service.order.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.czg.account.entity.ShopInfo;
import com.czg.account.service.ShopInfoService;
import com.czg.exception.CzgException;
import com.czg.order.entity.ShopOrderStatistic;
import com.czg.order.param.DataSummaryTradeParam;
import com.czg.order.service.DataSummaryService;
import com.czg.order.service.ShopOrderStatisticService;
import com.czg.order.vo.CountPayTypeVo;
import com.czg.order.vo.TotalVo;
import com.czg.service.order.mapper.ShopOrderStatisticMapper;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.*;
/**
* 服务层实现。
* 店铺订单统计报表 服务层实现。
*
* @author zs
* @since 2025-03-07
* @author ww
* @since 2025-11-20
*/
@Service
@Slf4j
public class ShopOrderStatisticServiceImpl extends ServiceImpl<ShopOrderStatisticMapper, ShopOrderStatistic> implements ShopOrderStatisticService {
@Resource
private DataSummaryService dataSummaryService;
@DubboReference
private ShopInfoService shopInfoService;
@Override
public void statistic(DateTime dateTime) {
// 获取前一天的开始时间00:00:00
DateTime startOfDay = DateUtil.beginOfDay(dateTime);
// 获取前一天的结束时间23:59:59
DateTime endOfDay = DateUtil.endOfDay(dateTime);
List<Long> shopIdList = dataSummaryService.getShopIdList();
if (CollUtil.isEmpty(shopIdList)) {
return;
public ShopOrderStatistic getArchiveTradeData(Long shopId, String rangeType, LocalDate start, LocalDate end) {
LocalDate currentDate = LocalDate.now();
if ("today".equals(rangeType)) {
return getRealTimeDataByDay(shopId, currentDate);
} else if ("yesterday".equals(rangeType)) {
return getStatSingleDate(shopId, currentDate.minusDays(1));
}
List<List<Long>> split = CollUtil.split(shopIdList, 5);
for (List<Long> splitIdList : split) {
splitIdList.parallelStream().forEach(shopId -> {
Long mainShopId = shopInfoService.getMainIdByShopId(shopId);
DataSummaryTradeParam param = new DataSummaryTradeParam();
param.setShopId(shopId);
param.setMainShopId(mainShopId);
param.setBeginDate(startOfDay.toStringDefaultTimeZone());
param.setEndDate(endOfDay.toStringDefaultTimeZone());
ShopOrderStatistic statistic = dataSummaryService.getRealTimeTradeData(param);
statistic.setShopId(shopId);
statistic.setCreateDay(dateTime.toLocalDateTime().toLocalDate());
statistic.setUpdateTime(LocalDateTime.now());
if (statistic.getNewMemberCount() != 0L) {
System.out.println("newMemberCount:" + statistic.getNewMemberCount());
}
// 如果没有订单和退款,则不更新数据,否则会产出很多数据
if (statistic.getSaleCount() == 0
&& statistic.getRefundCount() == 0
&& statistic.getMemberPayCount() == 0
&& statistic.getNewMemberCount() == 0
) {
log.info("店铺:{}{},没有要存档的订单统计数据", shopId, dateTime.toDateStr());
} else {
ShopOrderStatistic entity =
getMapper()
.selectOneByQuery(query().eq("shop_id", shopId)
.eq("create_day", dateTime.toDateStr()));
if (entity != null) {
statistic.setId(entity.getId());
updateById(statistic);
} else {
save(statistic);
}
}
});
if (start.isAfter(currentDate)) {
throw new CzgException("开始时间不能晚于当前时间");
}
if (start.equals(end)) {
return getStatSingleDate(shopId, start);
}
//包括当前时间
if (end.isBefore(currentDate)) {
return getStatDateRange(shopId, start, end);
} else {
ShopOrderStatistic realTimeDataByDay = getRealTimeDataByDay(shopId, currentDate);
ShopOrderStatistic statDateRange = getStatDateRange(shopId, start, end);
ShopOrderStatistic shopOrderStatistic = ShopOrderStatistic.mergeStatistics(realTimeDataByDay, statDateRange);
shopOrderStatistic.setShopId(shopId);
return shopOrderStatistic;
}
}
@Override
public List<TotalVo> getDateAmount(Long shopId, Integer day) {
LocalDate currentDate = LocalDate.now();
LocalDate startDate = currentDate;
if (day == 7) {
startDate = currentDate.minusDays(6);
} else if (day == 30) {
startDate = currentDate.minusDays(29);
}
TotalVo onlineDataAmount = mapper.getOnlineDataAmount(shopId, currentDate);
List<TotalVo> statDateRange = mapper.getStatDateRange(shopId, startDate, currentDate);
return TotalVo.mergeAndFillData(onlineDataAmount, statDateRange, startDate, currentDate);
}
@Override
public List<CountPayTypeVo> getSummaryPayTypeData(Long shopId, Integer day) {
LocalDate currentDate = LocalDate.now();
LocalDate startDate = currentDate;
if (day == 7) {
startDate = currentDate.minusDays(6);
} else if (day == 30) {
startDate = currentDate.minusDays(29);
}
return CountPayTypeVo.mergePayTypeData(
mapper.getOnlinePayTypeDate(shopId, currentDate),
mapper.getPayTypeDateRangeRaw(shopId, startDate, currentDate));
}
@Override
public void statisticAndInsert(Long shopId, LocalDate day) {
ShopOrderStatistic realTimeData = getRealTimeDataByDay(shopId, day);
if (realTimeData != null && realTimeData.hasValidData(realTimeData)) {
ShopOrderStatistic result = getOne(QueryWrapper.create().eq(ShopOrderStatistic::getShopId, shopId).eq(ShopOrderStatistic::getStatisticDate, day));
if (result != null) {
removeById(result);
}
save(realTimeData);
}
}
@Override
public ShopOrderStatistic getRealTimeDataByDay(Long shopId, LocalDate day) {
ShopOrderStatistic result = new ShopOrderStatistic();
ShopOrderStatistic onlineStat = mapper.getOnlineStatSingleDate(shopId, day);
ShopOrderStatistic orderStat = mapper.getOrderStatSingleDate(shopId, day);
ShopOrderStatistic userFlowStat = mapper.getShopUserFlowStatSingleDate(shopId, day);
Long discountCount = mapper.countDiscountOrder(shopId, day);
BigDecimal discountAmount = mapper.countDiscountAmount(shopId, day);
Long tableCount = mapper.countShopTable(shopId);
Long newMemberCount = mapper.countNewMember(shopId, day);
BigDecimal productCostAmount = mapper.countProductCostAmount(shopId, day);
// 合并结果
BeanUtils.copyProperties(onlineStat, result);
BeanUtils.copyProperties(orderStat, result);
BeanUtils.copyProperties(userFlowStat, result);
result.setShopId(shopId);
result.setStatisticDate(day);
result.setDiscountCount(discountCount);
result.setDiscountAmount(discountAmount);
result.setTableCount(tableCount);
result.setNewMemberCount(newMemberCount);
result.setProductCostAmount(productCostAmount);
//会员充值退款 充值退款金额(线上退款+现金退款)
result.setRechargeRefundAmount(onlineStat.getOnlineRechargeRefundAmount().add(userFlowStat.getCashRechargeRefundAmount()));
//实付金额 (线上付款 现金支付 会员支付 挂账)
result.setPayAmount(onlineStat.getOnlinePayAmount().add(orderStat.getCashPayAmount()).add(orderStat.getMemberPayAmount()).add(orderStat.getCreditPayAmount()));
//毛利润(订单实付金额-商品成本)
result.setProfitAmount(result.getPayAmount().subtract(productCostAmount));
//毛利率(订单实付金额-商品成本)/订单实付金额*100%
if (result.getPayAmount().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal profitRate = result.getProfitAmount().divide(result.getPayAmount(), 4, RoundingMode.HALF_DOWN).multiply(BigDecimal.valueOf(100));
result.setProfitRate(profitRate);
result.setNetProfitAmount(profitRate);
} else {
result.setProfitRate(BigDecimal.ZERO);
result.setNetProfitAmount(BigDecimal.ZERO);
}
//客单价 实付金额(包括线上支付 包含现金支付 包含会员支付 包含挂账)/就餐人数
result.setAvgPayAmount(result.getPayAmount().divide(new BigDecimal(result.getCustomerCount()), 2, RoundingMode.HALF_DOWN));
//翻台率 (订单数-桌台数)/桌台数*100%
if (tableCount > 0) {
BigDecimal turnoverRate = new BigDecimal(result.getOrderCount()).subtract(new BigDecimal(tableCount)).divide(new BigDecimal(tableCount), 4, RoundingMode.HALF_DOWN).multiply(BigDecimal.valueOf(100));
result.setTurnoverRate(turnoverRate);
} else {
result.setTurnoverRate(BigDecimal.ZERO);
}
return result;
}
@Override
public ShopOrderStatistic getStatSingleDate(Long shopId, LocalDate day) {
ShopOrderStatistic result = getOne(QueryWrapper.create().eq(ShopOrderStatistic::getShopId, shopId).eq(ShopOrderStatistic::getStatisticDate, day));
if (result == null) {
result = new ShopOrderStatistic();
result.setShopId(shopId);
result.setStatisticDate(day);
result.init();
}
return result;
}
@Override
public ShopOrderStatistic getStatDateRange(Long shopId, LocalDate start, LocalDate end) {
ShopOrderStatistic result = mapper.countStatistic(shopId, start, end);
if (result == null) {
result = new ShopOrderStatistic();
result.setStatisticDate(start);
result.init();
}
result.setShopId(shopId);
return result;
}
}

View File

@@ -1,87 +1,194 @@
package com.czg.service.order.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import com.czg.exception.CzgException;
import com.czg.order.entity.ShopProdStatistic;
import com.czg.order.param.DataSummaryProductSaleParam;
import com.czg.order.service.DataSummaryService;
import com.czg.order.service.ShopProdStatisticService;
import com.czg.order.vo.DataSummaryProductSaleRankingVo;
import com.czg.order.vo.SaleSummaryCountVo;
import com.czg.service.order.mapper.ShopProdStatisticMapper;
import com.czg.utils.CzgStrUtils;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 服务层实现。
* 商品统计表 服务层实现。
*
* @author zs
* @since 2025-03-07
* @author ww
* @since 2025-11-21
*/
@Service
@Slf4j
public class ShopProdStatisticServiceImpl extends ServiceImpl<ShopProdStatisticMapper, ShopProdStatistic> implements ShopProdStatisticService {
@Resource
private DataSummaryService dataSummaryService;
@Resource
private ShopProdStatisticMapper shopProdStatisticMapper;
@Data
private static class StatisticTask {
private BigDecimal successCount = BigDecimal.ZERO;
private BigDecimal successAmount = BigDecimal.ZERO;
private BigDecimal refundCount = BigDecimal.ZERO;
private BigDecimal refundAmount = BigDecimal.ZERO;
@Override
public List<ShopProdStatistic> getArchiveTradeDataBy20(Long shopId, String rangeType, LocalDate start, LocalDate end) {
List<ShopProdStatistic> archiveTradeData = getArchiveTradeData(shopId, null, rangeType, start, end);
// 按照 saleCount 降序排序
return archiveTradeData.stream()
.sorted(
Comparator.comparing(
ShopProdStatistic::getSaleCount,
Comparator.nullsLast(BigDecimal::compareTo).reversed()
)
)
.limit(20)
.toList();
}
@Override
public SaleSummaryCountVo summaryCount(Long shopId, String productName, String rangeType, LocalDate start, LocalDate end) {
LocalDate currentDate = LocalDate.now();
if ("today".equals(rangeType)) {
return mapper.summaryCountByDay(shopId, currentDate, productName);
} else if ("yesterday".equals(rangeType)) {
return mapper.summaryCountSingleDate(shopId, currentDate.minusDays(1), productName);
} else {
if (start.isAfter(currentDate)) {
throw new CzgException("开始时间不能晚于当前时间");
}
if (start.equals(end)) {
return mapper.summaryCountSingleDate(shopId, start, productName);
} else {
if (end.isBefore(currentDate)) {
return mapper.summaryCountDateRange(shopId, start, end, productName);
} else {
SaleSummaryCountVo todaySummary = mapper.summaryCountByDay(shopId, currentDate, productName);
SaleSummaryCountVo dateRangeSummary = mapper.summaryCountDateRange(shopId, start, end, productName);
return mergeSummaryCountVo(todaySummary, dateRangeSummary);
}
}
}
}
@Override
public void statistic(DateTime dateTime) {
// 获取前一天的开始时间00:00:00
DateTime startOfDay = DateUtil.beginOfDay(dateTime);
// 获取前一天的结束时间23:59:59
DateTime endOfDay = DateUtil.endOfDay(dateTime);
List<Long> shopIdList = dataSummaryService.getShopIdList();
if (CollUtil.isEmpty(shopIdList)) {
return;
}
List<List<Long>> split = CollUtil.split(shopIdList, 5);
for (List<Long> splitIdList : split) {
splitIdList.parallelStream().forEach(shopId -> {
DataSummaryProductSaleParam param = new DataSummaryProductSaleParam();
param.setShopId(shopId);
param.setBeginDate(startOfDay.toStringDefaultTimeZone());
param.setEndDate(endOfDay.toStringDefaultTimeZone());
// 删除之前统计数据
getMapper().deleteByQuery(
QueryWrapper.create()
.eq("shop_id", shopId)
.eq("create_day", dateTime.toDateStr()));
// 重新统计数据
List<DataSummaryProductSaleRankingVo> list = shopProdStatisticMapper.findProdRandingSummaryPage2(param);
for (DataSummaryProductSaleRankingVo dto : list) {
ShopProdStatistic entity = new ShopProdStatistic();
entity.setProdId(dto.getProductId());
entity.setSaleCount(dto.getNumber());
entity.setSaleAmount(dto.getAmount());
entity.setRefundCount(dto.getRefundCount());
entity.setRefundAmount(dto.getRefundAmount());
entity.setShopId(shopId);
entity.setCreateDay(dateTime.toJdkDate());
if (NumberUtil.isLessOrEqual(entity.getSaleCount(), BigDecimal.ZERO) && NumberUtil.isLessOrEqual(entity.getRefundCount(), BigDecimal.ZERO)) {
log.info("店铺:{}{},没有要存档的商品统计数据", shopId, dateTime.toDateStr());
} else {
save(entity);
}
public List<ShopProdStatistic> getArchiveTradeData(Long shopId, String productName, String rangeType, LocalDate start, LocalDate end) {
LocalDate currentDate = LocalDate.now();
productName = CzgStrUtils.getStrOrNull(productName);
List<ShopProdStatistic> resultList;
if ("today".equals(rangeType)) {
resultList = getRealTimeDataByDay(shopId, currentDate, productName);
} else if ("yesterday".equals(rangeType)) {
resultList = getProdStatSingleDate(shopId, currentDate.minusDays(1), productName);
} else {
if (start.isAfter(currentDate)) {
throw new CzgException("开始时间不能晚于当前时间");
}
if (start.equals(end)) {
resultList = getProdStatSingleDate(shopId, start, productName);
} else {
if (end.isBefore(currentDate)) {
resultList = getProdStatDateRange(shopId, start, end, productName);
} else {
List<ShopProdStatistic> realTimeDataByDay = getRealTimeDataByDay(shopId, currentDate, productName);
List<ShopProdStatistic> dateRange = getProdStatDateRange(shopId, start, end, productName);
resultList = mergeProdStatistic(realTimeDataByDay, dateRange);
}
});
}
}
return resultList;
}
@Override
public void statisticAndInsert(Long shopId, LocalDate day) {
List<ShopProdStatistic> realTimeData = getRealTimeDataByDay(shopId, day, null);
if (CollUtil.isNotEmpty(realTimeData)) {
// 过滤掉没有有效数据的记录
realTimeData = realTimeData.stream()
.filter(ShopProdStatistic::isValid)
.toList();
if(CollUtil.isNotEmpty(realTimeData)) {
boolean exists = exists(QueryWrapper.create().eq(ShopProdStatistic::getShopId, shopId).eq(ShopProdStatistic::getCreateDay, day));
if (exists) {
remove(QueryWrapper.create().eq(ShopProdStatistic::getShopId, shopId).eq(ShopProdStatistic::getCreateDay, day));
}
saveBatch(realTimeData);
}
}
}
@Override
public List<ShopProdStatistic> getRealTimeDataByDay(Long shopId, LocalDate day, String productName) {
return mapper.selectProStatByDay(shopId, day, productName);
}
@Override
public List<ShopProdStatistic> getProdStatSingleDate(Long shopId, LocalDate day, String productName) {
return mapper.getProdStatSingleDate(shopId, day, productName);
}
@Override
public List<ShopProdStatistic> getProdStatDateRange(Long shopId, LocalDate start, LocalDate end, String productName) {
return mapper.getProdStatDateRange(shopId, start, end, productName);
}
private SaleSummaryCountVo mergeSummaryCountVo(SaleSummaryCountVo todaySummary, SaleSummaryCountVo dateRangeSummary) {
if (todaySummary == null) todaySummary = new SaleSummaryCountVo();
if (dateRangeSummary == null) dateRangeSummary = new SaleSummaryCountVo();
return new SaleSummaryCountVo(
safeAdd(todaySummary.getTotalAmount(), dateRangeSummary.getTotalAmount()),
safeAdd(todaySummary.getRefundAmount(), dateRangeSummary.getRefundAmount()),
safeAdd(todaySummary.getSaleCount(), dateRangeSummary.getSaleCount()),
safeAdd(todaySummary.getRefundCount(), dateRangeSummary.getRefundCount())
);
}
/**
* 合并实时数据和日期范围数据
*
* @param realTimeDataByDay 实时数据
* @param dateRange 日期范围数据
* @return 合并后的数据
*/
private List<ShopProdStatistic> mergeProdStatistic(List<ShopProdStatistic> realTimeDataByDay, List<ShopProdStatistic> dateRange) {
if (realTimeDataByDay == null) realTimeDataByDay = new ArrayList<>();
if (dateRange == null) dateRange = new ArrayList<>();
return Stream.concat(realTimeDataByDay.stream(), dateRange.stream())
.filter(Objects::nonNull)
.collect(Collectors.toMap(
ShopProdStatistic::getProdId,
Function.identity(),
(stat1, stat2) -> {
// 创建合并后的对象
ShopProdStatistic merged = new ShopProdStatistic();
merged.setId(stat1.getId());
merged.setProdId(stat1.getProdId());
merged.setShopId(stat1.getShopId());
// 安全处理BigDecimal相加处理null值
merged.setSaleCount(safeAdd(stat1.getSaleCount(), stat2.getSaleCount()));
merged.setSaleAmount(safeAdd(stat1.getSaleAmount(), stat2.getSaleAmount()));
merged.setRefundCount(safeAdd(stat1.getRefundCount(), stat2.getRefundCount()));
merged.setRefundAmount(safeAdd(stat1.getRefundAmount(), stat2.getRefundAmount()));
return merged;
}
))
.values()
.stream()
.toList();
}
private BigDecimal safeAdd(BigDecimal num1, BigDecimal num2) {
BigDecimal safeNum1 = num1 != null ? num1 : BigDecimal.ZERO;
BigDecimal safeNum2 = num2 != null ? num2 : BigDecimal.ZERO;
return safeNum1.add(safeNum2);
}
}

View File

@@ -1,118 +1,79 @@
package com.czg.service.order.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.czg.exception.CzgException;
import com.czg.order.entity.ShopTableOrderStatistic;
import com.czg.order.param.TableSummaryParam;
import com.czg.order.service.DataSummaryService;
import com.czg.order.service.ShopTableOrderStatisticService;
import com.czg.order.vo.TableSummaryInfoVo;
import com.czg.service.order.mapper.ShopTableOrderStatisticMapper;
import com.czg.utils.PageUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
/**
* 台桌订单统计表 服务层实现。
*
* @author zs
* @since 2025-03-07
* @author ww
* @since 2025-11-21
*/
@Service
@Slf4j
public class ShopTableOrderStatisticServiceImpl extends ServiceImpl<ShopTableOrderStatisticMapper, ShopTableOrderStatistic> implements ShopTableOrderStatisticService {
@Resource
private DataSummaryService dataSummaryService;
@Resource
private ShopTableOrderStatisticMapper shopTableOrderStatisticMapper;
public class ShopTableOrderStatisticServiceImpl extends ServiceImpl<ShopTableOrderStatisticMapper, ShopTableOrderStatistic> implements ShopTableOrderStatisticService{
@Override
public Page<ShopTableOrderStatistic> summary(Long shopId, String startTime, String endTime) {
Page<Object> page = PageUtil.buildPage();
PageHelper.startPage(Math.toIntExact(page.getPageNumber()), Math.toIntExact(page.getPageSize()));
return PageUtil.convert(new PageInfo<>(mapper.selectSummary(shopId, startTime, endTime)));
}
@Override
public boolean addInfo(long shopId, long tableId, long count, BigDecimal amount) {
ShopTableOrderStatistic statistic = getOne(new QueryWrapper().eq(ShopTableOrderStatistic::getShopId, shopId).eq(ShopTableOrderStatistic::getTableId, tableId)
.eq(ShopTableOrderStatistic::getCreateDay, DateUtil.date().toDateStr()));
if (statistic == null) {
statistic = new ShopTableOrderStatistic();
statistic.setShopId(shopId);
statistic.setTableId(tableId);
statistic.setCreateDay(DateUtil.date().toSqlDate());
statistic.setOrderCount(count);
statistic.setOrderAmount(amount);
save(statistic);
public List<ShopTableOrderStatistic> getArchiveTradeData(Long shopId, String rangeType, LocalDate start, LocalDate end) {
LocalDate currentDate = LocalDate.now();
if ("today".equals(rangeType)) {
return getRealTimeDataByDay(shopId, currentDate);
} else if ("yesterday".equals(rangeType)) {
return getStatSingleDate(shopId, currentDate.minusDays(1));
}
return mapper.incrInfo(shopId, tableId, count, amount, DateUtil.date().toDateStr());
}
@Data
private static class StatisticTask {
private long successCount = 0;
private BigDecimal successAmount = BigDecimal.ZERO;
private long refundCount = 0;
private BigDecimal refundAmount = BigDecimal.ZERO;
}
@Override
public void statistic(DateTime dateTime) {
// 获取前一天的开始时间00:00:00
DateTime startOfDay = DateUtil.beginOfDay(dateTime);
// 获取前一天的结束时间23:59:59
DateTime endOfDay = DateUtil.endOfDay(dateTime);
List<Long> shopIdList = dataSummaryService.getShopIdList();
if (CollUtil.isEmpty(shopIdList)) {
return;
if (start.isAfter(currentDate)) {
throw new CzgException("开始时间不能晚于当前时间");
}
List<List<Long>> split = CollUtil.split(shopIdList, 5);
for (List<Long> splitIdList : split) {
splitIdList.parallelStream().forEach(shopId -> {
TableSummaryParam param = new TableSummaryParam();
param.setShopId(shopId);
param.setBeginDate(startOfDay.toStringDefaultTimeZone());
param.setEndDate(endOfDay.toStringDefaultTimeZone());
// 删除之前统计数据
getMapper()
.deleteByQuery(
new QueryWrapper()
.eq("shop_id", shopId)
.eq("create_day", dateTime.toDateStr()));
// 重新统计数据
List<TableSummaryInfoVo> list = shopTableOrderStatisticMapper.findSummaryList2(param);
for (TableSummaryInfoVo dto : list) {
ShopTableOrderStatistic entity = new ShopTableOrderStatistic();
entity.setTableId(dto.getTableId());
entity.setTableCode(dto.getTableCode());
entity.setTableName(dto.getTableName());
entity.setAreaName(dto.getAreaName());
entity.setOrderCount(dto.getOrderCount());
entity.setOrderAmount(dto.getOrderAmount());
entity.setRefundCount(dto.getRefundCount());
entity.setRefundAmount(dto.getRefundAmount());
entity.setShopId(shopId);
entity.setCreateDay(dateTime.toJdkDate());
if (entity.getOrderCount() == 0L && entity.getRefundCount() == 0L) {
log.info("店铺:{}{},没有要存档的台桌统计数据", shopId, dateTime.toDateStr());
} else {
save(entity);
}
}
});
if (start.equals(end)) {
return getStatSingleDate(shopId, start);
}
//包括当前时间
if (end.isBefore(currentDate)) {
return getStatDateRange(shopId, start, end);
} else {
List<ShopTableOrderStatistic> realTimeDataByDay = getRealTimeDataByDay(shopId, currentDate);
List<ShopTableOrderStatistic> statDateRange = getStatDateRange(shopId, start, end);
return ShopTableOrderStatistic.mergeWithStream(realTimeDataByDay, statDateRange);
}
}
@Override
public void statisticAndInsert(Long shopId, LocalDate day) {
List<ShopTableOrderStatistic> realTimeData = getRealTimeDataByDay(shopId, day);
if (CollUtil.isNotEmpty(realTimeData)) {
// 过滤掉没有有效数据的记录
realTimeData = realTimeData.stream()
.filter(ShopTableOrderStatistic::hasValidData)
.toList();
if (CollUtil.isNotEmpty(realTimeData)) {
remove(QueryWrapper.create().eq(ShopTableOrderStatistic::getShopId, shopId).eq(ShopTableOrderStatistic::getCreateDay, day));
saveBatch(realTimeData);
}
}
}
@Override
public List<ShopTableOrderStatistic> getRealTimeDataByDay(Long shopId, LocalDate day) {
return mapper.getOnlineData(shopId, day);
}
@Override
public List<ShopTableOrderStatistic> getStatSingleDate(Long shopId, LocalDate day) {
return mapper.getStatSingleDate(shopId, day);
}
@Override
public List<ShopTableOrderStatistic> getStatDateRange(Long shopId, LocalDate start, LocalDate end) {
return mapper.getStatDateRange(shopId, start, end);
}
}

View File

@@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
import com.czg.order.param.TableSummaryParam;
import com.czg.order.service.TableSummaryService;
import com.czg.order.vo.TableSummaryExportVo;
import com.czg.order.vo.TableSummaryInfoVo;
import com.czg.service.order.mapper.ShopTableOrderStatisticMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
@@ -28,10 +27,7 @@ public class TableSummaryServiceImpl implements TableSummaryService {
@Resource
private ShopTableOrderStatisticMapper shopTableOrderStatisticMapper;
@Override
public List<TableSummaryInfoVo> summaryList(TableSummaryParam param) {
return shopTableOrderStatisticMapper.findSummaryList(param);
}
@Override
public List<TableSummaryExportVo> summaryExportList(TableSummaryParam param) {

View File

@@ -3,125 +3,4 @@
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.order.mapper.ShopOrderStatisticMapper">
<select id="getTradeData" resultType="com.czg.order.entity.ShopOrderStatistic">
select
sum(sale_amount) as sale_amount,
sum(sale_count) as sale_count,
sum(discount_amount) as discount_amount,
sum(discount_count) as discount_count,
sum(refund_amount) as refund_amount,
sum(refund_count) as refund_count,
sum(wechat_pay_count) as wechat_pay_count,
sum(wechat_pay_amount) as wechat_pay_amount,
sum(ali_pay_count) as ali_pay_count,
sum(ali_pay_amount) as ali_pay_amount,
sum(credit_pay_count) as credit_pay_count,
sum(credit_pay_amount) as credit_pay_amount,
sum(member_pay_count) as member_pay_count,
sum(member_pay_amount) as member_pay_amount,
sum(scan_pay_count) as scan_pay_count,
sum(scan_pay_amount) as scan_pay_amount,
sum(cash_pay_count) as cash_pay_count,
sum(cash_pay_amount) as cash_pay_amount,
sum(recharge_amount) as recharge_amount,
avg(customer_unit_price) as customer_unit_price,
avg(table_turnover_rate) as table_turnover_rate,
sum(new_member_count) as new_member_count,
max(update_time) as update_time
from tb_shop_order_statistic
where shop_id = #{shopId}
<if test="beginDate != null and beginDate != ''">
and create_day >= str_to_date(#{beginDate}, '%Y-%m-%d')
</if>
<if test="endDate != null and endDate != ''">
<![CDATA[
and create_day <= str_to_date(#{endDate}, '%Y-%m-%d')
]]>
</if>
group by shop_id
</select>
<select id="getNewMemberCount" resultType="java.lang.Long">
select count(1) from tb_shop_user where main_shop_id = #{mainShopId} and is_vip = 1
<if test="beginDate != null and beginDate != ''">
and join_time >= str_to_date(#{beginDate}, '%Y-%m-%d %H:%i:%s')
</if>
<if test="endDate != null and endDate != ''">
<![CDATA[
and join_time <= str_to_date(#{endDate}, '%Y-%m-%d %H:%i:%s')
]]>
</if>
</select>
<select id="getPayTypeAmountCount" resultType="java.util.Map">
SELECT
t1.pay_type as payType,
sum(t1.pay_amount) as amount,
sum(t1.refund_amount) as refund,
count(case when t1.refund_amount>0 then 1 end) as refundCount,
sum(t1.discount_amount) as discount,
count(case when t1.discount_amount>0 then 1 end) as discountCount,
count(*) as count
FROM
tb_order_info t1
WHERE t1.shop_id = #{shopId}
and t1.paid_time is not null
<if test="beginDate != null and beginDate != ''">
and t1.create_time >= str_to_date(#{beginDate}, '%Y-%m-%d %H:%i:%s')
</if>
<if test="endDate != null and endDate != ''">
<![CDATA[
and t1.create_time <= str_to_date(#{endDate}, '%Y-%m-%d %H:%i:%s')
]]>
</if>
group by t1.pay_type
</select>
<select id="getVipRechargeAmountCount" resultType="java.util.Map">
select
t1.biz_code as bizCode,sum(t1.amount) as amount,count(*) as count
from tb_shop_user_flow t1
where t1.shop_id = #{shopId}
<if test="beginDate != null and beginDate != ''">
and t1.create_time >= str_to_date(#{beginDate}, '%Y-%m-%d %H:%i:%s')
</if>
<if test="endDate != null and endDate != ''">
<![CDATA[
and t1.create_time <= str_to_date(#{endDate}, '%Y-%m-%d %H:%i:%s')
]]>
</if>
group by t1.biz_code
</select>
<select id="getCustomerUnitPrice" resultType="java.math.BigDecimal">
SELECT
ifnull((sum(t1.pay_amount)-sum(t1.refund_amount))/count(ifnull(case t1.seat_num when 0 then 1 end,1)),0.00) as customerUnitPrice
FROM
tb_order_info t1
WHERE t1.shop_id = #{shopId}
and t1.paid_time is not null
<if test="beginDate != null and beginDate != ''">
and t1.create_time >= str_to_date(#{beginDate}, '%Y-%m-%d %H:%i:%s')
</if>
<if test="endDate != null and endDate != ''">
<![CDATA[
and t1.create_time <= str_to_date(#{endDate}, '%Y-%m-%d %H:%i:%s')
]]>
</if>
</select>
<select id="getTableTurnoverRate" resultType="java.math.BigDecimal">
SELECT
ifnull((count(*)-count(DISTINCT t1.table_code))/count(DISTINCT t1.table_code)*100,0.00)
FROM
tb_order_info t1
WHERE t1.shop_id = #{shopId}
and t1.paid_time is not null
<if test="beginDate != null and beginDate != ''">
and t1.create_time >= str_to_date(#{beginDate}, '%Y-%m-%d %H:%i:%s')
</if>
<if test="endDate != null and endDate != ''">
<![CDATA[
and t1.create_time <= str_to_date(#{endDate}, '%Y-%m-%d %H:%i:%s')
]]>
</if>
</select>
<select id="getShopIdList" resultType="java.lang.Long">
select id from tb_shop_info order by id
</select>
</mapper>

View File

@@ -4,164 +4,120 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.order.mapper.ShopProdStatisticMapper">
<select id="findProdRandingSummaryPage" resultType="com.czg.order.vo.DataSummaryProductSaleRankingVo">
select
t1.prod_id,
t2.name as product_name,
sum(t1.sale_count) as number,
sum(t1.sale_amount) as amount
from tb_shop_prod_statistic t1
left join tb_product t2 on t1.prod_id = t2.id
where t1.shop_id = #{shopId}
and t1.create_day in
<foreach item="day" collection="days" open="(" separator="," close=")">
#{day}
</foreach>
group by t1.prod_id,t2.name
order by sum(t1.sale_count) desc
</select>
<select id="getSaleSummaryCount" resultType="com.czg.order.vo.SaleSummaryCountVo">
select
sum(t1.sale_count) as sale_count,
sum(t1.sale_amount) as total_amount,
sum(t1.refund_count) as refund_count,
sum(t1.refund_amount) as refund_amount
from tb_shop_prod_statistic t1
left join tb_product t2 on t1.prod_id = t2.id
where t1.shop_id = #{shopId}
<if test="productName != null and productName != ''">
and t2.name like concat('%', #{productName}, '%')
</if>
<if test="prodCategoryId != null">
and t2.category_id = #{prodCategoryId}
</if>
<if test="beginDate != null and beginDate != ''">
and t1.create_day >= str_to_date(#{beginDate}, '%Y-%m-%d')
</if>
<if test="endDate != null and endDate != ''">
<![CDATA[
and t1.create_day <= str_to_date(#{endDate}, '%Y-%m-%d')
]]>
</if>
</select>
<select id="findSaleSummaryList" resultType="com.czg.order.vo.SaleSummaryInfoVo">
select
t1.prod_id,
max(t1.id) as id,
t2.NAME AS product_name,
t3.name as category_name,
sum(t1.sale_count) as sale_count,
sum(t1.sale_amount) as sale_amount,
sum(t1.refund_count) as refund_count,
sum(t1.refund_amount) as refund_amount
from tb_shop_prod_statistic t1
left join tb_product t2 on t1.prod_id = t2.id
left join tb_shop_prod_category t3 on t2.category_id = t3.id
where t1.shop_id = #{shopId}
<if test="productName != null and productName != ''">
and t2.name like concat('%', #{productName}, '%')
</if>
<if test="prodCategoryId != null">
and t2.category_id = #{prodCategoryId}
</if>
<if test="beginDate != null and beginDate != ''">
and t1.create_day >= str_to_date(#{beginDate}, '%Y-%m-%d')
</if>
<if test="endDate != null and endDate != ''">
<![CDATA[
and t1.create_day <= str_to_date(#{endDate}, '%Y-%m-%d')
]]>
</if>
group by t1.prod_id
ORDER BY sum( t1.sale_count ) DESC,max(t1.id) DESC
</select>
<select id="getSaleSummaryCount2" resultType="com.czg.order.vo.SaleSummaryCountVo">
<select id="selectProStatByDay" resultType="com.czg.order.entity.ShopProdStatistic">
SELECT
sum(t1.pay_amount) as totalAmount,
sum(t1.refund_amount) as refundAmount,
count(*) as saleCount,
count(case when t1.refund_amount > 0 then 1 end) as refundCount
CASE WHEN detail.is_temporary = 1 THEN -1 ELSE detail.product_id END AS prodId,
CASE WHEN detail.is_temporary = 1 THEN '临时菜' ELSE prod.name END AS productName,
sum(detail.num-detail.return_num) as saleCount,
sum(detail.pay_amount) as saleAmount,
sum(detail.refund_num) refundCount,
sum(detail.return_amount) refundAmount
FROM
tb_order_info t1
left join (
SELECT
x1.order_id,
concat( ',', GROUP_CONCAT( x2.category_id ), ',' ) AS category_id,
GROUP_CONCAT( x1.product_name ) AS product_name
FROM
tb_order_detail x1
LEFT JOIN tb_product x2 ON x1.product_id = x2.id
GROUP BY x1.order_id
) t2 on t1.id = t2.order_id
where t1.shop_id = #{shopId}
and t1.status in ('part-refund','refund','done')
tb_order_info `order`
INNER JOIN tb_order_detail detail ON `order`.id = detail.order_id
LEFT JOIN tb_product prod ON detail.product_id = prod.id
WHERE
`order`.shop_id = #{shopId}
AND `order`.trade_day = #{tradeDay}
AND `order`.paid_time IS NOT NULL
<if test="productName != null and productName != ''">
and t2.product_name like concat('%', #{productName}, '%')
</if>
<if test="prodCategoryId != null">
and t2.category_id like concat('%,', #{prodCategoryId}, ',%')
</if>
<if test="beginDate != null and beginDate != ''">
and t1.create_time >= str_to_date(#{beginDate}, '%Y-%m-%d %H:%i:%s')
</if>
<if test="endDate != null and endDate != ''">
<![CDATA[
and t1.create_time <= str_to_date(#{endDate}, '%Y-%m-%d %H:%i:%s')
]]>
AND detail.name LIKE CONCAT('%',#{productName},'%')
</if>
GROUP BY prodId
</select>
<select id="findSaleSummaryList2" resultType="com.czg.order.vo.SaleSummaryInfoVo">
<select id="getProdStatSingleDate" resultType="com.czg.order.entity.ShopProdStatistic">
SELECT
t1.product_id,
t1.product_name as productName,
t3.name as category_name,
sum( t1.num ) AS saleCount,
sum( t1.pay_amount ) AS saleAmount,
sum( t1.return_num ) AS refundCount,
sum( t1.return_amount ) AS refundAmount
tb_shop_prod_statistic.*,
CASE WHEN tb_shop_prod_statistic.prod_id = -1 THEN '临时菜' ELSE prod.name END AS productName
FROM
tb_order_detail t1
left join tb_product t2 on t1.product_id = t2.id
left join tb_shop_prod_category t3 on t2.category_id = t3.id
where t1.shop_id = #{shopId}
and t1.status in ('part-refund','refund','done')
tb_shop_prod_statistic
LEFT JOIN tb_product prod ON tb_shop_prod_statistic.prod_id = prod.id
WHERE
tb_shop_prod_statistic.shop_id = #{shopId}
AND tb_shop_prod_statistic.create_day = #{day}
<if test="productName != null and productName != ''">
and t1.product_name like concat('%', #{productName}, '%')
AND prod.name LIKE CONCAT('%',#{productName},'%')
</if>
<if test="prodCategoryId != null">
and t2.category_id = #{prodCategoryId}
</if>
<if test="beginDate != null and beginDate != ''">
and t1.create_time >= str_to_date(#{beginDate}, '%Y-%m-%d %H:%i:%s')
</if>
<if test="endDate != null and endDate != ''">
<![CDATA[
and t1.create_time <= str_to_date(#{endDate}, '%Y-%m-%d %H:%i:%s')
]]>
</if>
GROUP BY t1.product_id,t1.product_name
ORDER BY sum( t1.num ) DESC,max(t1.product_id) DESC
ORDER BY
tb_shop_prod_statistic.sale_count DESC, tb_shop_prod_statistic.prod_id
</select>
<select id="findProdRandingSummaryPage2" resultType="com.czg.order.vo.DataSummaryProductSaleRankingVo">
select
t1.product_id,
t1.product_name,
sum(t1.num) as number,
sum(t1.pay_amount) as amount,
sum(t1.return_num) AS refundCount,
sum(t1.return_amount) AS refundAmount
from tb_order_detail t1
where t1.shop_id = #{shopId}
and t1.status in ('part-refund','refund','done')
<if test="beginDate != null and beginDate != ''">
and t1.create_time >= str_to_date(#{beginDate}, '%Y-%m-%d %H:%i:%s')
<select id="getProdStatDateRange" resultType="com.czg.order.entity.ShopProdStatistic">
SELECT
tb_shop_prod_statistic.prod_id as prodId,
CASE WHEN prod_id = -1 THEN '临时菜' ELSE prod.name END AS productName,
tb_shop_prod_statistic.shop_id as shopId,
SUM(tb_shop_prod_statistic.sale_count) AS saleCount,
SUM(tb_shop_prod_statistic.sale_amount) AS saleAmount,
SUM(tb_shop_prod_statistic.refund_count) AS refundCount,
SUM(tb_shop_prod_statistic.refund_amount) AS refundAmount
FROM
tb_shop_prod_statistic
LEFT JOIN tb_product prod ON tb_shop_prod_statistic.prod_id = prod.id
WHERE
tb_shop_prod_statistic.shop_id = #{shopId}
AND tb_shop_prod_statistic.create_day &gt;= #{start}
AND tb_shop_prod_statistic.create_day &lt;= #{end}
<if test="productName != null and productName != ''">
AND prod.name LIKE CONCAT('%',#{productName},'%')
</if>
<if test="endDate != null and endDate != ''">
<![CDATA[
and t1.create_time <= str_to_date(#{endDate}, '%Y-%m-%d %H:%i:%s')
]]>
</if>
GROUP BY t1.product_id,t1.product_name
order by sum(t1.num) desc
GROUP BY
tb_shop_prod_statistic.prod_id
ORDER BY
tb_shop_prod_statistic.sale_count DESC, tb_shop_prod_statistic.prod_id
</select>
<select id="summaryCountByDay" resultType="com.czg.order.vo.SaleSummaryCountVo">
SELECT
sum(detail.num-detail.return_num) as saleCount,
sum(detail.pay_amount) as saleAmount,
sum(detail.refund_num) refundCount,
sum(detail.return_amount) refundAmount
FROM
tb_order_info `order`
INNER JOIN tb_order_detail detail ON `order`.id = detail.order_id
LEFT JOIN tb_product prod ON detail.product_id = prod.id
WHERE
`order`.shop_id = #{shopId}
AND `order`.trade_day = #{day}
AND `order`.paid_time IS NOT NULL
<if test="productName != null and productName != ''">
AND detail.name LIKE CONCAT('%',#{productName},'%')
</if>
</select>
<select id="summaryCountSingleDate" resultType="com.czg.order.vo.SaleSummaryCountVo">
SELECT
sum(tb_shop_prod_statistic.sale_count) AS saleCount,
sum(tb_shop_prod_statistic.sale_amount) AS saleAmount,
sum(tb_shop_prod_statistic.refund_count) AS refundCount,
sum(tb_shop_prod_statistic.refund_amount) AS refundAmount
FROM
tb_shop_prod_statistic
WHERE
tb_shop_prod_statistic.shop_id = #{shopId}
AND tb_shop_prod_statistic.create_day = #{day}
<if test="productName != null and productName != ''">
AND prod.name LIKE CONCAT('%',#{productName},'%')
</if>
</select>
<select id="summaryCountDateRange" resultType="com.czg.order.vo.SaleSummaryCountVo">
SELECT
sum(tb_shop_prod_statistic.sale_count) AS saleCount,
sum(tb_shop_prod_statistic.sale_amount) AS saleAmount,
sum(tb_shop_prod_statistic.refund_count) AS refundCount,
sum(tb_shop_prod_statistic.refund_amount) AS refundAmount
FROM
tb_shop_prod_statistic
WHERE
tb_shop_prod_statistic.shop_id = #{shopId}
AND tb_shop_prod_statistic.create_day &gt;= #{start}
AND tb_shop_prod_statistic.create_day &lt;= #{end}
<if test="productName != null and productName != ''">
AND prod.name LIKE CONCAT('%',#{productName},'%')
</if>
</select>
</mapper>

View File

@@ -3,124 +3,50 @@
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.order.mapper.ShopTableOrderStatisticMapper">
<update id="incrInfo">
update tb_shop_table_order_statistic set order_count=order_count + #{count}, order_amount=order_amount + #{count}
where shop_id = #{shopId} and table_id = #{tableId} and create_day=#{date}
</update>
<select id="selectSummary" resultType="com.czg.order.entity.ShopTableOrderStatistic">
<select id="findSummaryExportList" resultType="com.czg.order.vo.TableSummaryExportVo">
SELECT
a.table_id as tableId, b.name as name, sum(a.order_count) as orderCount, sum(a.order_amount) as orderAmount
t.product_name,
date_format(t.create_time, '%Y-%m-%d') as create_date,
if(t.table_name = 'NONE','银收客',t.table_name) as table_name,
concat(if(t.table_name = 'NONE','银收客',t.table_name),'-',date_format(t.create_time, '%Y-%m-%d')) as tableConcatDate,
t.category_name,
t.unit_name,
group_concat(distinct t.sku_name SEPARATOR ';') as sku_name,
sum(t.num) as num,
avg(t.unit_price) as unit_price,
sum(t.num*t.unit_price) as amount,
sum(-t.refund_num) as refund_num,
sum(-t.refund_num*t.unit_price) as refund_amount
FROM
tb_shop_table_order_statistic as a
left join tb_shop_table as b on a.table_id = b.id
WHERE
a.shop_id = #{shopId}
<if test="startTime != null and startTime != ''">
AND a.create_day >= #{startTime}
</if>
<if test="endTime != null and endTime != ''">
and a.create_day &lt;= #{endTime}
</if>
GROUP BY
a.table_id
order by a.id desc
</select>
<select id="findSummaryList" resultType="com.czg.order.vo.TableSummaryInfoVo">
select
t1.table_code,
t1.table_id,
t1.table_name as table_name,
t1.area_name as area_name,
sum(t1.order_count) as order_count,
sum(t1.order_amount) as order_amount,
ifnull(sum(t1.refund_count),0) as refund_count,
ifnull(sum(t1.refund_amount),0) as refund_amount
from tb_shop_table_order_statistic t1
left join tb_shop_table t2 on t1.table_id = t2.id
left join tb_shop_table_area t3 on t2.area_id = t3.id
where t1.shop_id = #{shopId}
<if test="beginDate != null and beginDate != ''">
AND t1.create_day >= str_to_date(#{beginDate}, '%Y-%m-%d')
</if>
<if test="endDate != null and endDate != ''">
and t1.create_day &lt;= str_to_date(#{endDate}, '%Y-%m-%d')
</if>
group by t1.table_code
order by sum(t1.order_count) desc,sum(t1.order_amount) desc
</select>
<select id="findSummaryList2" resultType="com.czg.order.vo.TableSummaryInfoVo">
(
SELECT
t1.table_code,
t2.id as table_id,
ifnull(t2.NAME,t1.table_code) AS table_name,
ifnull(t3.NAME,'未知') AS area_name,
count( t1.id ) AS order_count,
sum( t1.pay_amount ) as order_amount,
ifnull(count( case when t1.refund_amount > 0 then 1 end),0) AS refund_count,
ifnull( sum( t1.refund_amount ), 0 ) AS refund_amount
t1.product_id,
t2.table_code,
IF(t2.table_code is null or t2.table_code = '' or t6.NAME is null, 'NONE', t6.NAME) AS table_name,
t1.create_time,
t4.NAME AS category_name,
t3.name as product_name,
t5.NAME AS unit_name,
t1.sku_name,
t1.num,
t1.unit_price,
t1.refund_num as refund_num
FROM
tb_order_info t1
LEFT JOIN tb_shop_table t2 ON t1.table_code = t2.table_code and t1.shop_id = t2.shop_id
LEFT JOIN tb_shop_table_area t3 ON t2.area_id = t3.id
where t1.shop_id = #{shopId}
and t1.table_code is not null
and t2.table_code is not null
and t2.table_code != ''
and t2.name is not null
and t1.paid_time is not null
tb_order_detail t1
LEFT JOIN tb_order_info t2 ON t1.order_id = t2.id
LEFT JOIN tb_product t3 ON t1.product_id = t3.id
LEFT JOIN tb_shop_prod_category t4 ON t3.category_id = t4.id
LEFT JOIN tb_shop_prod_unit t5 ON t3.unit_id = t5.id
LEFT JOIN tb_shop_table t6 ON t2.table_code = t6.table_code AND t1.shop_id = t6.shop_id and t6.table_code != '' and t6.table_code is not null
WHERE t1.shop_id = #{shopId}
<if test="beginDate != null and beginDate != ''">
AND t1.create_time >= str_to_date(#{beginDate}, '%Y-%m-%d %H:%i:%s')
</if>
<if test="endDate != null and endDate != ''">
and t1.create_time &lt;= str_to_date(#{endDate}, '%Y-%m-%d %H:%i:%s')
</if>
GROUP BY t1.table_code
order by count( t1.id ) desc,sum( t1.pay_amount ) desc
</select>
<select id="findSummaryExportList" resultType="com.czg.order.vo.TableSummaryExportVo">
SELECT
t.product_name,
date_format(t.create_time, '%Y-%m-%d') as create_date,
if(t.table_name = 'NONE','银收客',t.table_name) as table_name,
concat(if(t.table_name = 'NONE','银收客',t.table_name),'-',date_format(t.create_time, '%Y-%m-%d')) as tableConcatDate,
t.category_name,
t.unit_name,
group_concat(distinct t.sku_name SEPARATOR ';') as sku_name,
sum(t.num) as num,
avg(t.unit_price) as unit_price,
sum(t.num*t.unit_price) as amount,
sum(-t.refund_num) as refund_num,
sum(-t.refund_num*t.unit_price) as refund_amount
FROM
(
SELECT
t1.product_id,
t2.table_code,
IF(t2.table_code is null or t2.table_code = '' or t6.NAME is null, 'NONE', t6.NAME) AS table_name,
t1.create_time,
t4.NAME AS category_name,
t3.name as product_name,
t5.NAME AS unit_name,
t1.sku_name,
t1.num,
t1.unit_price,
t1.refund_num as refund_num
FROM
tb_order_detail t1
LEFT JOIN tb_order_info t2 ON t1.order_id = t2.id
LEFT JOIN tb_product t3 ON t1.product_id = t3.id
LEFT JOIN tb_shop_prod_category t4 ON t3.category_id = t4.id
LEFT JOIN tb_shop_prod_unit t5 ON t3.unit_id = t5.id
LEFT JOIN tb_shop_table t6 ON t2.table_code = t6.table_code AND t1.shop_id = t6.shop_id and t6.table_code != '' and t6.table_code is not null
WHERE t1.shop_id = #{shopId}
<if test="beginDate != null and beginDate != ''">
AND t1.create_time >= str_to_date(#{beginDate}, '%Y-%m-%d %H:%i:%s')
</if>
<if test="endDate != null and endDate != ''">
and t1.create_time &lt;= str_to_date(#{endDate}, '%Y-%m-%d %H:%i:%s')
</if>
) t
) t
group by t.product_id,date_format(t.create_time, '%Y-%m-%d'),t.table_name
order by t.table_name,t.table_code,date_format(t.create_time, '%Y-%m-%d'),t.category_name,t.product_id
</select>