Sfoglia il codice sorgente

新增
1、餐饮计划导入功能,如无导入的菜品则新增
修改
1、获取机构看板统计(院内动态、护理情况)接口修改统计截止时间

liangwenxuan 2 mesi fa
parent
commit
8c03982e56

+ 18 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/restaurant/CateringPlanController.java

@@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.system.controller.admin.restaurant.vo.CateringPla
 import cn.iocoder.yudao.module.system.controller.admin.restaurant.vo.CateringPlanSaveReqVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.restaurant.CateringPlanDO;
 import cn.iocoder.yudao.module.system.service.restaurant.CateringPlanService;
+import cn.iocoder.yudao.module.system.controller.admin.biz.vo.ImportResultVO;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import org.springframework.web.bind.annotation.*;
@@ -27,6 +28,9 @@ import java.io.IOException;
 import java.util.List;
 import java.util.Objects;
 
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.web.multipart.MultipartFile;
+
 import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 
@@ -84,4 +88,18 @@ public class CateringPlanController {
         return success(cateringPlanService.exportCateringPlanItemExcelDate(id));
     }
 
+    @GetMapping("/import-template")
+    @Operation(summary = "下载餐饮计划导入模板")
+    @TenantIgnore
+    public void importTemplate(HttpServletResponse response) throws IOException {
+        cateringPlanService.exportImportTemplate(response);
+    }
+
+    @PostMapping("/import")
+    @Operation(summary = "导入餐饮计划")
+    @TenantIgnore
+    public CommonResult<ImportResultVO> importCateringPlan(@RequestParam("file") MultipartFile file,
+                                                           @RequestParam("restaurantId") Long restaurantId) throws Exception {
+        return success(cateringPlanService.importCateringPlan(file, restaurantId));
+    }
 }

+ 5 - 2
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/restaurant/CateringPlanItemDO.java

@@ -110,6 +110,9 @@ public class CateringPlanItemDO {
      */
     @Schema(description = "周日可选")
     private String sundayExtra;
-
-
+    /**
+     * 机构id
+     */
+    @Schema(description = "机构id")
+    private Long tenantId;
 }

+ 1 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/biz/ElderlyBuildFloorActivityImageMapper.java

@@ -37,3 +37,4 @@ public interface ElderlyBuildFloorActivityImageMapper extends BaseMapperX<Elderl
 
 
 
+

+ 1 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/biz/ElderlyInfoServiceImpl.java

