From 91a2facb83f7a2165b71c4e57c062cc6904655f0 Mon Sep 17 00:00:00 2001 From: zjf <1799650030@qq.com> Date: Fri, 3 Jul 2026 10:19:57 +0800 Subject: [PATCH] =?UTF-8?q?=E8=8F=9C=E8=B0=B1=E6=8E=92=E9=A4=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/mealplan/CanteenMealPlanItemDTO.java | 23 ++ .../dto/mealplan/CanteenMealPlanSaveDTO.java | 42 +++ .../menurecipe/CanteenMenuRecipeMatchDTO.java | 17 + .../entity/CanteenErpMaterialEntity.java | 8 + .../canteen/entity/CanteenMealPlanEntity.java | 74 ++++ .../entity/CanteenMealPlanItemEntity.java | 37 ++ .../entity/CanteenMenuRecipeEntity.java | 76 ++++ .../CanteenMenuRecipeMaterialEntity.java | 40 ++ .../vo/mealplan/CanteenMealPlanExportRow.java | 17 + .../CanteenMealPlanMaterialBriefVO.java | 15 + .../vo/mealplan/CanteenMealPlanVO.java | 47 +++ .../CanteenMenuRecipeMaterialVO.java | 23 ++ .../vo/menurecipe/CanteenMenuRecipeVO.java | 36 ++ .../controller/CanteenMealPlanController.java | 94 +++++ .../CanteenMenuRecipeController.java | 84 +++++ .../mapper/CanteenMealPlanItemMapper.java | 9 + .../canteen/mapper/CanteenMealPlanMapper.java | 9 + .../mapper/CanteenMenuRecipeMapper.java | 9 + .../CanteenMenuRecipeMaterialMapper.java | 9 + .../service/CanteenMealPlanService.java | 29 ++ .../service/CanteenMenuRecipeService.java | 25 ++ .../impl/CanteenMealPlanServiceImpl.java | 346 ++++++++++++++++++ .../impl/CanteenMenuRecipeServiceImpl.java | 219 +++++++++++ .../mapper/CanteenErpMaterialMapper.xml | 6 + 24 files changed, 1294 insertions(+) create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/mealplan/CanteenMealPlanItemDTO.java create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/mealplan/CanteenMealPlanSaveDTO.java create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/menurecipe/CanteenMenuRecipeMatchDTO.java create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenMealPlanEntity.java create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenMealPlanItemEntity.java create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenMenuRecipeEntity.java create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenMenuRecipeMaterialEntity.java create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/mealplan/CanteenMealPlanExportRow.java create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/mealplan/CanteenMealPlanMaterialBriefVO.java create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/mealplan/CanteenMealPlanVO.java create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/menurecipe/CanteenMenuRecipeMaterialVO.java create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/menurecipe/CanteenMenuRecipeVO.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenMealPlanController.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenMenuRecipeController.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenMealPlanItemMapper.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenMealPlanMapper.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenMenuRecipeMapper.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenMenuRecipeMaterialMapper.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenMealPlanService.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenMenuRecipeService.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenMealPlanServiceImpl.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenMenuRecipeServiceImpl.java diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/mealplan/CanteenMealPlanItemDTO.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/mealplan/CanteenMealPlanItemDTO.java new file mode 100644 index 000000000..23757bafc --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/mealplan/CanteenMealPlanItemDTO.java @@ -0,0 +1,23 @@ +package com.bsd.pjcloud.canteen.dto.mealplan; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDate; + +@Data +@Schema(description = "排餐计划明细项") +public class CanteenMealPlanItemDTO { + + private Long id; + + private LocalDate planDate; + + private Integer mealSegment; + + private Long recipeId; + + private String recipeName; + + private Integer sortOrder; +} diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/mealplan/CanteenMealPlanSaveDTO.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/mealplan/CanteenMealPlanSaveDTO.java new file mode 100644 index 000000000..609ce38cf --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/mealplan/CanteenMealPlanSaveDTO.java @@ -0,0 +1,42 @@ +package com.bsd.pjcloud.canteen.dto.mealplan; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.time.LocalDate; +import java.util.List; + +@Data +@Schema(description = "保存排餐计划") +public class CanteenMealPlanSaveDTO { + + private Long id; + + @NotBlank(message = "请选择商户") + private String merchantId; + + @NotBlank(message = "请输入计划名称") + private String planName; + + @NotNull(message = "请选择开始日期") + private LocalDate startDate; + + @NotNull(message = "请填写排餐天数") + private Integer dayCount; + + @NotEmpty(message = "请勾选餐次") + private List mealSegments; + + private List selectedMaterialIds; + + private Integer matchMode; + + private Integer status; + + private String remark; + + private List items; +} diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/menurecipe/CanteenMenuRecipeMatchDTO.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/menurecipe/CanteenMenuRecipeMatchDTO.java new file mode 100644 index 000000000..074f82108 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/menurecipe/CanteenMenuRecipeMatchDTO.java @@ -0,0 +1,17 @@ +package com.bsd.pjcloud.canteen.dto.menurecipe; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +@Schema(description = "按选用食材匹配菜谱") +public class CanteenMenuRecipeMatchDTO { + + @Schema(description = "已选食材ID") + private List materialIds; + + @Schema(description = "可选:限定商户") + private String merchantId; +} diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenErpMaterialEntity.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenErpMaterialEntity.java index 2d962ab43..4f62fdda0 100644 --- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenErpMaterialEntity.java +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenErpMaterialEntity.java @@ -113,4 +113,12 @@ public class CanteenErpMaterialEntity extends Model { @TableField(typeHandler = JacksonTypeHandler.class) private List nutritionTags; + @TableField(exist = false) + @Schema(description = "筛选:营养元素名称") + private String nutritionName; + + @TableField(exist = false) + @Schema(description = "筛选:营养功效") + private String nutritionEffect; + } \ No newline at end of file diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenMealPlanEntity.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenMealPlanEntity.java new file mode 100644 index 000000000..c43ad07e2 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenMealPlanEntity.java @@ -0,0 +1,74 @@ +package com.bsd.pjcloud.canteen.entity; + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.baomidou.mybatisplus.annotation.*; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.bsd.pjcloud.common.core.util.TenantTable; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +@Data +@TenantTable +@TableName(value = "canteen_meal_plan", autoResultMap = true) +@EqualsAndHashCode(callSuper = true) +@Schema(description = "排餐计划") +public class CanteenMealPlanEntity extends Model { + + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + private Long tenantId; + + private Long deptId; + + private String merchantId; + + private String planName; + + private LocalDate startDate; + + private Integer dayCount; + + @TableField(value = "meal_segments_json", typeHandler = JacksonTypeHandler.class) + private List mealSegments; + + @TableField(value = "selected_material_ids_json", typeHandler = JacksonTypeHandler.class) + private List selectedMaterialIds; + + private Integer matchMode; + + @Schema(description = "0草稿 1已定稿") + private Integer status; + + private String remark; + + @TableField(fill = FieldFill.INSERT) + private String createBy; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + + @TableLogic + @TableField(fill = FieldFill.INSERT) + private String delFlag; + + @ExcelIgnore + @TableField(exist = false) + private String merchantName; + + @ExcelIgnore + @TableField(exist = false) + private List itemList; +} diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenMealPlanItemEntity.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenMealPlanItemEntity.java new file mode 100644 index 000000000..55f2f7514 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenMealPlanItemEntity.java @@ -0,0 +1,37 @@ +package com.bsd.pjcloud.canteen.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +@Data +@TableName("canteen_meal_plan_item") +@EqualsAndHashCode(callSuper = true) +@Schema(description = "排餐计划明细") +public class CanteenMealPlanItemEntity extends Model { + + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + private Long planId; + + private LocalDate planDate; + + @Schema(description = "1早2午3晚4夜") + private Integer mealSegment; + + private Long recipeId; + + private String recipeName; + + private Integer sortOrder; + + private LocalDateTime createTime; +} diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenMenuRecipeEntity.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenMenuRecipeEntity.java new file mode 100644 index 000000000..5acb6787f --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenMenuRecipeEntity.java @@ -0,0 +1,76 @@ +package com.bsd.pjcloud.canteen.entity; + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.baomidou.mybatisplus.annotation.*; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import com.bsd.pjcloud.common.core.util.TenantTable; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +@TenantTable +@TableName("canteen_menu_recipe") +@EqualsAndHashCode(callSuper = true) +@Schema(description = "排菜菜谱") +public class CanteenMenuRecipeEntity extends Model { + + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + private Long tenantId; + + @Schema(description = "所属部门") + private Long deptId; + + @Schema(description = "关联商户") + private String merchantId; + + @Schema(description = "菜名") + private String name; + + @Schema(description = "图片") + private String picUrl; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "可选关联线上商品") + private Long onlineSpuId; + + @Schema(description = "0正常 1停用") + private Integer status; + + @TableField(fill = FieldFill.INSERT) + private String createBy; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + + @TableLogic + @TableField(fill = FieldFill.INSERT) + private String delFlag; + + @ExcelIgnore + @TableField(exist = false) + private List materialList; + + @ExcelIgnore + @TableField(exist = false) + @Schema(description = "食材摘要(列表展示)") + private String materialSummary; + + @ExcelIgnore + @TableField(exist = false) + @Schema(description = "关联线上商品名称") + private String onlineSpuName; +} diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenMenuRecipeMaterialEntity.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenMenuRecipeMaterialEntity.java new file mode 100644 index 000000000..bd6bc1dc6 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenMenuRecipeMaterialEntity.java @@ -0,0 +1,40 @@ +package com.bsd.pjcloud.canteen.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +@TableName("canteen_menu_recipe_material") +@EqualsAndHashCode(callSuper = true) +@Schema(description = "排菜菜谱-食材明细") +public class CanteenMenuRecipeMaterialEntity extends Model { + + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + private Long recipeId; + + private Long materialId; + + @Schema(description = "参考用量") + private BigDecimal quantity; + + private Integer sortOrder; + + private LocalDateTime createTime; + + @TableField(exist = false) + private String materialName; + + @TableField(exist = false) + private String unit; +} diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/mealplan/CanteenMealPlanExportRow.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/mealplan/CanteenMealPlanExportRow.java new file mode 100644 index 000000000..786937105 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/mealplan/CanteenMealPlanExportRow.java @@ -0,0 +1,17 @@ +package com.bsd.pjcloud.canteen.vo.mealplan; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +@Data +public class CanteenMealPlanExportRow { + + @ExcelProperty("日期") + private String planDate; + + @ExcelProperty("餐次") + private String mealSegmentName; + + @ExcelProperty("菜名") + private String recipeName; +} diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/mealplan/CanteenMealPlanMaterialBriefVO.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/mealplan/CanteenMealPlanMaterialBriefVO.java new file mode 100644 index 000000000..c6c2dd47b --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/mealplan/CanteenMealPlanMaterialBriefVO.java @@ -0,0 +1,15 @@ +package com.bsd.pjcloud.canteen.vo.mealplan; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(description = "排餐选用食材摘要") +public class CanteenMealPlanMaterialBriefVO { + + private Long id; + + private String name; + + private String unit; +} diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/mealplan/CanteenMealPlanVO.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/mealplan/CanteenMealPlanVO.java new file mode 100644 index 000000000..a8f118a5a --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/mealplan/CanteenMealPlanVO.java @@ -0,0 +1,47 @@ +package com.bsd.pjcloud.canteen.vo.mealplan; + +import com.bsd.pjcloud.canteen.dto.mealplan.CanteenMealPlanItemDTO; +import com.bsd.pjcloud.canteen.vo.menurecipe.CanteenMenuRecipeVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +@Data +@Schema(description = "排餐计划详情") +public class CanteenMealPlanVO { + + private Long id; + + private String merchantId; + + private String merchantName; + + private String planName; + + private LocalDate startDate; + + private Integer dayCount; + + private List mealSegments; + + private List selectedMaterialIds; + + private Integer matchMode; + + private Integer status; + + private String remark; + + private LocalDateTime createTime; + + private List planDates; + + private List items; + + private List selectedMaterials; + + private List matchedRecipes; +} diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/menurecipe/CanteenMenuRecipeMaterialVO.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/menurecipe/CanteenMenuRecipeMaterialVO.java new file mode 100644 index 000000000..6fda26005 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/menurecipe/CanteenMenuRecipeMaterialVO.java @@ -0,0 +1,23 @@ +package com.bsd.pjcloud.canteen.vo.menurecipe; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@Schema(description = "排菜菜谱食材明细") +public class CanteenMenuRecipeMaterialVO { + + private Long id; + + private Long materialId; + + private String materialName; + + private String unit; + + private BigDecimal quantity; + + private Integer sortOrder; +} diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/menurecipe/CanteenMenuRecipeVO.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/menurecipe/CanteenMenuRecipeVO.java new file mode 100644 index 000000000..979d20c96 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/menurecipe/CanteenMenuRecipeVO.java @@ -0,0 +1,36 @@ +package com.bsd.pjcloud.canteen.vo.menurecipe; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +@Schema(description = "排菜菜谱详情") +public class CanteenMenuRecipeVO { + + private Long id; + + private Long deptId; + + private String merchantId; + + private String name; + + private String picUrl; + + private String remark; + + private Long onlineSpuId; + + private String onlineSpuName; + + private Integer status; + + private LocalDateTime createTime; + + private String materialSummary; + + private List materialList; +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenMealPlanController.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenMealPlanController.java new file mode 100644 index 000000000..3461710f8 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenMealPlanController.java @@ -0,0 +1,94 @@ +package com.bsd.pjcloud.canteen.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.canteen.dto.mealplan.CanteenMealPlanSaveDTO; +import com.bsd.pjcloud.canteen.entity.CanteenMealPlanEntity; +import com.bsd.pjcloud.canteen.service.CanteenMealPlanService; +import com.bsd.pjcloud.canteen.vo.mealplan.CanteenMealPlanExportRow; +import com.bsd.pjcloud.common.core.util.R; +import com.bsd.pjcloud.common.excel.annotation.ResponseExcel; +import com.bsd.pjcloud.common.log.annotation.SysLog; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.http.HttpHeaders; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/canteenMealPlan") +@Tag(name = "一键排餐") +@SecurityRequirement(name = HttpHeaders.AUTHORIZATION) +public class CanteenMealPlanController { + + private final CanteenMealPlanService canteenMealPlanService; + + @Operation(summary = "分页查询") + @GetMapping("/page") + @PreAuthorize("@pms.hasPermission('canteen_canteenMealPlan_view')") + public R> page(@ParameterObject Page page, + @ParameterObject CanteenMealPlanEntity query) { + var wrapper = Wrappers.lambdaQuery() + .like(StrUtil.isNotBlank(query.getPlanName()), CanteenMealPlanEntity::getPlanName, query.getPlanName()) + .eq(StrUtil.isNotBlank(query.getMerchantId()), CanteenMealPlanEntity::getMerchantId, + query.getMerchantId()) + .eq(query.getStatus() != null, CanteenMealPlanEntity::getStatus, query.getStatus()) + .orderByDesc(CanteenMealPlanEntity::getCreateTime); + return R.ok(canteenMealPlanService.pageScope(page, wrapper)); + } + + @Operation(summary = "详情") + @GetMapping("/{id}") + @PreAuthorize("@pms.hasPermission('canteen_canteenMealPlan_view')") + public R getById(@PathVariable Long id) { + return R.ok(canteenMealPlanService.getDetail(id)); + } + + @SysLog("新增排餐计划") + @Operation(summary = "新增") + @PostMapping + @PreAuthorize("@pms.hasPermission('canteen_canteenMealPlan_add')") + public R save(@Valid @RequestBody CanteenMealPlanSaveDTO dto) { + return R.ok(canteenMealPlanService.savePlan(dto)); + } + + @SysLog("修改排餐计划") + @Operation(summary = "修改") + @PutMapping + @PreAuthorize("@pms.hasPermission('canteen_canteenMealPlan_edit')") + public R update(@Valid @RequestBody CanteenMealPlanSaveDTO dto) { + return R.ok(canteenMealPlanService.updatePlan(dto)); + } + + @SysLog("删除排餐计划") + @Operation(summary = "删除") + @DeleteMapping + @PreAuthorize("@pms.hasPermission('canteen_canteenMealPlan_del')") + public R remove(@RequestBody Long[] ids) { + return R.ok(canteenMealPlanService.removePlans(ids)); + } + + @ResponseExcel(name = "排餐明细") + @Operation(summary = "导出明细(日期+餐次+菜名)") + @GetMapping("/export/{id}") + @PreAuthorize("@pms.hasPermission('canteen_canteenMealPlan_export')") + public List exportDetail(@PathVariable Long id) { + return canteenMealPlanService.exportRows(id); + } + + @Operation(summary = "导出餐表(横轴日期、纵轴餐次)") + @GetMapping("/export-grid/{id}") + @PreAuthorize("@pms.hasPermission('canteen_canteenMealPlan_export')") + public void exportGrid(@PathVariable Long id, HttpServletResponse response) { + canteenMealPlanService.exportGridExcel(id, response); + } +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenMenuRecipeController.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenMenuRecipeController.java new file mode 100644 index 000000000..4ef33e904 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenMenuRecipeController.java @@ -0,0 +1,84 @@ +package com.bsd.pjcloud.canteen.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.canteen.dto.menurecipe.CanteenMenuRecipeMatchDTO; +import com.bsd.pjcloud.canteen.entity.CanteenMenuRecipeEntity; +import com.bsd.pjcloud.canteen.service.CanteenMenuRecipeService; +import com.bsd.pjcloud.common.core.util.R; +import com.bsd.pjcloud.common.log.annotation.SysLog; +import com.bsd.pjcloud.common.security.util.SecurityUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.http.HttpHeaders; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/canteenMenuRecipe") +@Tag(name = "排菜菜谱") +@SecurityRequirement(name = HttpHeaders.AUTHORIZATION) +public class CanteenMenuRecipeController { + + private final CanteenMenuRecipeService canteenMenuRecipeService; + + @Operation(summary = "分页查询") + @GetMapping("/page") + @PreAuthorize("@pms.hasPermission('canteen_canteenMenuRecipe_view')") + public R> page(@ParameterObject Page page, + @ParameterObject CanteenMenuRecipeEntity query) { + var wrapper = Wrappers.lambdaQuery() + .like(StrUtil.isNotBlank(query.getName()), CanteenMenuRecipeEntity::getName, query.getName()) + .eq(StrUtil.isNotBlank(query.getMerchantId()), CanteenMenuRecipeEntity::getMerchantId, + query.getMerchantId()) + .eq(query.getStatus() != null, CanteenMenuRecipeEntity::getStatus, query.getStatus()) + .orderByDesc(CanteenMenuRecipeEntity::getCreateTime); + return R.ok(canteenMenuRecipeService.pageScope(page, wrapper)); + } + + @Operation(summary = "详情") + @GetMapping("/{id}") + @PreAuthorize("@pms.hasPermission('canteen_canteenMenuRecipe_view')") + public R getById(@PathVariable Long id) { + return R.ok(canteenMenuRecipeService.getDetail(id)); + } + + @SysLog("新增排菜菜谱") + @Operation(summary = "新增") + @PostMapping + @PreAuthorize("@pms.hasPermission('canteen_canteenMenuRecipe_add')") + public R save(@RequestBody CanteenMenuRecipeEntity entity) { + entity.setDeptId(SecurityUtils.getUser().getDeptId()); + return R.ok(canteenMenuRecipeService.saveDeep(entity)); + } + + @SysLog("修改排菜菜谱") + @Operation(summary = "修改") + @PutMapping + @PreAuthorize("@pms.hasPermission('canteen_canteenMenuRecipe_edit')") + public R update(@RequestBody CanteenMenuRecipeEntity entity) { + return R.ok(canteenMenuRecipeService.updateDeep(entity)); + } + + @SysLog("删除排菜菜谱") + @Operation(summary = "删除") + @DeleteMapping + @PreAuthorize("@pms.hasPermission('canteen_canteenMenuRecipe_del')") + public R remove(@RequestBody Long[] ids) { + return R.ok(canteenMenuRecipeService.removeDeep(ids)); + } + + @Operation(summary = "按选用食材匹配可做菜谱", description = "所选食材须覆盖菜谱全部食材") + @PostMapping("/match-by-materials") + @PreAuthorize("@pms.hasPermission('canteen_canteenMenuRecipe_view')") + public R> matchByMaterials(@RequestBody CanteenMenuRecipeMatchDTO dto) { + return R.ok(canteenMenuRecipeService.matchByMaterials(dto)); + } +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenMealPlanItemMapper.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenMealPlanItemMapper.java new file mode 100644 index 000000000..39d933234 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenMealPlanItemMapper.java @@ -0,0 +1,9 @@ +package com.bsd.pjcloud.canteen.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.bsd.pjcloud.canteen.entity.CanteenMealPlanItemEntity; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface CanteenMealPlanItemMapper extends BaseMapper { +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenMealPlanMapper.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenMealPlanMapper.java new file mode 100644 index 000000000..cdaf302f8 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenMealPlanMapper.java @@ -0,0 +1,9 @@ +package com.bsd.pjcloud.canteen.mapper; + +import com.bsd.pjcloud.canteen.entity.CanteenMealPlanEntity; +import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface CanteenMealPlanMapper extends PjcloudBaseMapper { +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenMenuRecipeMapper.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenMenuRecipeMapper.java new file mode 100644 index 000000000..4c8b23e48 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenMenuRecipeMapper.java @@ -0,0 +1,9 @@ +package com.bsd.pjcloud.canteen.mapper; + +import com.bsd.pjcloud.canteen.entity.CanteenMenuRecipeEntity; +import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface CanteenMenuRecipeMapper extends PjcloudBaseMapper { +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenMenuRecipeMaterialMapper.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenMenuRecipeMaterialMapper.java new file mode 100644 index 000000000..db3f92b0e --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenMenuRecipeMaterialMapper.java @@ -0,0 +1,9 @@ +package com.bsd.pjcloud.canteen.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.bsd.pjcloud.canteen.entity.CanteenMenuRecipeMaterialEntity; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface CanteenMenuRecipeMaterialMapper extends BaseMapper { +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenMealPlanService.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenMealPlanService.java new file mode 100644 index 000000000..77a21de82 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenMealPlanService.java @@ -0,0 +1,29 @@ +package com.bsd.pjcloud.canteen.service; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.bsd.pjcloud.canteen.dto.mealplan.CanteenMealPlanSaveDTO; +import com.bsd.pjcloud.canteen.entity.CanteenMealPlanEntity; +import com.bsd.pjcloud.canteen.vo.mealplan.CanteenMealPlanExportRow; +import com.bsd.pjcloud.canteen.vo.mealplan.CanteenMealPlanVO; +import jakarta.servlet.http.HttpServletResponse; + +import java.util.List; + +public interface CanteenMealPlanService extends IService { + + Page pageScope(Page page, LambdaQueryWrapper wrapper); + + Long savePlan(CanteenMealPlanSaveDTO dto); + + Boolean updatePlan(CanteenMealPlanSaveDTO dto); + + Boolean removePlans(Long[] ids); + + CanteenMealPlanVO getDetail(Long id); + + List exportRows(Long id); + + void exportGridExcel(Long id, HttpServletResponse response); +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenMenuRecipeService.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenMenuRecipeService.java new file mode 100644 index 000000000..919315ce1 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenMenuRecipeService.java @@ -0,0 +1,25 @@ +package com.bsd.pjcloud.canteen.service; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.bsd.pjcloud.canteen.dto.menurecipe.CanteenMenuRecipeMatchDTO; +import com.bsd.pjcloud.canteen.entity.CanteenMenuRecipeEntity; +import com.bsd.pjcloud.canteen.vo.menurecipe.CanteenMenuRecipeVO; + +import java.util.List; + +public interface CanteenMenuRecipeService extends IService { + + Page pageScope(Page page, LambdaQueryWrapper wrapper); + + Boolean saveDeep(CanteenMenuRecipeEntity entity); + + Boolean updateDeep(CanteenMenuRecipeEntity entity); + + Boolean removeDeep(Long[] ids); + + CanteenMenuRecipeVO getDetail(Long id); + + List matchByMaterials(CanteenMenuRecipeMatchDTO dto); +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenMealPlanServiceImpl.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenMealPlanServiceImpl.java new file mode 100644 index 000000000..c6b5becbf --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenMealPlanServiceImpl.java @@ -0,0 +1,346 @@ +package com.bsd.pjcloud.canteen.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.excel.EasyExcel; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bsd.pjcloud.canteen.dto.mealplan.CanteenMealPlanItemDTO; +import com.bsd.pjcloud.canteen.dto.mealplan.CanteenMealPlanSaveDTO; +import com.bsd.pjcloud.canteen.dto.menurecipe.CanteenMenuRecipeMatchDTO; +import com.bsd.pjcloud.canteen.entity.CanteenErpMaterialEntity; +import com.bsd.pjcloud.canteen.entity.CanteenMealPlanEntity; +import com.bsd.pjcloud.canteen.entity.CanteenMealPlanItemEntity; +import com.bsd.pjcloud.canteen.entity.CanteenMenuRecipeEntity; +import com.bsd.pjcloud.canteen.mapper.CanteenErpMaterialMapper; +import com.bsd.pjcloud.canteen.mapper.CanteenMealPlanItemMapper; +import com.bsd.pjcloud.canteen.mapper.CanteenMealPlanMapper; +import com.bsd.pjcloud.canteen.mapper.CanteenMenuRecipeMapper; +import com.bsd.pjcloud.canteen.service.CanteenMealPlanService; +import com.bsd.pjcloud.canteen.service.CanteenMenuRecipeService; +import com.bsd.pjcloud.canteen.vo.mealplan.CanteenMealPlanExportRow; +import com.bsd.pjcloud.canteen.vo.mealplan.CanteenMealPlanMaterialBriefVO; +import com.bsd.pjcloud.canteen.vo.mealplan.CanteenMealPlanVO; +import com.bsd.pjcloud.canteen.vo.menurecipe.CanteenMenuRecipeVO; +import com.bsd.pjcloud.common.core.util.BeanUtils; +import com.bsd.pjcloud.common.core.util.R; +import com.bsd.pjcloud.common.data.datascope.DataScope; +import com.bsd.pjcloud.common.security.util.SecurityUtils; +import com.bsd.pjcloud.wallet.api.feign.RemoteMchService; +import com.bsd.pjcloud.wallet.api.vo.MchManageVo; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +@Service +@RequiredArgsConstructor +public class CanteenMealPlanServiceImpl extends ServiceImpl + implements CanteenMealPlanService { + + private static final Map SEGMENT_NAMES = Map.of( + 1, "早餐", + 2, "午餐", + 3, "晚餐", + 4, "夜宵"); + + private static final String[] WEEKDAY_LABELS = { "日", "一", "二", "三", "四", "五", "六" }; + + private final CanteenMealPlanItemMapper itemMapper; + private final CanteenErpMaterialMapper erpMaterialMapper; + private final CanteenMenuRecipeService menuRecipeService; + private final CanteenMenuRecipeMapper menuRecipeMapper; + private final RemoteMchService remoteMchService; + + @Override + public Page pageScope(Page page, + LambdaQueryWrapper wrapper) { + Page result = baseMapper.selectPageByScope(page, wrapper, DataScope.of()); + fillMerchantNames(result.getRecords()); + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long savePlan(CanteenMealPlanSaveDTO dto) { + CanteenMealPlanEntity entity = toEntity(dto); + entity.setDeptId(SecurityUtils.getUser().getDeptId()); + if (entity.getMatchMode() == null) { + entity.setMatchMode(1); + } + if (entity.getStatus() == null) { + entity.setStatus(0); + } + baseMapper.insert(entity); + saveItems(entity.getId(), dto.getItems()); + return entity.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updatePlan(CanteenMealPlanSaveDTO dto) { + CanteenMealPlanEntity entity = toEntity(dto); + baseMapper.updateById(entity); + itemMapper.delete(Wrappers.lambdaQuery() + .eq(CanteenMealPlanItemEntity::getPlanId, dto.getId())); + saveItems(dto.getId(), dto.getItems()); + return Boolean.TRUE; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean removePlans(Long[] ids) { + if (ids == null || ids.length == 0) { + return Boolean.TRUE; + } + baseMapper.deleteBatchIds(CollUtil.toList(ids)); + itemMapper.delete(Wrappers.lambdaQuery() + .in(CanteenMealPlanItemEntity::getPlanId, CollUtil.toList(ids))); + return Boolean.TRUE; + } + + @Override + public CanteenMealPlanVO getDetail(Long id) { + CanteenMealPlanEntity entity = baseMapper.selectById(id); + if (entity == null) { + return null; + } + CanteenMealPlanVO vo = BeanUtils.toBean(entity, CanteenMealPlanVO.class); + vo.setMerchantName(resolveMerchantName(entity.getMerchantId())); + vo.setPlanDates(buildPlanDates(entity.getStartDate(), entity.getDayCount())); + List items = itemMapper.selectList( + Wrappers.lambdaQuery() + .eq(CanteenMealPlanItemEntity::getPlanId, id) + .orderByAsc(CanteenMealPlanItemEntity::getPlanDate) + .orderByAsc(CanteenMealPlanItemEntity::getMealSegment) + .orderByAsc(CanteenMealPlanItemEntity::getSortOrder)); + vo.setItems(BeanUtils.toBean(items, CanteenMealPlanItemDTO.class)); + vo.setSelectedMaterials(loadMaterialBriefs(entity.getSelectedMaterialIds())); + if (CollUtil.isNotEmpty(entity.getSelectedMaterialIds())) { + CanteenMenuRecipeMatchDTO matchDTO = new CanteenMenuRecipeMatchDTO(); + matchDTO.setMaterialIds(entity.getSelectedMaterialIds()); + matchDTO.setMerchantId(entity.getMerchantId()); + vo.setMatchedRecipes(menuRecipeService.matchByMaterials(matchDTO)); + } else { + vo.setMatchedRecipes(Collections.emptyList()); + } + return vo; + } + + @Override + public List exportRows(Long id) { + CanteenMealPlanVO detail = getDetail(id); + if (detail == null || CollUtil.isEmpty(detail.getItems())) { + return Collections.emptyList(); + } + List rows = new ArrayList<>(); + for (CanteenMealPlanItemDTO item : detail.getItems()) { + CanteenMealPlanExportRow row = new CanteenMealPlanExportRow(); + row.setPlanDate(item.getPlanDate() != null ? item.getPlanDate().toString() : ""); + row.setMealSegmentName(SEGMENT_NAMES.getOrDefault(item.getMealSegment(), String.valueOf(item.getMealSegment()))); + row.setRecipeName(item.getRecipeName()); + rows.add(row); + } + return rows; + } + + @Override + public void exportGridExcel(Long id, HttpServletResponse response) { + CanteenMealPlanVO detail = getDetail(id); + if (detail == null) { + throw new IllegalArgumentException("排餐计划不存在"); + } + List dates = detail.getPlanDates(); + List segments = detail.getMealSegments() != null ? detail.getMealSegments() : Collections.emptyList(); + Map> cellMap = new LinkedHashMap<>(); + if (CollUtil.isNotEmpty(detail.getItems())) { + for (CanteenMealPlanItemDTO item : detail.getItems()) { + if (item.getPlanDate() == null || item.getMealSegment() == null) { + continue; + } + String name = item.getRecipeName() != null ? item.getRecipeName().trim() : ""; + if (name.isEmpty()) { + continue; + } + String key = buildCellKey(item.getPlanDate(), item.getMealSegment()); + cellMap.computeIfAbsent(key, k -> new ArrayList<>()).add(name); + } + } + List> rows = new ArrayList<>(); + List header = new ArrayList<>(); + header.add("餐次"); + for (LocalDate d : dates) { + header.add(formatDateHeader(d)); + } + rows.add(header); + for (Integer seg : segments) { + List line = new ArrayList<>(); + line.add(SEGMENT_NAMES.getOrDefault(seg, String.valueOf(seg))); + for (LocalDate d : dates) { + line.add(formatCellRecipes(cellMap.get(buildCellKey(d, seg)))); + } + rows.add(line); + } + try { + String fileName = URLEncoder.encode( + (detail.getPlanName() != null ? detail.getPlanName() : "排餐计划") + "-餐表.xlsx", + StandardCharsets.UTF_8).replace("+", "%20"); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName); + EasyExcel.write(response.getOutputStream()).sheet("排餐餐表").doWrite(rows); + } catch (IOException e) { + throw new RuntimeException("导出失败", e); + } + } + + private CanteenMealPlanEntity toEntity(CanteenMealPlanSaveDTO dto) { + CanteenMealPlanEntity entity = BeanUtils.toBean(dto, CanteenMealPlanEntity.class); + if (entity.getDayCount() == null || entity.getDayCount() < 1) { + entity.setDayCount(1); + } + return entity; + } + + private void saveItems(Long planId, List items) { + if (CollUtil.isEmpty(items)) { + return; + } + Map recipeNameMap = loadRecipeNames(items); + int sort = 0; + for (CanteenMealPlanItemDTO dto : items) { + if (dto.getPlanDate() == null || dto.getMealSegment() == null || dto.getRecipeId() == null) { + continue; + } + CanteenMealPlanItemEntity item = new CanteenMealPlanItemEntity(); + item.setPlanId(planId); + item.setPlanDate(dto.getPlanDate()); + item.setMealSegment(dto.getMealSegment()); + item.setRecipeId(dto.getRecipeId()); + String name = dto.getRecipeName(); + if (name == null || name.isBlank()) { + name = recipeNameMap.get(dto.getRecipeId()); + } + item.setRecipeName(name); + item.setSortOrder(dto.getSortOrder() != null ? dto.getSortOrder() : sort++); + itemMapper.insert(item); + } + } + + private Map loadRecipeNames(List items) { + Set ids = items.stream().map(CanteenMealPlanItemDTO::getRecipeId).filter(Objects::nonNull) + .collect(Collectors.toSet()); + if (ids.isEmpty()) { + return Collections.emptyMap(); + } + return menuRecipeMapper.selectBatchIds(ids).stream() + .collect(Collectors.toMap(CanteenMenuRecipeEntity::getId, CanteenMenuRecipeEntity::getName, (a, b) -> a)); + } + + private List loadMaterialBriefs(List ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return erpMaterialMapper.selectBatchIds(ids).stream().map(m -> { + CanteenMealPlanMaterialBriefVO vo = new CanteenMealPlanMaterialBriefVO(); + vo.setId(m.getId()); + vo.setName(m.getName()); + vo.setUnit(m.getUnit()); + return vo; + }).toList(); + } + + private List buildPlanDates(LocalDate startDate, Integer dayCount) { + if (startDate == null || dayCount == null || dayCount < 1) { + return Collections.emptyList(); + } + List list = new ArrayList<>(); + for (int i = 0; i < dayCount; i++) { + list.add(startDate.plusDays(i)); + } + return list; + } + + private void fillMerchantNames(List records) { + if (CollUtil.isEmpty(records)) { + return; + } + Map nameMap = loadMerchantNameMap(records.stream() + .map(CanteenMealPlanEntity::getMerchantId) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toList())); + for (CanteenMealPlanEntity record : records) { + if (StrUtil.isBlank(record.getMerchantId())) { + continue; + } + record.setMerchantName(nameMap.getOrDefault(record.getMerchantId().trim(), record.getMerchantId())); + } + } + + private String resolveMerchantName(String merchantId) { + if (StrUtil.isBlank(merchantId)) { + return null; + } + return loadMerchantNameMap(Collections.singletonList(merchantId)) + .getOrDefault(merchantId.trim(), merchantId); + } + + private Map loadMerchantNameMap(List merchantIds) { + if (CollUtil.isEmpty(merchantIds)) { + return Collections.emptyMap(); + } + List ids = merchantIds.stream() + .filter(StrUtil::isNotBlank) + .map(String::trim) + .distinct() + .map(id -> { + try { + return Long.parseLong(id); + } + catch (NumberFormatException ex) { + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (ids.isEmpty()) { + return Collections.emptyMap(); + } + R> resp = remoteMchService.getByIds(ids); + if (resp == null || !resp.isOk() || CollUtil.isEmpty(resp.getData())) { + return Collections.emptyMap(); + } + return resp.getData().stream() + .filter(Objects::nonNull) + .collect(Collectors.toMap(m -> String.valueOf(m.getId()), MchManageVo::getName, (a, b) -> a)); + } + + private String buildCellKey(LocalDate planDate, Integer mealSegment) { + return planDate + "_" + mealSegment; + } + + private String formatDateHeader(LocalDate date) { + int weekday = date.getDayOfWeek().getValue() % 7; + return date.format(DateTimeFormatter.ofPattern("M月d日")) + " 周" + WEEKDAY_LABELS[weekday]; + } + + private String formatCellRecipes(List recipeNames) { + if (CollUtil.isEmpty(recipeNames)) { + return ""; + } + return IntStream.range(0, recipeNames.size()) + .mapToObj(i -> (i + 1) + ". " + recipeNames.get(i)) + .collect(Collectors.joining("\n")); + } +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenMenuRecipeServiceImpl.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenMenuRecipeServiceImpl.java new file mode 100644 index 000000000..741ea21c2 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenMenuRecipeServiceImpl.java @@ -0,0 +1,219 @@ +package com.bsd.pjcloud.canteen.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bsd.pjcloud.canteen.dto.menurecipe.CanteenMenuRecipeMatchDTO; +import com.bsd.pjcloud.canteen.entity.CanteenErpMaterialEntity; +import com.bsd.pjcloud.canteen.entity.CanteenMenuRecipeEntity; +import com.bsd.pjcloud.canteen.entity.CanteenMenuRecipeMaterialEntity; +import com.bsd.pjcloud.canteen.entity.CanteenOnlineSpuEntity; +import com.bsd.pjcloud.canteen.mapper.CanteenErpMaterialMapper; +import com.bsd.pjcloud.canteen.mapper.CanteenMenuRecipeMapper; +import com.bsd.pjcloud.canteen.mapper.CanteenMenuRecipeMaterialMapper; +import com.bsd.pjcloud.canteen.mapper.CanteenOnlineSpuMapper; +import com.bsd.pjcloud.canteen.service.CanteenMenuRecipeService; +import com.bsd.pjcloud.canteen.vo.menurecipe.CanteenMenuRecipeMaterialVO; +import com.bsd.pjcloud.canteen.vo.menurecipe.CanteenMenuRecipeVO; +import com.bsd.pjcloud.common.core.util.BeanUtils; +import com.bsd.pjcloud.common.data.datascope.DataScope; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class CanteenMenuRecipeServiceImpl extends ServiceImpl + implements CanteenMenuRecipeService { + + private final CanteenMenuRecipeMaterialMapper materialMapper; + private final CanteenErpMaterialMapper erpMaterialMapper; + private final CanteenOnlineSpuMapper onlineSpuMapper; + + @Override + public Page pageScope(Page page, + LambdaQueryWrapper wrapper) { + Page pageData = baseMapper.selectPageByScope(page, wrapper, DataScope.of()); + fillListExtras(pageData.getRecords()); + return pageData; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean saveDeep(CanteenMenuRecipeEntity entity) { + if (entity.getStatus() == null) { + entity.setStatus(0); + } + baseMapper.insert(entity); + saveMaterials(entity.getId(), entity.getMaterialList()); + return Boolean.TRUE; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateDeep(CanteenMenuRecipeEntity entity) { + baseMapper.updateById(entity); + materialMapper.delete(Wrappers.lambdaQuery() + .eq(CanteenMenuRecipeMaterialEntity::getRecipeId, entity.getId())); + saveMaterials(entity.getId(), entity.getMaterialList()); + return Boolean.TRUE; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean removeDeep(Long[] ids) { + if (ids == null || ids.length == 0) { + return Boolean.TRUE; + } + baseMapper.deleteBatchIds(CollUtil.toList(ids)); + materialMapper.delete(Wrappers.lambdaQuery() + .in(CanteenMenuRecipeMaterialEntity::getRecipeId, CollUtil.toList(ids))); + return Boolean.TRUE; + } + + @Override + public CanteenMenuRecipeVO getDetail(Long id) { + CanteenMenuRecipeEntity entity = baseMapper.selectById(id); + if (entity == null) { + return null; + } + CanteenMenuRecipeVO vo = BeanUtils.toBean(entity, CanteenMenuRecipeVO.class); + vo.setMaterialList(loadMaterialVos(id)); + vo.setMaterialSummary(buildMaterialSummary(vo.getMaterialList())); + if (entity.getOnlineSpuId() != null) { + CanteenOnlineSpuEntity spu = onlineSpuMapper.selectById(entity.getOnlineSpuId()); + if (spu != null) { + vo.setOnlineSpuName(spu.getName()); + } + } + return vo; + } + + @Override + public List matchByMaterials(CanteenMenuRecipeMatchDTO dto) { + if (dto == null || CollUtil.isEmpty(dto.getMaterialIds())) { + return Collections.emptyList(); + } + Set selected = new HashSet<>(dto.getMaterialIds()); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() + .eq(CanteenMenuRecipeEntity::getStatus, 0); + if (StringUtils.isNotBlank(dto.getMerchantId())) { + wrapper.and(w -> w.eq(CanteenMenuRecipeEntity::getMerchantId, dto.getMerchantId()) + .or().isNull(CanteenMenuRecipeEntity::getMerchantId) + .or().eq(CanteenMenuRecipeEntity::getMerchantId, "")); + } + List recipes = baseMapper.selectListByScope(wrapper, DataScope.of()); + if (CollUtil.isEmpty(recipes)) { + return Collections.emptyList(); + } + List recipeIds = recipes.stream().map(CanteenMenuRecipeEntity::getId).toList(); + List allLines = materialMapper.selectList( + Wrappers.lambdaQuery() + .in(CanteenMenuRecipeMaterialEntity::getRecipeId, recipeIds) + .orderByAsc(CanteenMenuRecipeMaterialEntity::getSortOrder)); + Map> recipeMaterialMap = allLines.stream() + .collect(Collectors.groupingBy(CanteenMenuRecipeMaterialEntity::getRecipeId, + Collectors.mapping(CanteenMenuRecipeMaterialEntity::getMaterialId, Collectors.toList()))); + + List matched = new ArrayList<>(); + for (CanteenMenuRecipeEntity recipe : recipes) { + List needIds = recipeMaterialMap.getOrDefault(recipe.getId(), Collections.emptyList()); + if (CollUtil.isEmpty(needIds)) { + continue; + } + if (selected.containsAll(needIds)) { + matched.add(getDetail(recipe.getId())); + } + } + return matched; + } + + private void saveMaterials(Long recipeId, List list) { + if (CollUtil.isEmpty(list)) { + return; + } + int sort = 0; + for (CanteenMenuRecipeMaterialEntity line : list) { + if (line.getMaterialId() == null) { + continue; + } + line.setId(null); + line.setRecipeId(recipeId); + if (line.getSortOrder() == null) { + line.setSortOrder(sort++); + } + materialMapper.insert(line); + } + } + + private List loadMaterialVos(Long recipeId) { + List lines = materialMapper.selectList( + Wrappers.lambdaQuery() + .eq(CanteenMenuRecipeMaterialEntity::getRecipeId, recipeId) + .orderByAsc(CanteenMenuRecipeMaterialEntity::getSortOrder)); + if (CollUtil.isEmpty(lines)) { + return Collections.emptyList(); + } + List vos = BeanUtils.toBean(lines, CanteenMenuRecipeMaterialVO.class); + for (CanteenMenuRecipeMaterialVO vo : vos) { + CanteenErpMaterialEntity material = erpMaterialMapper.selectById(vo.getMaterialId()); + if (material != null) { + vo.setMaterialName(material.getName()); + vo.setUnit(material.getUnit()); + } + } + return vos; + } + + private void fillListExtras(List records) { + if (CollUtil.isEmpty(records)) { + return; + } + List recipeIds = records.stream().map(CanteenMenuRecipeEntity::getId).toList(); + List allLines = materialMapper.selectList( + Wrappers.lambdaQuery() + .in(CanteenMenuRecipeMaterialEntity::getRecipeId, recipeIds) + .orderByAsc(CanteenMenuRecipeMaterialEntity::getSortOrder)); + Set materialIds = allLines.stream().map(CanteenMenuRecipeMaterialEntity::getMaterialId) + .filter(Objects::nonNull).collect(Collectors.toSet()); + Map materialMap = materialIds.isEmpty() ? Collections.emptyMap() + : erpMaterialMapper.selectBatchIds(materialIds).stream() + .collect(Collectors.toMap(CanteenErpMaterialEntity::getId, m -> m, (a, b) -> a)); + + Map> grouped = allLines.stream() + .collect(Collectors.groupingBy(CanteenMenuRecipeMaterialEntity::getRecipeId)); + + Set spuIds = records.stream().map(CanteenMenuRecipeEntity::getOnlineSpuId).filter(Objects::nonNull) + .collect(Collectors.toSet()); + Map spuNameMap = spuIds.isEmpty() ? Collections.emptyMap() + : onlineSpuMapper.selectBatchIds(spuIds).stream() + .collect(Collectors.toMap(CanteenOnlineSpuEntity::getId, CanteenOnlineSpuEntity::getName, (a, b) -> a)); + + for (CanteenMenuRecipeEntity record : records) { + List lines = grouped.getOrDefault(record.getId(), Collections.emptyList()); + String summary = lines.stream().map(line -> { + CanteenErpMaterialEntity m = materialMap.get(line.getMaterialId()); + return m != null ? m.getName() : String.valueOf(line.getMaterialId()); + }).collect(Collectors.joining("、")); + record.setMaterialSummary(summary); + if (record.getOnlineSpuId() != null) { + record.setOnlineSpuName(spuNameMap.get(record.getOnlineSpuId())); + } + } + } + + private String buildMaterialSummary(List list) { + if (CollUtil.isEmpty(list)) { + return ""; + } + return list.stream() + .map(vo -> vo.getMaterialName() != null ? vo.getMaterialName() : String.valueOf(vo.getMaterialId())) + .collect(Collectors.joining("、")); + } +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenErpMaterialMapper.xml b/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenErpMaterialMapper.xml index eb8081a6e..31d2cefd2 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenErpMaterialMapper.xml +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenErpMaterialMapper.xml @@ -61,6 +61,12 @@ and m.material_type = #{canteenErpMaterial.materialType} + + and m.nutrition_tags like concat('%"name":"%', #{canteenErpMaterial.nutritionName}, '%') + + + and m.nutrition_tags like concat('%"effect":"%', #{canteenErpMaterial.nutritionEffect}, '%') + \ No newline at end of file