This commit is contained in:
dzc committed 2026-03-13 09:55:31 +08:00
commit 23bb5fa2f9
29 files changed
+689 -55

No files matched your search

@@ -0,0 +1,30 @@
package com.bsd.pjcloud.canteen.api.feign;
import com.bsd.pjcloud.canteen.vo.erp.ProductTemplateVO;
import com.bsd.pjcloud.common.core.constant.ServiceNameConstants;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.feign.annotation.NoToken;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
/**
* 加工模版远程服务(用于线上订餐原料转换)
*/
@FeignClient(contextId = "remoteCanteenProductionTemplateService", value = ServiceNameConstants.CANTEEN_SERVICE)
public interface RemoteCanteenProductionTemplateService {
/**
* 根据菜品id列表获取加工模版详情
*/
@PostMapping("/canteenProductionTemplate/inner/getDetailsByDishIds")
R<List<ProductTemplateVO>> getDetailsByDishIds(@RequestBody List<Long> dishIds);
/**
* 根据菜品id列表获取线上加工模版详情(仅 template_type=2,用于线上订餐统计)
*/
@PostMapping("/canteenProductionTemplate/inner/getDetailsByDishIdsForOnline")
R<List<ProductTemplateVO>> getDetailsByDishIdsForOnline(@RequestBody List<Long> dishIds);
}
@@ -18,6 +18,8 @@ public class CanteenErpMaterialSaveDTO {
@Schema(description="商品单位")
private String unit;
@Schema(description="食材类型:1-线下,2-线上")
private Integer materialType;
/**
* 期初数量
@@ -41,6 +41,12 @@ public class CanteenErpMaterialEntity extends Model<CanteenErpMaterialEntity> {
@Schema(description="商品单位")
private String unit;
/**
* 食材类型:1-线下,2-线上
*/
@Schema(description="食材类型:1-线下,2-线上")
private Integer materialType;
/**
* 部门id
*/
@@ -59,6 +59,12 @@ public class CanteenProductionTemplateDetailEntity extends Model<CanteenProducti
@Schema(description="损耗率")
private Double lossRate;
/**
* 营养元素描述(用户自定义,如:每100g约120kcal、蛋白质5g等)
*/
@Schema(description="营养元素描述")
private String nutritionDesc;
/**
* 创建人
*/
@@ -44,6 +44,12 @@ public class CanteenProductionTemplateEntity extends Model<CanteenProductionTemp
@Schema(description="菜品id")
private Long dishId;
/**
* 模版类型:1-线下,2-线上(用于数据隔离与统计区分)
*/
@Schema(description="模版类型:1-线下,2-线上")
private Integer templateType;
/**
* 部门id
*/
@@ -101,4 +107,12 @@ public class CanteenProductionTemplateEntity extends Model<CanteenProductionTemp
@TableField(exist = false)
@EntityMapping(thisField = "id", joinField = "templateId")
private List<CanteenProductionTemplateDetailEntity> canteenProductionTemplateDetailList;
/**
* 菜品名称(回显用,不落库;根据 templateType 从 CanteenSpu 或 CanteenOnlineSpu 解析)
*/
@ExcelIgnore
@TableField(exist = false)
@Schema(description = "菜品名称")
private String dishName;
}
@@ -122,6 +122,12 @@ public class CanteenOnlineSpuAppVO {
@Schema(description="部门id")
private Long deptId;
/**
* 关联线下菜品id(canteen_spu.id),用于原料转换
*/
@Schema(description="关联线下菜品id")
private Long linkSpuId;
/**
* 创建时间
@@ -20,6 +20,8 @@ public class CanteenErpMaterialVO {
@Schema(description="商品单位")
private String unit;
@Schema(description="食材类型:1-线下,2-线上")
private Integer materialType;
/**
* 期初数量
@@ -0,0 +1,21 @@
package com.bsd.pjcloud.canteen.vo.erp;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "线上菜品简略VO,用于模板选择弹窗")
public class CanteenOnlineSpuSimpleVO {
@Schema(description = "id")
private Long id;
@Schema(description = "菜品名称")
private String name;
@Schema(description = "菜品封面图")
private String picUrl;
@Schema(description = "关联线下菜品id")
private Long linkSpuId;
}
@@ -59,4 +59,6 @@ public class ProductionTemplateDetailVO{
@Schema(description="单位")
private String unit;
@Schema(description="营养元素描述")
private String nutritionDesc;
}
@@ -49,7 +49,7 @@ public class CanteenErpMaterialController {
* @param canteenErpMaterial 原料表
* @return
*/
@Operation(summary = "分页查询" , description = "分页查询" )
@Operation(summary = "分页查询" , description = "分页查询,支持按食材类型筛选" )
@GetMapping("/page" )
@PreAuthorize("@pms.hasPermission('canteen_canteenErpMaterial_view')" )
public R getCanteenErpMaterialPage(@ParameterObject Page page, @ParameterObject CanteenErpMaterialEntity canteenErpMaterial) {
@@ -127,13 +127,15 @@ public class CanteenErpMaterialController {
/**
* erp选择食材分页数据
* 分页查询
* @param page 分页对象
* @return
* @param name 食材名称
* @param materialType 食材类型:1-线下,2-线上,不传默认线下
*/
@Operation(summary = "选择食材分页数据" , description = "选择食材分页数据" )
@Operation(summary = "选择食材分页数据" , description = "选择食材分页数据,materialType不传默认线下" )
@GetMapping("/erpMaterialPage" )
public R getErpMaterialPage(@ParameterObject Page page, @RequestParam(value = "name",required = false)String name) {
return R.ok(canteenErpMaterialService.getErpMaterialPage(page, name));
public R getErpMaterialPage(@ParameterObject Page page,
@RequestParam(value = "name", required = false) String name,
@RequestParam(value = "materialType", required = false) Integer materialType) {
return R.ok(canteenErpMaterialService.getErpMaterialPage(page, name, materialType));
}
}
@@ -45,6 +45,22 @@ public class CanteenOnlineSpuController {
private final CanteenOnlineSpuService canteenOnlineSpuService;
/**
* 模板选择线上菜品分页(用于加工模版选择菜品弹窗)
* 类似 /canteen/canteenSpu/getCanteenSpuSimplePage
*
* @param page 分页对象
* @param canteenOnlineSpu 查询条件(name、categoryId)
* @return
*/
@Operation(summary = "模板选择线上菜品分页", description = "用于加工模版选择线上菜品弹窗,支持按名称、分类筛选")
@GetMapping("/getCanteenOnlineSpuSimplePage")
@PreAuthorize("@pms.hasPermission('canteen_canteenOnlineSpu_view')")
public R<Page<com.bsd.pjcloud.canteen.vo.erp.CanteenOnlineSpuSimpleVO>> getCanteenOnlineSpuSimplePage(
@ParameterObject Page page, @ParameterObject CanteenOnlineSpuEntity canteenOnlineSpu) {
return R.ok(canteenOnlineSpuService.getCanteenOnlineSpuSimplePageScope(page, canteenOnlineSpu));
}
/**
* 分页查询
*
@@ -10,6 +10,7 @@ import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.log.annotation.SysLog;
import com.bsd.pjcloud.canteen.entity.CanteenProductionTemplateEntity;
import com.bsd.pjcloud.canteen.service.CanteenProductionTemplateService;
import com.bsd.pjcloud.common.security.annotation.Inner;
import com.bsd.pjcloud.common.security.util.SecurityUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import com.bsd.pjcloud.common.excel.annotation.ResponseExcel;
@@ -50,7 +51,8 @@ public class CanteenProductionTemplateController {
@PreAuthorize("@pms.hasPermission('canteen_canteenProductionTemplate_view')" )
public R getCanteenProductionTemplatePage(@ParameterObject Page page, @ParameterObject CanteenProductionTemplateEntity canteenProductionTemplate) {
LambdaQueryWrapper<CanteenProductionTemplateEntity> wrapper = Wrappers.lambdaQuery();
wrapper.eq(StrUtil.isNotBlank(canteenProductionTemplate.getTemplateName()),CanteenProductionTemplateEntity::getTemplateName,canteenProductionTemplate.getTemplateName());
wrapper.eq(StrUtil.isNotBlank(canteenProductionTemplate.getTemplateName()),CanteenProductionTemplateEntity::getTemplateName,canteenProductionTemplate.getTemplateName())
.eq(canteenProductionTemplate.getTemplateType() != null, CanteenProductionTemplateEntity::getTemplateType, canteenProductionTemplate.getTemplateType());
return R.ok(canteenProductionTemplateService.pageScope(page, wrapper));
}
@@ -129,4 +131,16 @@ public class CanteenProductionTemplateController {
public R getSelectTemplatePage(@ParameterObject Page page, @RequestParam(value = "name",required = false)String name) {
return R.ok(canteenProductionTemplateService.getSelectTemplatePageScope(page, name));
}
@Operation(summary = "根据菜品id列表获取模版详情(内部)")
@PostMapping("/inner/getDetailsByDishIds")
public R<List<com.bsd.pjcloud.canteen.vo.erp.ProductTemplateVO>> getDetailsByDishIds(@RequestBody List<Long> dishIds) {
return R.ok(canteenProductionTemplateService.getDetailsByDishIds(dishIds));
}
@Operation(summary = "根据菜品id列表获取线上模版详情(内部)", description = "仅返回 template_type=2 的模版,用于线上订餐统计")
@PostMapping("/inner/getDetailsByDishIdsForOnline")
public R<List<com.bsd.pjcloud.canteen.vo.erp.ProductTemplateVO>> getDetailsByDishIdsForOnline(@RequestBody List<Long> dishIds) {
return R.ok(canteenProductionTemplateService.getDetailsByDishIdsForOnline(dishIds));
}
}
@@ -7,6 +7,7 @@ import com.bsd.pjcloud.canteen.dto.erp.CanteenErpMaterialEditorDTO;
import com.bsd.pjcloud.canteen.dto.erp.CanteenErpMaterialSaveDTO;
import com.bsd.pjcloud.canteen.entity.CanteenErpMaterialEntity;
import com.bsd.pjcloud.canteen.vo.erp.CanteenErpMaterialVO;
import com.bsd.pjcloud.canteen.vo.erp.ErpMaterialPageVo;
public interface CanteenErpMaterialService extends IService<CanteenErpMaterialEntity> {
@@ -37,10 +38,11 @@ public interface CanteenErpMaterialService extends IService<CanteenErpMaterialEn
/**
* erp选择分页
* erp选择食材分页,materialType不传默认线下(1)
* @param page
* @param name
* @param materialType 食材类型:1-线下,2-线上
* @return
*/
Object getErpMaterialPage(Page page, String name);
Page<ErpMaterialPageVo> getErpMaterialPage(Page page, String name, Integer materialType);
}
@@ -7,12 +7,18 @@ import com.bsd.pjcloud.canteen.dto.CanteenOnlineSpuUpdateStockReqDTO;
import com.bsd.pjcloud.canteen.dto.canteenonline.CanteenOnlineSpuSaveDto;
import com.bsd.pjcloud.canteen.entity.CanteenOnlineSpuEntity;
import com.bsd.pjcloud.canteen.vo.APPOnlineSpuVO;
import com.bsd.pjcloud.canteen.vo.erp.CanteenOnlineSpuSimpleVO;
import com.bsd.pjcloud.common.core.util.R;
import java.util.List;
public interface CanteenOnlineSpuService extends IService<CanteenOnlineSpuEntity> {
/**
* 模板选择线上菜品分页(简化数据,用于选择弹窗)
*/
Page<CanteenOnlineSpuSimpleVO> getCanteenOnlineSpuSimplePageScope(Page page, CanteenOnlineSpuEntity canteenOnlineSpu);
/**
* 根据分类id获取商品列表
@@ -20,6 +20,20 @@ public interface CanteenProductionTemplateService extends MPJDeepService<Canteen
ProductTemplateVO getDetailById(Long id);
/**
* 根据菜品id列表获取加工模版详情
* @param dishIds 菜品id列表(canteen_spu.id 或 canteen_online_spu.link_spu_id)
* @return 模版详情列表
*/
java.util.List<ProductTemplateVO> getDetailsByDishIds(java.util.List<Long> dishIds);
/**
* 根据菜品id列表获取线上加工模版详情(仅 template_type=2)
* @param dishIds 菜品id列表(canteen_online_spu.link_spu_id)
* @return 模版详情列表
*/
java.util.List<ProductTemplateVO> getDetailsByDishIdsForOnline(java.util.List<Long> dishIds);
/**
* 分页
* @param page
@@ -20,6 +20,7 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
@@ -43,18 +44,23 @@ public class CanteenErpMaterialServiceImpl extends ServiceImpl<CanteenErpMateria
CanteenErpMaterialEntity canteenErpMaterialEntity = new CanteenErpMaterialEntity();
canteenErpMaterialEntity.setName(canteenErpMaterialSaveDTO.getName());
canteenErpMaterialEntity.setUnit(canteenErpMaterialSaveDTO.getUnit());
canteenErpMaterialEntity.setMaterialType(canteenErpMaterialSaveDTO.getMaterialType() != null ? canteenErpMaterialSaveDTO.getMaterialType() : 1);
canteenErpMaterialEntity.setDeptId(deptId);
canteenErpMaterialEntity.setPurchasePrice(canteenErpMaterialSaveDTO.getPurchasePrice());
canteenErpMaterialEntity.setCategoryId(canteenErpMaterialSaveDTO.getCategoryId());
int insert = baseMapper.insert(canteenErpMaterialEntity);
CanteenStockEntity canteenStockEntity = new CanteenStockEntity();
canteenStockEntity.setItemType("1");
canteenStockEntity.setItemId(canteenErpMaterialEntity.getId());
canteenStockEntity.setBaseStock(canteenErpMaterialSaveDTO.getBaseStock());
canteenStockEntity.setCurrentStock(canteenErpMaterialSaveDTO.getBaseStock());
canteenStockEntity.setInventoryUpperLimit(canteenErpMaterialSaveDTO.getInventoryUpperLimit());
canteenStockEntity.setInventoryLowerLimit(canteenErpMaterialSaveDTO.getInventoryLowerLimit());
canteenStockMapper.insert(canteenStockEntity);
// 线上食材(materialType=2)仅作转换规则,不建库存
int materialType = canteenErpMaterialEntity.getMaterialType() != null ? canteenErpMaterialEntity.getMaterialType() : 1;
if (materialType == 1) {
CanteenStockEntity canteenStockEntity = new CanteenStockEntity();
canteenStockEntity.setItemType("1");
canteenStockEntity.setItemId(canteenErpMaterialEntity.getId());
canteenStockEntity.setBaseStock(canteenErpMaterialSaveDTO.getBaseStock());
canteenStockEntity.setCurrentStock(canteenErpMaterialSaveDTO.getBaseStock());
canteenStockEntity.setInventoryUpperLimit(canteenErpMaterialSaveDTO.getInventoryUpperLimit());
canteenStockEntity.setInventoryLowerLimit(canteenErpMaterialSaveDTO.getInventoryLowerLimit());
canteenStockMapper.insert(canteenStockEntity);
}
return insert;
}
@@ -66,22 +72,28 @@ public class CanteenErpMaterialServiceImpl extends ServiceImpl<CanteenErpMateria
canteenErpMaterialEntity.setId(id);
canteenErpMaterialEntity.setName(canteenErpMaterialEditorDTO.getName());
canteenErpMaterialEntity.setUnit(canteenErpMaterialEditorDTO.getUnit());
canteenErpMaterialEntity.setMaterialType(canteenErpMaterialEditorDTO.getMaterialType() != null ? canteenErpMaterialEditorDTO.getMaterialType() : 1);
canteenErpMaterialEntity.setPurchasePrice(canteenErpMaterialEditorDTO.getPurchasePrice());
canteenErpMaterialEntity.setCategoryId(canteenErpMaterialEditorDTO.getCategoryId());
LambdaQueryWrapper<CanteenStockEntity> canteenStockEntityLambdaQueryWrapper = new LambdaQueryWrapper<>();
canteenStockEntityLambdaQueryWrapper.eq(CanteenStockEntity::getItemId, id);
CanteenStockEntity sourceCanteenStockEntity = canteenStockMapper.selectOne(canteenStockEntityLambdaQueryWrapper);
CanteenStockEntity canteenStockEntity = new CanteenStockEntity();
canteenStockEntity.setItemId(canteenErpMaterialEntity.getId());
canteenStockEntity.setBaseStock(canteenErpMaterialEditorDTO.getBaseStock());
canteenStockEntity.setCurrentStock(canteenErpMaterialEditorDTO.getBaseStock()+(sourceCanteenStockEntity.getCurrentStock()-sourceCanteenStockEntity.getBaseStock()));
canteenStockEntity.setInventoryUpperLimit(canteenErpMaterialEditorDTO.getInventoryUpperLimit());
canteenStockEntity.setInventoryLowerLimit(canteenErpMaterialEditorDTO.getInventoryLowerLimit());
int i = baseMapper.updateById(canteenErpMaterialEntity);
canteenStockMapper.update(canteenStockEntity,
new LambdaQueryWrapper<CanteenStockEntity>().eq(CanteenStockEntity::getItemId, id));
// 线上食材(materialType=2)无库存,不更新
int materialType = canteenErpMaterialEditorDTO.getMaterialType() != null ? canteenErpMaterialEditorDTO.getMaterialType() : 1;
if (materialType == 1) {
LambdaQueryWrapper<CanteenStockEntity> canteenStockEntityLambdaQueryWrapper = new LambdaQueryWrapper<>();
canteenStockEntityLambdaQueryWrapper.eq(CanteenStockEntity::getItemId, id);
CanteenStockEntity sourceCanteenStockEntity = canteenStockMapper.selectOne(canteenStockEntityLambdaQueryWrapper);
if (sourceCanteenStockEntity != null) {
CanteenStockEntity canteenStockEntity = new CanteenStockEntity();
canteenStockEntity.setItemId(canteenErpMaterialEntity.getId());
canteenStockEntity.setBaseStock(canteenErpMaterialEditorDTO.getBaseStock());
canteenStockEntity.setCurrentStock(canteenErpMaterialEditorDTO.getBaseStock() + (sourceCanteenStockEntity.getCurrentStock() - sourceCanteenStockEntity.getBaseStock()));
canteenStockEntity.setInventoryUpperLimit(canteenErpMaterialEditorDTO.getInventoryUpperLimit());
canteenStockEntity.setInventoryLowerLimit(canteenErpMaterialEditorDTO.getInventoryLowerLimit());
canteenStockMapper.update(canteenStockEntity,
new LambdaQueryWrapper<CanteenStockEntity>().eq(CanteenStockEntity::getItemId, id));
}
}
return i;
}
@@ -94,27 +106,39 @@ public class CanteenErpMaterialServiceImpl extends ServiceImpl<CanteenErpMateria
@Override
public CanteenErpMaterialVO getCanteenErpMaterialVOById(Long id) {
CanteenErpMaterialEntity canteenErpMaterialEntity = baseMapper.selectById(id);
LambdaQueryWrapper<CanteenStockEntity> canteenStockEntityLambdaQueryWrapper = new LambdaQueryWrapper<>();
canteenStockEntityLambdaQueryWrapper.eq(CanteenStockEntity::getItemId, id);
CanteenStockEntity sourceCanteenStockEntity = canteenStockMapper.selectOne(canteenStockEntityLambdaQueryWrapper);
CanteenErpMaterialVO canteenErpMaterialVO = new CanteenErpMaterialVO();
canteenErpMaterialVO.setId(canteenErpMaterialEntity.getId());
canteenErpMaterialVO.setName(canteenErpMaterialEntity.getName());
canteenErpMaterialVO.setUnit(canteenErpMaterialEntity.getUnit());
canteenErpMaterialVO.setBaseStock(sourceCanteenStockEntity.getBaseStock());
canteenErpMaterialVO.setInventoryUpperLimit(sourceCanteenStockEntity.getInventoryUpperLimit());
canteenErpMaterialVO.setInventoryLowerLimit(sourceCanteenStockEntity.getInventoryLowerLimit());
canteenErpMaterialVO.setCurrentStock(sourceCanteenStockEntity.getCurrentStock());
canteenErpMaterialVO.setPurchasePrice(canteenErpMaterialEntity.getPurchasePrice());
canteenErpMaterialVO.setCategoryId(canteenErpMaterialEntity.getCategoryId());
canteenErpMaterialVO.setMaterialType(canteenErpMaterialEntity.getMaterialType());
// 线上食材无库存,仅线下查询库存
int materialType = canteenErpMaterialEntity.getMaterialType() != null ? canteenErpMaterialEntity.getMaterialType() : 1;
if (materialType == 1) {
LambdaQueryWrapper<CanteenStockEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CanteenStockEntity::getItemId, id);
CanteenStockEntity stock = canteenStockMapper.selectOne(wrapper);
if (stock != null) {
canteenErpMaterialVO.setBaseStock(stock.getBaseStock());
canteenErpMaterialVO.setInventoryUpperLimit(stock.getInventoryUpperLimit());
canteenErpMaterialVO.setInventoryLowerLimit(stock.getInventoryLowerLimit());
canteenErpMaterialVO.setCurrentStock(stock.getCurrentStock());
}
} else {
canteenErpMaterialVO.setBaseStock(0D);
canteenErpMaterialVO.setCurrentStock(0D);
canteenErpMaterialVO.setInventoryUpperLimit(0);
canteenErpMaterialVO.setInventoryLowerLimit(0);
}
return canteenErpMaterialVO;
}
@Override
public Page<ErpMaterialPageVo> getErpMaterialPage(Page page, String name) {
public Page<ErpMaterialPageVo> getErpMaterialPage(Page page, String name, Integer materialType) {
LambdaQueryWrapper<CanteenErpMaterialEntity> smErpUnitEntityLambdaQueryWrapper = new LambdaQueryWrapper<>();
smErpUnitEntityLambdaQueryWrapper.like(StringUtils.isNotBlank(name), CanteenErpMaterialEntity::getName, name);
smErpUnitEntityLambdaQueryWrapper.like(StringUtils.isNotBlank(name), CanteenErpMaterialEntity::getName, name)
.eq(CanteenErpMaterialEntity::getMaterialType, materialType != null ? materialType : 1);
Page<CanteenErpMaterialEntity> spuPage = baseMapper.selectPageByScope(page, smErpUnitEntityLambdaQueryWrapper, DataScope.of());
// 将 SmErpSpuEntity 转换为 SmErpPageScopeVo
@@ -122,22 +146,22 @@ public class CanteenErpMaterialServiceImpl extends ServiceImpl<CanteenErpMateria
.map(entity -> BeanUtils.toBean(entity, ErpMaterialPageVo.class))
.collect(Collectors.toList());
List<Long> materialIds = voList.stream().map(vo -> vo.getId()).collect(Collectors.toList());
List<Long> materialIds = voList.stream().map(ErpMaterialPageVo::getId).collect(Collectors.toList());
// 批量查询库存信息
List<CanteenStockEntity> stocks = canteenStockMapper.selectList(
// 批量查询库存信息(materialIds 为空时跳过,避免 IN () 语法错误)
List<CanteenStockEntity> stocks = materialIds.isEmpty() ? Collections.emptyList() : canteenStockMapper.selectList(
new LambdaQueryWrapper<CanteenStockEntity>()
.in(CanteenStockEntity::getItemId, materialIds));
.in(CanteenStockEntity::getItemId, materialIds));
// 建立SPU ID与库存的映射关系
Map<Long,CanteenStockEntity> stockMap = stocks.stream()
Map<Long, CanteenStockEntity> stockMap = stocks.stream()
.collect(Collectors.toMap(CanteenStockEntity::getItemId, smErpStockEntity -> smErpStockEntity));
voList.stream().forEach(vo -> {
//库存
CanteenStockEntity smErpStockEntity = stockMap.get(vo.getId());
vo.setStock(smErpStockEntity != null ? smErpStockEntity.getCurrentStock() : 0);
CanteenStockEntity stock = stockMap.get(vo.getId());
Double currentStock = (stock != null && stock.getCurrentStock() != null) ? stock.getCurrentStock() : 0D;
vo.setStock(currentStock);
});
// 创建新的 Page 对象并设置数据
@@ -16,6 +16,7 @@ import com.bsd.pjcloud.canteen.mapper.CanteenOnlineCategoryMapper;
import com.bsd.pjcloud.canteen.mapper.CanteenOnlineSpuMapper;
import com.bsd.pjcloud.canteen.service.CanteenOnlineSpuService;
import com.bsd.pjcloud.canteen.vo.APPOnlineSpuVO;
import com.bsd.pjcloud.canteen.vo.erp.CanteenOnlineSpuSimpleVO;
import com.bsd.pjcloud.common.core.util.BeanUtils;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.data.datascope.DataScope;
@@ -33,6 +34,7 @@ import java.util.Set;
import java.util.stream.Collectors;
import java.util.Objects;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
/**
* 在线点餐spu
@@ -166,6 +168,23 @@ public class CanteenOnlineSpuServiceImpl extends ServiceImpl<CanteenOnlineSpuMap
}
}
@Override
public Page<CanteenOnlineSpuSimpleVO> getCanteenOnlineSpuSimplePageScope(Page page, CanteenOnlineSpuEntity canteenOnlineSpu) {
LambdaQueryWrapper<CanteenOnlineSpuEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StrUtil.isNotBlank(canteenOnlineSpu.getName()), CanteenOnlineSpuEntity::getName, canteenOnlineSpu.getName())
.eq(Objects.nonNull(canteenOnlineSpu.getCategoryId()), CanteenOnlineSpuEntity::getCategoryId, canteenOnlineSpu.getCategoryId())
.orderByAsc(CanteenOnlineSpuEntity::getSort).orderByDesc(CanteenOnlineSpuEntity::getId);
Page<CanteenOnlineSpuEntity> pageData = baseMapper.selectPageByScope(page, wrapper, DataScope.of());
List<CanteenOnlineSpuSimpleVO> voList = BeanUtils.toBean(pageData.getRecords(), CanteenOnlineSpuSimpleVO.class);
Page<CanteenOnlineSpuSimpleVO> resultPage = new Page<>();
resultPage.setRecords(voList);
resultPage.setCurrent(pageData.getCurrent());
resultPage.setPages(pageData.getPages());
resultPage.setSize(pageData.getSize());
resultPage.setTotal(pageData.getTotal());
return resultPage;
}
@Override
public Object pageScope(Page page, LambdaQueryWrapper<CanteenOnlineSpuEntity> wrapper) {
Page pageData = baseMapper.selectPageByScope(page, wrapper, DataScope.of());
@@ -6,8 +6,10 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bsd.pjcloud.canteen.entity.CanteenErpMaterialEntity;
import com.bsd.pjcloud.canteen.entity.CanteenProductionTemplateEntity;
import com.bsd.pjcloud.canteen.entity.CanteenOnlineSpuEntity;
import com.bsd.pjcloud.canteen.entity.CanteenSpuEntity;
import com.bsd.pjcloud.canteen.mapper.CanteenErpMaterialMapper;
import com.bsd.pjcloud.canteen.mapper.CanteenOnlineSpuMapper;
import com.bsd.pjcloud.canteen.mapper.CanteenProductionTemplateMapper;
import com.bsd.pjcloud.canteen.mapper.CanteenSpuMapper;
import com.bsd.pjcloud.canteen.service.CanteenProductionTemplateService;
@@ -38,8 +40,11 @@ import java.util.stream.Collectors;
public class CanteenProductionTemplateServiceImpl extends ServiceImpl<CanteenProductionTemplateMapper, CanteenProductionTemplateEntity> implements CanteenProductionTemplateService {
private final CanteenProductionTemplateDetailMapper canteenProductionTemplateDetailMapper;
private final CanteenSpuMapper canteenSpuMapper;
private final CanteenOnlineSpuMapper canteenOnlineSpuMapper;
private final CanteenErpMaterialMapper canteenErpMaterialMapper;
private static final int TEMPLATE_TYPE_ONLINE = 2;
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean saveDeep(CanteenProductionTemplateEntity canteenProductionTemplate) {
@@ -85,29 +90,101 @@ public class CanteenProductionTemplateServiceImpl extends ServiceImpl<CanteenPro
@Override
public ProductTemplateVO getDetailById(Long id) {
CanteenProductionTemplateEntity canteenProductionTemplateEntity = baseMapper.selectById(id);
if (canteenProductionTemplateEntity == null) return null;
Long dishId = canteenProductionTemplateEntity.getDishId();
CanteenSpuEntity canteenSpuEntity = canteenSpuMapper.selectById(dishId);
String dishName = resolveDishName(canteenProductionTemplateEntity.getTemplateType(), dishId);
LambdaQueryWrapper<CanteenProductionTemplateDetailEntity> canteenProductionTemplateDetailEntityLambdaQueryWrapper = new LambdaQueryWrapper<>();
canteenProductionTemplateDetailEntityLambdaQueryWrapper.eq(CanteenProductionTemplateDetailEntity::getTemplateId, id);
List<CanteenProductionTemplateDetailEntity> canteenProductionTemplateDetailEntities = canteenProductionTemplateDetailMapper.selectList(canteenProductionTemplateDetailEntityLambdaQueryWrapper);
ProductTemplateVO bean = BeanUtils.toBean(canteenProductionTemplateEntity, ProductTemplateVO.class);
bean.setDishName(canteenSpuEntity.getName());
bean.setDishName(dishName);
List<ProductionTemplateDetailVO> productionTemplateDetailVOList= BeanUtils.toBean(canteenProductionTemplateDetailEntities, ProductionTemplateDetailVO.class);
for (ProductionTemplateDetailVO productionTemplateDetailVO : productionTemplateDetailVOList) {
Long materialId = productionTemplateDetailVO.getMaterialId();
CanteenErpMaterialEntity canteenErpMaterialEntity = canteenErpMaterialMapper.selectById(materialId);
productionTemplateDetailVO.setMaterialName(canteenErpMaterialEntity.getName());
productionTemplateDetailVO.setUnit(canteenErpMaterialEntity.getUnit());
CanteenErpMaterialEntity material = canteenErpMaterialMapper.selectById(materialId);
if (material != null) {
productionTemplateDetailVO.setMaterialName(material.getName());
productionTemplateDetailVO.setUnit(material.getUnit());
}
}
bean.setCanteenProductionTemplateDetailList(productionTemplateDetailVOList);
return bean;
}
@Override
public List<ProductTemplateVO> getDetailsByDishIds(List<Long> dishIds) {
if (CollUtil.isEmpty(dishIds)) {
return Collections.emptyList();
}
List<CanteenProductionTemplateEntity> templates = baseMapper.selectList(
Wrappers.<CanteenProductionTemplateEntity>lambdaQuery().in(CanteenProductionTemplateEntity::getDishId, dishIds));
if (CollUtil.isEmpty(templates)) {
return Collections.emptyList();
}
return buildTemplateDetailList(templates);
}
/** 根据模板类型解析菜品名称:线上查 CanteenOnlineSpu,线下查 CanteenSpu */
private String resolveDishName(Integer templateType, Long dishId) {
if (dishId == null) return "";
if (TEMPLATE_TYPE_ONLINE == templateType) {
CanteenOnlineSpuEntity onlineSpu = canteenOnlineSpuMapper.selectById(dishId);
if (onlineSpu != null) return onlineSpu.getName();
// 兼容:线上模板可能存的是 linkSpuId,回退查线下
}
CanteenSpuEntity spu = canteenSpuMapper.selectById(dishId);
return spu != null ? spu.getName() : "";
}
@Override
public List<ProductTemplateVO> getDetailsByDishIdsForOnline(List<Long> dishIds) {
if (CollUtil.isEmpty(dishIds)) {
return Collections.emptyList();
}
List<CanteenProductionTemplateEntity> templates = baseMapper.selectList(
Wrappers.<CanteenProductionTemplateEntity>lambdaQuery()
.in(CanteenProductionTemplateEntity::getDishId, dishIds)
.eq(CanteenProductionTemplateEntity::getTemplateType, TEMPLATE_TYPE_ONLINE));
if (CollUtil.isEmpty(templates)) {
return Collections.emptyList();
}
return buildTemplateDetailList(templates);
}
private List<ProductTemplateVO> buildTemplateDetailList(List<CanteenProductionTemplateEntity> templates) {
List<Long> templateIds = templates.stream().map(CanteenProductionTemplateEntity::getId).collect(Collectors.toList());
List<CanteenProductionTemplateDetailEntity> details = canteenProductionTemplateDetailMapper.selectList(
Wrappers.<CanteenProductionTemplateDetailEntity>lambdaQuery().in(CanteenProductionTemplateDetailEntity::getTemplateId, templateIds));
Set<Long> dishIdSet = templates.stream().map(CanteenProductionTemplateEntity::getDishId).collect(Collectors.toSet());
Set<Long> materialIds = details.stream().map(CanteenProductionTemplateDetailEntity::getMaterialId).collect(Collectors.toSet());
Map<Long, CanteenSpuEntity> dishMap = CollUtil.isEmpty(dishIdSet) ? Collections.emptyMap()
: canteenSpuMapper.selectByIds(dishIdSet).stream().collect(Collectors.toMap(CanteenSpuEntity::getId, e -> e));
Map<Long, CanteenErpMaterialEntity> materialMap = CollUtil.isEmpty(materialIds) ? Collections.emptyMap()
: canteenErpMaterialMapper.selectByIds(materialIds).stream().collect(Collectors.toMap(CanteenErpMaterialEntity::getId, e -> e));
Map<Long, List<CanteenProductionTemplateDetailEntity>> detailMap = details.stream().collect(Collectors.groupingBy(CanteenProductionTemplateDetailEntity::getTemplateId));
List<ProductTemplateVO> result = new ArrayList<>();
for (CanteenProductionTemplateEntity t : templates) {
ProductTemplateVO vo = BeanUtils.toBean(t, ProductTemplateVO.class);
vo.setDishName(dishMap.getOrDefault(t.getDishId(), new CanteenSpuEntity()).getName());
List<ProductionTemplateDetailVO> detailVOList = BeanUtils.toBean(detailMap.getOrDefault(t.getId(), Collections.emptyList()), ProductionTemplateDetailVO.class);
for (ProductionTemplateDetailVO d : detailVOList) {
CanteenErpMaterialEntity m = materialMap.get(d.getMaterialId());
if (m != null) {
d.setMaterialName(m.getName());
d.setUnit(m.getUnit());
}
}
vo.setCanteenProductionTemplateDetailList(detailVOList);
result.add(vo);
}
return result;
}
@Override
public Object pageScope(Page page, LambdaQueryWrapper<CanteenProductionTemplateEntity> wrapper) {
Page pageData = baseMapper.selectPageByScope(page, wrapper, DataScope.of());
// pageData.getRecords().forEach(item->{
// });
Page<CanteenProductionTemplateEntity> pageData = baseMapper.selectPageByScope(page, wrapper, DataScope.of());
pageData.getRecords().forEach(item ->
item.setDishName(resolveDishName(item.getTemplateType(), item.getDishId())));
return pageData;
}
@@ -7,6 +7,7 @@
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="unit" column="unit"/>
<result property="materialType" column="material_type"/>
<result property="deptId" column="dept_id"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
@@ -22,6 +23,7 @@
m.id,
m.name,
m.unit,
m.material_type AS materialType,
m.category_id,
m.purchase_price,
s.base_stock AS baseStock,
@@ -36,6 +38,9 @@
<if test="canteenErpMaterial.name != null">
and m.name like concat('%',#{canteenErpMaterial.name},'%')
</if>
<if test="canteenErpMaterial.materialType != null">
and m.material_type = #{canteenErpMaterial.materialType}
</if>
</where>
</select>
</mapper>
@@ -10,6 +10,7 @@
<result property="deptId" column="dept_id"/>
<result property="baseUsage" column="base_usage"/>
<result property="lossRate" column="loss_rate"/>
<result property="nutritionDesc" column="nutrition_desc"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
@@ -7,6 +7,7 @@
<id property="id" column="id"/>
<result property="templateName" column="template_name"/>
<result property="dishId" column="dish_id"/>
<result property="templateType" column="template_type"/>
<result property="deptId" column="dept_id"/>
<result property="baseOutput" column="base_output"/>
<result property="createBy" column="create_by"/>
@@ -0,0 +1,21 @@
package com.bsd.pjcloud.hard.api.dto.order;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
/**
* 线上订餐统计查询参数
*/
@Data
@Schema(description = "线上订餐统计查询")
public class HardOnlineCanteenStatsQueryDto {
@Schema(description = "支付时间范围")
private LocalDateTime[] payTimeList;
@Schema(description = "商户(食堂)id列表")
private List<Long> mchIds;
}
@@ -0,0 +1,24 @@
package com.bsd.pjcloud.hard.api.vo.order;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 原料用量明细
*/
@Data
@Schema(description = "原料用量明细")
public class HardOnlineCanteenMaterialUsageVo {
@Schema(description = "原料id")
private Long materialId;
@Schema(description = "原料名称")
private String materialName;
@Schema(description = "用量")
private Double usage;
@Schema(description = "单位")
private String unit;
}
@@ -0,0 +1,15 @@
package com.bsd.pjcloud.hard.api.vo.order;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 营养元素汇总(原料营养描述合集)
*/
@Data
@Schema(description = "营养元素汇总")
public class HardOnlineCanteenNutritionVo {
@Schema(description = "营养描述(各原料营养描述的汇总,用户自定义文案)")
private String nutritionDesc;
}
@@ -0,0 +1,32 @@
package com.bsd.pjcloud.hard.api.vo.order;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* 线上订餐统计结果(按食堂汇总)
*/
@Data
@Schema(description = "线上订餐统计-按食堂")
public class HardOnlineCanteenStatsVo {
@Schema(description = "食堂(商户)id")
private Long merchantId;
@Schema(description = "食堂名称")
private String merchantName;
@Schema(description = "订单数")
private Long orderCount = 0L;
@Schema(description = "菜品份数合计")
private Long totalDishCount = 0L;
@Schema(description = "原料用量明细")
private List<HardOnlineCanteenMaterialUsageVo> materialUsages;
@Schema(description = "营养元素汇总")
private HardOnlineCanteenNutritionVo nutritionSummary;
}
@@ -30,6 +30,7 @@ import com.bsd.pjcloud.hard.api.constant.order.HardPayTypeEnum;
import com.bsd.pjcloud.hard.api.dto.app.HardAppCreateOrderScancode;
import com.bsd.pjcloud.hard.api.dto.hard.CurrentUserHardTypeInfoDto;
import com.bsd.pjcloud.hard.api.dto.hard.HardOfflinePayDto;
import com.bsd.pjcloud.hard.api.dto.order.HardOnlineCanteenStatsQueryDto;
import com.bsd.pjcloud.hard.api.dto.order.HardOrderDto;
import com.bsd.pjcloud.hard.api.dto.order.HardOrderPreferencesDto;
import com.bsd.pjcloud.hard.api.dto.order.act.HardCreateActOrderDto;
@@ -39,7 +40,9 @@ import com.bsd.pjcloud.hard.api.entity.order.HardOrderEntity;
import com.bsd.pjcloud.hard.api.vo.order.HardOrderVo;
import com.bsd.pjcloud.hard.api.vo.order.excel.HardOrderExcelVo;
import com.bsd.pjcloud.hard.mapper.order.HardOrderMapper;
import com.bsd.pjcloud.hard.api.vo.order.HardOnlineCanteenStatsVo;
import com.bsd.pjcloud.hard.service.HardManagerService;
import com.bsd.pjcloud.hard.service.HardOnlineCanteenStatsService;
import com.bsd.pjcloud.hard.service.HardOrderService;
import com.bsd.pjcloud.hard.service.HardOrderSyncService;
import com.bsd.pjcloud.wallet.api.constant.WalletTypeEnum;
@@ -78,6 +81,7 @@ public class HardOrderController {
private final HardOrderService hardwareOrderService;
private final HardManagerService hardManagerService;
private final HardOnlineCanteenStatsService hardOnlineCanteenStatsService;
private final HardOrderSyncService hardOrderSyncService;
private final HardOrderMapper hardOrderMapper;
@@ -262,6 +266,27 @@ public class HardOrderController {
return R.ok(hardwareOrderService.page(page, wrapper));
}
@Operation(summary = "线上订餐统计", description = "按食堂统计线上订餐订单的菜品份数仅用 template_type=2 的线上模板,排除退款")
@PostMapping("/online-canteen-stats")
@PreAuthorize("@pms.hasPermission('hard_hardwareOrder_view')")
public R<List<HardOnlineCanteenStatsVo>> onlineCanteenStats(@RequestBody HardOnlineCanteenStatsQueryDto query) {
if (query.getMchIds() == null || query.getMchIds().isEmpty()) {
query.setMchIds(null);
}
List<Long> mchDtoIds = query.getMchIds();
if (ObjectUtil.isNotEmpty(mchDtoIds) && mchDtoIds.size() == 1) {
Long mchId = mchDtoIds.get(0);
List<MchManageVo> mchList = mchService.getByParentId(mchId).getData();
if (ObjectUtil.isNotEmpty(mchList)) {
List<Long> mchIds = mchList.stream().map(MchManageVo::getId).toList();
mchDtoIds = new java.util.ArrayList<>(List.of(mchId));
mchDtoIds.addAll(mchIds);
query.setMchIds(mchDtoIds);
}
}
return R.ok(hardOnlineCanteenStatsService.stats(query));
}
@Operation(summary = "分页复杂查询", description = "分页复杂查询")
@PostMapping("/page-join")
@PreAuthorize("@pms.hasPermission('hard_hardwareOrder_view')")
@@ -0,0 +1,17 @@
package com.bsd.pjcloud.hard.service;
import com.bsd.pjcloud.hard.api.dto.order.HardOnlineCanteenStatsQueryDto;
import com.bsd.pjcloud.hard.api.vo.order.HardOnlineCanteenStatsVo;
import java.util.List;
/**
* 线上订餐统计服务(按食堂汇总:菜品、原料、营养)
*/
public interface HardOnlineCanteenStatsService {
/**
* 统计各食堂线上订餐数据:订单数、菜品份数、原料用量、营养元素(排除退款)
*/
List<HardOnlineCanteenStatsVo> stats(HardOnlineCanteenStatsQueryDto query);
}
@@ -0,0 +1,217 @@
package com.bsd.pjcloud.hard.service.impl.order;
import cn.hutool.core.util.ObjectUtil;
import com.bsd.pjcloud.canteen.api.feign.RemoteCanteenProductionTemplateService;
import com.bsd.pjcloud.canteen.api.feign.RemoteOnlineCanteenSpuService;
import com.bsd.pjcloud.canteen.vo.app.CanteenOnlineSpuAppVO;
import com.bsd.pjcloud.canteen.vo.erp.ProductTemplateVO;
import com.bsd.pjcloud.canteen.vo.erp.ProductionTemplateDetailVO;
import com.bsd.pjcloud.common.core.constant.enums.MerchantTypeEnum;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.data.datascope.DataScope;
import com.bsd.pjcloud.common.data.datascope.DataScopeFuncEnum;
import com.bsd.pjcloud.hard.api.constant.order.HardOrderStatusEnum;
import com.bsd.pjcloud.hard.api.constant.order.HardOrderTypeEnum;
import com.bsd.pjcloud.hard.api.constant.refund.HardRefundStatusEnum;
import com.bsd.pjcloud.hard.api.dto.order.HardOnlineCanteenStatsQueryDto;
import com.bsd.pjcloud.hard.api.dto.order.HardOrderDto;
import com.bsd.pjcloud.hard.api.entity.order.HardCarEntity;
import com.bsd.pjcloud.hard.api.vo.order.*;
import com.bsd.pjcloud.hard.service.HardOrderService;
import com.bsd.pjcloud.hard.service.HardOnlineCanteenStatsService;
import com.bsd.pjcloud.wallet.api.feign.RemoteMchService;
import com.bsd.pjcloud.wallet.api.vo.MchManageVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
/**
* 线上订餐统计实现
* 仅统计线上订餐订单,排除退款,按食堂汇总:菜品份数、原料用量、营养元素
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class HardOnlineCanteenStatsServiceImpl implements HardOnlineCanteenStatsService {
private final HardOrderService hardOrderService;
private final RemoteOnlineCanteenSpuService onlineCanteenSpuService;
private final RemoteCanteenProductionTemplateService productionTemplateService;
private final RemoteMchService mchService;
@Override
public List<HardOnlineCanteenStatsVo> stats(HardOnlineCanteenStatsQueryDto query) {
HardOrderDto dto = buildQueryDto(query);
DataScope dataScope = DataScope.of().func(DataScopeFuncEnum.ALL);
dataScope.setScopeDeptName("merchant_dept_id");
List<HardOrderVo> ordersWithCar = hardOrderService.scopeList(dto, dataScope);
if (ObjectUtil.isEmpty(ordersWithCar)) {
return Collections.emptyList();
}
Set<Long> onlineSpuIds = new HashSet<>();
Map<Long, List<OrderItem>> merchantItems = new HashMap<>();
Map<Long, String> merchantNameMap = new HashMap<>();
for (HardOrderVo o : ordersWithCar) {
if (ObjectUtil.isEmpty(o.getCarList())) continue;
Long mchId = o.getMerchantId();
if (mchId == null) continue;
merchantNameMap.put(mchId, o.getMerchantName());
for (HardCarVo car : o.getCarList()) {
if (car.getProductId() == null || car.getProductCount() == null || car.getProductCount() <= 0) continue;
onlineSpuIds.add(car.getProductId());
merchantItems.computeIfAbsent(mchId, k -> new ArrayList<>())
.add(new OrderItem(car.getProductId(), car.getProductCount()));
}
}
if (onlineSpuIds.isEmpty()) {
return buildResult(merchantNameMap, Collections.emptyMap(), Collections.emptyMap());
}
R<List<CanteenOnlineSpuAppVO>> spuRes = onlineCanteenSpuService.getSpuListByIds(new ArrayList<>(onlineSpuIds));
List<CanteenOnlineSpuAppVO> onlineSpus = (spuRes != null && spuRes.getData() != null) ? spuRes.getData() : Collections.emptyList();
Map<Long, Long> onlineSpuToLinkSpu = onlineSpus.stream()
.filter(s -> s.getLinkSpuId() != null)
.collect(Collectors.toMap(CanteenOnlineSpuAppVO::getId, CanteenOnlineSpuAppVO::getLinkSpuId, (a, b) -> a));
// 模板查找:每日菜品用 linkSpuId,常驻菜品用 onlineSpuId
// 优先查 template_type=2 的线上模板;若无则回退查全部(兼容未设置类型的模板)
Set<Long> dishIdsForTemplate = new HashSet<>(onlineSpuToLinkSpu.values());
onlineSpus.stream().filter(s -> s.getLinkSpuId() == null).forEach(s -> dishIdsForTemplate.add(s.getId()));
Map<Long, ProductTemplateVO> dishIdToTemplate = new HashMap<>();
if (!dishIdsForTemplate.isEmpty()) {
List<ProductTemplateVO> templates = fetchTemplatesWithFallback(new ArrayList<>(dishIdsForTemplate));
for (ProductTemplateVO t : templates) {
if (t.getDishId() != null) {
dishIdToTemplate.put(t.getDishId(), t);
}
}
}
Map<Long, Map<Long, MaterialAccumulator>> merchantMaterialMap = new HashMap<>();
Map<Long, NutritionAccumulator> merchantNutritionMap = new HashMap<>();
Map<Long, long[]> merchantCounts = new HashMap<>();
for (Map.Entry<Long, List<OrderItem>> e : merchantItems.entrySet()) {
Long mchId = e.getKey();
merchantMaterialMap.put(mchId, new HashMap<>());
merchantNutritionMap.put(mchId, new NutritionAccumulator());
long orderCount = ordersWithCar.stream().filter(o -> Objects.equals(o.getMerchantId(), mchId)).count();
long dishCount = 0;
for (OrderItem item : e.getValue()) {
Long templateKey = onlineSpuToLinkSpu.getOrDefault(item.productId, item.productId);
ProductTemplateVO template = dishIdToTemplate.get(templateKey);
if (template == null || ObjectUtil.isEmpty(template.getCanteenProductionTemplateDetailList())) continue;
double baseOutput = ObjectUtil.defaultIfNull(template.getBaseOutput(), 1.0);
if (baseOutput <= 0) baseOutput = 1.0;
double ratio = item.count / baseOutput;
dishCount += item.count;
for (ProductionTemplateDetailVO d : template.getCanteenProductionTemplateDetailList()) {
double baseUsage = ObjectUtil.defaultIfNull(d.getBaseUsage(), 0.0);
double lossRate = ObjectUtil.defaultIfNull(d.getLossRate(), 0.0);
double usage = ratio * baseUsage * (1 + lossRate);
merchantMaterialMap.get(mchId).computeIfAbsent(d.getMaterialId(), k -> new MaterialAccumulator(d.getMaterialId(), d.getMaterialName(), d.getUnit()))
.add(usage);
if (d.getNutritionDesc() != null && !d.getNutritionDesc().isBlank()) {
merchantNutritionMap.get(mchId).addDesc(d.getMaterialName(), d.getNutritionDesc());
}
}
}
merchantCounts.put(mchId, new long[]{orderCount, dishCount});
}
return buildResult(merchantNameMap, merchantMaterialMap, merchantNutritionMap, merchantCounts);
}
/** 优先查线上模板(type=2),无则回退查全部,兼容未设置 templateType 的模板 */
private List<ProductTemplateVO> fetchTemplatesWithFallback(List<Long> dishIds) {
R<List<ProductTemplateVO>> res = productionTemplateService.getDetailsByDishIdsForOnline(dishIds);
List<ProductTemplateVO> list = (res != null && res.getData() != null) ? res.getData() : Collections.emptyList();
if (!list.isEmpty()) return list;
R<List<ProductTemplateVO>> fallback = productionTemplateService.getDetailsByDishIds(dishIds);
return (fallback != null && fallback.getData() != null) ? fallback.getData() : Collections.emptyList();
}
private HardOrderDto buildQueryDto(HardOnlineCanteenStatsQueryDto query) {
HardOrderDto dto = new HardOrderDto();
dto.setOrderTypeCodes(List.of(HardOrderTypeEnum.RESERVE.getType()));
dto.setMchTypeCodes(List.of(MerchantTypeEnum.CANTEEN.getType()));
dto.setOrderStatusCodes(HardOrderStatusEnum.getPaySuccessCodes());
dto.setRefundStatusCodes(List.of(HardRefundStatusEnum.NONE.getStatus(), HardRefundStatusEnum.APPLY.getStatus(), HardRefundStatusEnum.REJECT.getStatus()));
dto.setPayTimeList(query.getPayTimeList());
dto.setMchIds(query.getMchIds());
dto.setIsFindCarList(true);
return dto;
}
private List<HardOnlineCanteenStatsVo> buildResult(Map<Long, String> merchantNameMap,
Map<Long, Map<Long, MaterialAccumulator>> materialMap,
Map<Long, NutritionAccumulator> nutritionMap) {
return buildResult(merchantNameMap, materialMap, nutritionMap, new HashMap<>());
}
private List<HardOnlineCanteenStatsVo> buildResult(Map<Long, String> merchantNameMap,
Map<Long, Map<Long, MaterialAccumulator>> materialMap,
Map<Long, NutritionAccumulator> nutritionMap,
Map<Long, long[]> countsMap) {
Set<Long> mchIds = new HashSet<>(merchantNameMap.keySet());
mchIds.addAll(materialMap.keySet());
mchIds.addAll(nutritionMap.keySet());
mchIds.addAll(countsMap.keySet());
List<HardOnlineCanteenStatsVo> result = new ArrayList<>();
for (Long mchId : mchIds) {
HardOnlineCanteenStatsVo vo = new HardOnlineCanteenStatsVo();
vo.setMerchantId(mchId);
vo.setMerchantName(merchantNameMap.getOrDefault(mchId, ""));
long[] c = countsMap.getOrDefault(mchId, new long[]{0, 0});
vo.setOrderCount(c[0]);
vo.setTotalDishCount(c[1]);
Map<Long, MaterialAccumulator> mat = materialMap.getOrDefault(mchId, Collections.emptyMap());
vo.setMaterialUsages(mat.values().stream().map(MaterialAccumulator::toVo).collect(Collectors.toList()));
vo.setNutritionSummary(nutritionMap.getOrDefault(mchId, new NutritionAccumulator()).toVo());
result.add(vo);
}
result.sort(Comparator.comparing(HardOnlineCanteenStatsVo::getTotalDishCount).reversed());
return result;
}
private record OrderItem(Long productId, int count) {}
private static class MaterialAccumulator {
Long materialId;
String materialName;
String unit;
double usage;
MaterialAccumulator(Long materialId, String materialName, String unit) {
this.materialId = materialId;
this.materialName = materialName;
this.unit = unit;
}
void add(double u) { usage += u; }
HardOnlineCanteenMaterialUsageVo toVo() {
HardOnlineCanteenMaterialUsageVo vo = new HardOnlineCanteenMaterialUsageVo();
vo.setMaterialId(materialId);
vo.setMaterialName(materialName);
vo.setUnit(unit);
vo.setUsage(usage);
return vo;
}
}
private static class NutritionAccumulator {
private final Set<String> descSet = new LinkedHashSet<>();
void addDesc(String materialName, String nutritionDesc) {
descSet.add(materialName + ":" + nutritionDesc);
}
HardOnlineCanteenNutritionVo toVo() {
HardOnlineCanteenNutritionVo vo = new HardOnlineCanteenNutritionVo();
vo.setNutritionDesc(String.join(";", descSet));
return vo;
}
}
}
@@ -97,6 +97,12 @@
<if test="dto.orderType != null and dto.orderType != '' ">
and a.order_type = #{dto.orderType}
</if>
<if test="dto.orderTypeCodes != null and dto.orderTypeCodes.size() != 0">
and a.order_type in
<foreach collection="dto.orderTypeCodes" item="v" open="(" close=")" separator=",">
#{v}
</foreach>
</if>
<if test="dto.hourType != null and dto.hourType != '' ">
and a.hour_type = #{dto.hourType}
</if>
@@ -195,6 +201,13 @@
<if test="dto.payTimeList != null and dto.payTimeList.length == 2">
and a.pay_time between #{dto.payTimeList[0]} and #{dto.payTimeList[1]}
</if>
<if test="dto.refundStatusCodes != null and dto.refundStatusCodes.size() != 0">
and (a.refund_status in
<foreach collection="dto.refundStatusCodes" item="v" open="(" close=")" separator=",">
#{v}
</foreach>
or a.refund_status is null)
</if>
</sql>
<sql id="pageList">