@@ -1578,7 +1578,7 @@ public class ElderlyInfoServiceImpl implements ElderlyInfoService {
         long askLeaveCount = Optional.ofNullable(elderlyAskLeaveMapper.selectCount(new LambdaQueryWrapperX<ElderlyAskLeaveDO>()
                 .in(ElderlyAskLeaveDO::getTenantId, tenantId)
                 .le(ElderlyAskLeaveDO::getOutDate, todayEndDate)
-                .and(w -> w.ge(ElderlyAskLeaveDO::getUpdateDate, todayStartDate).or().isNull(ElderlyAskLeaveDO::getUpdateDate)))).orElse(0L);
+                .and(w -> w.gt(ElderlyAskLeaveDO::getUpdateDate, LocalDateTime.now()).or().isNull(ElderlyAskLeaveDO::getUpdateDate)))).orElse(0L);
 
         long todayAskLeaveCount = Optional.ofNullable(elderlyAskLeaveMapper.selectCount(new LambdaQueryWrapperX<ElderlyAskLeaveDO>()
                 .in(ElderlyAskLeaveDO::getTenantId, tenantId)

+ 8 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/restaurant/CateringPlanService.java

@@ -7,7 +7,11 @@ import cn.iocoder.yudao.module.system.controller.admin.restaurant.vo.CateringPla
 import cn.iocoder.yudao.module.system.controller.admin.restaurant.vo.CateringPlanSaveReqVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.restaurant.CateringPlanDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.restaurant.CateringPlanItemDO;
+import cn.iocoder.yudao.module.system.controller.admin.biz.vo.ImportResultVO;
+import org.springframework.web.multipart.MultipartFile;
 
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
 import java.util.List;
 
 /**
@@ -32,4 +36,8 @@ public interface CateringPlanService {
 
     CateringPlanRespVO exportCateringPlanItemExcelDate(Long id);
 
+    void exportImportTemplate(HttpServletResponse response) throws IOException;
+
+    ImportResultVO importCateringPlan(MultipartFile file, Long restaurantId) throws Exception;
+
 }

+ 284 - 3
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/restaurant/CateringPlanServiceImpl.java

@@ -2,33 +2,51 @@ package cn.iocoder.yudao.module.system.service.restaurant;
 
 import cn.hutool.core.collection.CollectionUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
+import cn.iocoder.yudao.module.system.controller.admin.biz.vo.ImportResultVO;
 import cn.iocoder.yudao.module.system.controller.admin.restaurant.vo.CateringPlanPageReqVO;
 import cn.iocoder.yudao.module.system.controller.admin.restaurant.vo.CateringPlanRespVO;
 import cn.iocoder.yudao.module.system.controller.admin.restaurant.vo.CateringPlanSaveReqVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.restaurant.CateringPlanDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.restaurant.CateringPlanItemDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.restaurant.DishesDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.restaurant.RestaurantManagementDO;
 import cn.iocoder.yudao.module.system.dal.mysql.restaurant.CateringPlanItemMapper;
 import cn.iocoder.yudao.module.system.dal.mysql.restaurant.CateringPlanMapper;
 import cn.iocoder.yudao.module.system.dal.mysql.restaurant.DishesMapper;
+import cn.iocoder.yudao.module.system.dal.mysql.restaurant.RestaurantManagementMapper;
+import cn.iocoder.yudao.module.system.util.ImportUtil;
 import cn.iocoder.yudao.module.system.util.http.HttpClientUtil;
 import cn.iocoder.yudao.module.system.util.http.HttpClientResult;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.event.AnalysisEventListener;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
+import com.google.gson.Gson;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
 
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
+import org.springframework.web.multipart.MultipartFile;
 
-import java.util.Date;
-import java.util.List;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * 餐谱计划 Service 实现类
@@ -46,6 +64,8 @@ public class CateringPlanServiceImpl implements CateringPlanService {
     @Resource
     private CateringPlanItemMapper cateringPlanItemMapper;
 
+    @Resource
+    private RestaurantManagementMapper restaurantManagementMapper;
 
     @Override
     public Long createCateringPlan(CateringPlanSaveReqVO createReqVO) {
@@ -195,6 +215,162 @@ public class CateringPlanServiceImpl implements CateringPlanService {
         return respVO;
     }
 
+    @Override
+    public void exportImportTemplate(HttpServletResponse response) throws IOException {
+        List<List<String>> data = buildImportTemplateSampleData();
+        ExcelUtils.setResponseProperties(response, "餐饮计划导入模板.xlsx");
+        try (java.io.OutputStream outputStream = response.getOutputStream()) {
+            EasyExcel.write(outputStream)
+                    .sheet("模板")
+                    .doWrite(data);
+            outputStream.flush();
+        }
+    }
+
+    @Override
+    @Transactional
+    public ImportResultVO importCateringPlan(MultipartFile file, Long restaurantId) throws Exception {
+        List<Map<Integer, String>> rawRows = new ArrayList<>();
+        EasyExcel.read(file.getInputStream(), new AnalysisEventListener<Map<Integer, String>>() {
+                    @Override
+                    public void invoke(Map<Integer, String> data, AnalysisContext context) {
+                        rawRows.add(data);
+                    }
+
+                    @Override
+                    public void doAfterAllAnalysed(AnalysisContext context) {
+                    }
+                })
+                .autoCloseStream(true)
+                .sheet(0)
+                .headRowNumber(0)
+                .doRead();
+
+        int success = 0;
+        List<ImportResultVO.RowError> failures = new ArrayList<>();
+
+        if (rawRows.isEmpty()) {
+            return ImportUtil.buildResult(success, failures);
+        }
+
+        Map<String, String> meta = new HashMap<>();
+        List<CateringPlanItemDO> items = new ArrayList<>();
+        for (int i = 0; i < rawRows.size(); i++) {
+            Map<Integer, String> row = rawRows.get(i);
+            String col0 = getCell(row, 0);
+            String col1 = getCell(row, 1);
+            String nextCol0 = i+1 >= rawRows.size() ? "" :getCell(rawRows.get(i+1), 0);
+            if (i < 4) {
+                if (col0 != null) {
+                    meta.put(col0.trim(), col1);
+                }
+                continue;
+            }
+            if(i == 4){
+                continue;
+            }
+            if (col0 == null && col1 == null) {
+                continue;
+            }
+            CateringPlanItemDO item = new CateringPlanItemDO();
+            item.setTenantId(TenantContextHolder.getTenantId());
+            item.setMealTimes(col0);
+            item.setMonday(parseDishes(col1));
+            item.setTuesday(parseDishes(getCell(row, 2)));
+            item.setWednesday(parseDishes(getCell(row, 3)));
+            item.setThursday(parseDishes(getCell(row, 4)));
+            item.setFriday(parseDishes(getCell(row, 5)));
+            item.setSaturday(parseDishes(getCell(row, 6)));
+            item.setSunday(parseDishes(getCell(row, 7)));
+            items.add(item);
+            if (isOptionalMeal(nextCol0)) {
+                Map<Integer, String> nextRow = rawRows.get(i + 1);
+                item.setMondayExtra(parseDishes(getCell(nextRow, 1)));
+                item.setTuesdayExtra(parseDishes(getCell(nextRow, 2)));
+                item.setWednesdayExtra(parseDishes(getCell(nextRow, 3)));
+                item.setThursdayExtra(parseDishes(getCell(nextRow, 4)));
+                item.setFridayExtra(parseDishes(getCell(nextRow, 5)));
+                item.setSaturdayExtra(parseDishes(getCell(nextRow, 6)));
+                item.setSundayExtra(parseDishes(getCell(nextRow, 7)));
+                i++;
+            }
+        }
+
+        if (items.isEmpty()) {
+            ImportUtil.addFailure(failures, 7, null, null, "未读取到餐次明细");
+            return ImportUtil.buildResult(success, failures);
+        }
+
+        CateringPlanDO cateringPlan = buildCateringPlan(meta, restaurantId, failures);
+        if (!failures.isEmpty()) {
+            return ImportUtil.buildResult(success, failures);
+        }
+
+        ensureDishesExists(items, cateringPlan, failures);
+        if (!failures.isEmpty()) {
+            return ImportUtil.buildResult(success, failures);
+        }
+
+        cateringPlanMapper.insert(cateringPlan);
+        for (CateringPlanItemDO item : items) {
+            item.setCateringPlanId(cateringPlan.getId());
+            cateringPlanItemMapper.insert(item);
+        }
+
+        invokeWechatInterface(cateringPlan.getRestaurantId(), cateringPlan.getRestaurantName());
+        success = 1;
+        return ImportUtil.buildResult(success, failures);
+    }
+
+    private LocalDate parseDate(String value, List<ImportResultVO.RowError> failures) {
+        List<DateTimeFormatter> patterns = Arrays.asList(
+                DateTimeFormatter.ofPattern("yyyy/M/d"),
+                DateTimeFormatter.ofPattern("yyyy-MM-dd"),
+                DateTimeFormatter.ofPattern("yyyy/MM/dd"),
+                DateTimeFormatter.ofPattern("yyyy.M.d")
+        );
+        for (DateTimeFormatter formatter : patterns) {
+            try {
+                return LocalDate.parse(value, formatter);
+            } catch (DateTimeParseException ignored) {
+            }
+        }
+        ImportUtil.addFailure(failures, 1, null, null, "计划开始日期格式不正确");
+        return null;
+    }
+
+    private String getCell(Map<Integer, String> row, int index) {
+        String value = row.get(index);
+        return value != null ? value.trim() : null;
+    }
+
+    /**
+     * 格式化菜品字符串
+     * @param dishes
+     * @return
+     */
+    private String parseDishes(String dishes){
+        if(StringUtils.isBlank(dishes)){
+            return "";
+        }
+        return new Gson().toJson(dishes.split("、"));
+    }
+
+    private List<List<String>> buildImportTemplateSampleData() {
+        List<List<String>> data = new ArrayList<>();
+        data.add(Arrays.asList("计划开始日期", ""));
+        data.add(Arrays.asList("计划名称", ""));
+        data.add(Arrays.asList("餐谱说明", ""));
+        data.add(Arrays.asList("上墙备注", ""));
+        data.add(Arrays.asList("餐次", "周一", "周二", "周三", "周四", "周五", "周六", "周日"));
+        data.add(Arrays.asList("早餐", "", "", "", "", "", "", ""));
+        data.add(Arrays.asList("中餐", "", "", "", "", "", "", ""));
+        data.add(Arrays.asList("中餐(可选)", "", "", "", "", "", "", ""));
+        data.add(Arrays.asList("晚餐", "", "", "", "", "", "", ""));
+        return data;
+    }
+
+
     private void invokeWechatInterface(Long restaurantId,String restaurantName){
         // 调用外部接口更新餐厅周菜单
         try {
@@ -220,4 +396,109 @@ public class CateringPlanServiceImpl implements CateringPlanService {
             log.error("调用更新餐厅周菜单接口异常:{}", e.getMessage(), e);
         }
     }
-}
+
+    private CateringPlanDO buildCateringPlan(Map<String, String> meta, Long restaurantId, List<ImportResultVO.RowError> failures) {
+        String startDateStr = meta.get("计划开始日期");
+        String planName = meta.get("计划名称");
+        String description = meta.get("餐谱说明");
+        String remarks = meta.get("上墙备注");
+
+        if (startDateStr == null) {
+            ImportUtil.addFailure(failures, 1, null, null, "计划开始日期不能为空");
+        }
+        if (planName == null) {
+            ImportUtil.addFailure(failures, 2, null, null, "计划名称不能为空");
+        }
+        if (!failures.isEmpty()) {
+            return null;
+        }
+
+        LocalDate startDate = parseDate(startDateStr.trim(), failures);
+        if (startDate == null) {
+            return null;
+        }
+
+        CateringPlanDO cateringPlan = new CateringPlanDO();
+        cateringPlan.setRestaurantId(restaurantId);
+        RestaurantManagementDO restaurant = restaurantManagementMapper.selectById(restaurantId);
+        if (restaurant != null) {
+            cateringPlan.setRestaurantName(restaurant.getRestaurantName());
+        }
+        cateringPlan.setScheduledStartDate(startDate);
+        cateringPlan.setScheduledEndDate(startDate.plusDays(6));
+        cateringPlan.setPlanName(planName);
+        cateringPlan.setDescription(description);
+        cateringPlan.setRemarks(remarks);
+        cateringPlan.setCreatedTime(new Date());
+        cateringPlan.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
+        cateringPlan.setTenantId(TenantContextHolder.getTenantId());
+        return cateringPlan;
+    }
+
+    private void ensureDishesExists(List<CateringPlanItemDO> items, CateringPlanDO cateringPlan, List<ImportResultVO.RowError> failures) {
+        Map<String, DishesDO> existing = dishesMapper.selectList(new LambdaQueryWrapperX<DishesDO>()
+                        .eq(DishesDO::getTenantId, TenantContextHolder.getTenantId())
+                        .eq(DishesDO::getRestaurantId, cateringPlan.getRestaurantId()))
+                .stream()
+                .collect(java.util.stream.Collectors.toMap(DishesDO::getFoodName, item -> item, (a, b) -> a));
+
+        Set<String> toCreate = new LinkedHashSet<>();
+        for (CateringPlanItemDO item : items) {
+            collectDishes(item.getMonday(), toCreate, existing);
+            collectDishes(item.getTuesday(), toCreate, existing);
+            collectDishes(item.getWednesday(), toCreate, existing);
+            collectDishes(item.getThursday(), toCreate, existing);
+            collectDishes(item.getFriday(), toCreate, existing);
+            collectDishes(item.getSaturday(), toCreate, existing);
+            collectDishes(item.getSunday(), toCreate, existing);
+            collectDishes(item.getMondayExtra(), toCreate, existing);
+            collectDishes(item.getTuesdayExtra(), toCreate, existing);
+            collectDishes(item.getWednesdayExtra(), toCreate, existing);
+            collectDishes(item.getThursdayExtra(), toCreate, existing);
+            collectDishes(item.getFridayExtra(), toCreate, existing);
+            collectDishes(item.getSaturdayExtra(), toCreate, existing);
+            collectDishes(item.getSundayExtra(), toCreate, existing);
+        }
+
+        for (String foodName : toCreate) {
+            try {
+                DishesDO dishes = new DishesDO();
+                dishes.setFoodName(foodName);
+                dishes.setRestaurantId(cateringPlan.getRestaurantId());
+                dishes.setRestaurantName(cateringPlan.getRestaurantName());
+                dishes.setTenantId(TenantContextHolder.getTenantId());
+                dishes.setPrice(BigDecimal.ZERO);
+                dishes.setStatus(1);
+                dishes.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
+                dishes.setCreatedTime(new Date());
+                dishesMapper.insert(dishes);
+            } catch (Exception ex) {
+                ImportUtil.addFailure(failures, 1, null, null, "自动创建菜品失败:" + foodName, ex);
+            }
+        }
+    }
+
+    private void collectDishes(String value, Set<String> toCreate, Map<String, DishesDO> existing) {
+        if (value == null || value.isEmpty()) {
+            return;
+        }
+        String normalized = value.replace(",", ",");
+        for (String name : normalized.split(",")) {
+            String trimmed = name.trim();
+            if (trimmed.isEmpty()) {
+                continue;
+            }
+            if (!existing.containsKey(trimmed)) {
+                toCreate.add(trimmed);
+            }
+        }
+    }
+
+    private boolean isOptionalMeal(String mealTimes) {
+        if (mealTimes == null) {
+            return false;
+        }
+        String normalized = mealTimes.replace("(", "(").replace(")", ")");
+        return normalized.contains("(可选)") || normalized.contains("可选");
+    }
+}

+ 1 - 0
yudao-server/src/main/resources/application-test.yaml

@@ -87,6 +87,7 @@ spring:
     host: 47.112.126.153 # 地址\
     port: 6379 # 端口
     database: 0 # 数据库索引
+    password: ynkbc0!
 
 --- #################### 定时任务相关配置 ####################