From 6975aaaa4831d90e2b9e6dba2c75c72385f28734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E5=AE=9D=E9=BE=99?= Date: Thu, 2 Jul 2026 11:25:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../constant/enums/ModelTemplateTypeEnum.java | 19 +++ .../cms/controller/ModelCommonController.java | 70 +++++++++++ .../cms/domain/dto/ModelExportParamDto.java | 32 +++++ .../ruoyi/cms/service/ModelCommonService.java | 45 +++++++ .../ModelWarningIndicatorDataService.java | 21 ++++ .../service/impl/ModelCommonServiceImpl.java | 113 ++++++++++++++++++ .../ModelWarningIndicatorDataServiceImpl.java | 74 ++++++++++++ ...动力动态监测系统-就业失业预警指标数据.xlsx | Bin 0 -> 10471 bytes 8 files changed, 374 insertions(+) create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/constant/enums/ModelTemplateTypeEnum.java create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/ModelCommonController.java create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/dto/ModelExportParamDto.java create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/ModelCommonService.java create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/ModelCommonServiceImpl.java create mode 100644 ruoyi-bussiness/src/main/resources/template/劳动力动态监测系统-就业失业预警指标数据.xlsx diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/constant/enums/ModelTemplateTypeEnum.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/constant/enums/ModelTemplateTypeEnum.java new file mode 100644 index 0000000..a103345 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/constant/enums/ModelTemplateTypeEnum.java @@ -0,0 +1,19 @@ +package com.ruoyi.cms.constant.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 模型模板类型枚举 + * + * @author 马宝龙 + * @date 2026/7/2 + */ +@Getter +@AllArgsConstructor +public enum ModelTemplateTypeEnum { + + MODEL_WARNING_INDICATOR_DATA("劳动力动态监测系统-就业失业预警指标数据"), + ; + private final String desc; +} diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/ModelCommonController.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/ModelCommonController.java new file mode 100644 index 0000000..c423510 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/ModelCommonController.java @@ -0,0 +1,70 @@ +package com.ruoyi.cms.controller; + +import com.ruoyi.cms.constant.enums.ModelTemplateTypeEnum; +import com.ruoyi.cms.domain.dto.ModelExportParamDto; +import com.ruoyi.cms.service.ModelCommonService; +import com.ruoyi.common.core.domain.AjaxResult; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; + +/** + * 模型通用 + * + * @author 马宝龙 + * @date 2026/7/2 + */ +@RestController +@RequestMapping("/model/common") +@RequiredArgsConstructor +public class ModelCommonController { + + private final ModelCommonService modelCommonService; + + /** + * 下载导入模板 + * + * @param response 响应体 + * @param modelTemplateType 模型模板类型 + */ + @GetMapping("/template") + public void template(HttpServletResponse response, + @RequestParam ModelTemplateTypeEnum modelTemplateType) { + modelCommonService.template(response, modelTemplateType); + } + + /** + * 导入数据 + * + * @param file 文件 + * @param modelTemplateType 模型模板类型 + * @return 导入结果 + */ + @PostMapping("/import") + public AjaxResult importExcel(@RequestParam("file") MultipartFile file, + @RequestParam ModelTemplateTypeEnum modelTemplateType) { + String message = modelCommonService.importExcel(file, modelTemplateType); + return AjaxResult.success(message); + } + + /** + * 导出数据 + * + * @param response 响应体 + * @param dto 导出参数 + * @return 导出结果 + */ + @PostMapping("/export") + public AjaxResult exportExcel(HttpServletResponse response, + @RequestBody ModelExportParamDto dto) { + modelCommonService.exportExcel(response, dto); + return AjaxResult.success(); + } +} diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/dto/ModelExportParamDto.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/dto/ModelExportParamDto.java new file mode 100644 index 0000000..bfa39be --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/dto/ModelExportParamDto.java @@ -0,0 +1,32 @@ +package com.ruoyi.cms.domain.dto; + +import com.ruoyi.cms.constant.enums.ModelTemplateTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 模型数据导出参数 + * + * @author 马宝龙 + * @date 2026/7/2 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ModelExportParamDto implements Serializable { + private static final long serialVersionUID = 2273746043886980704L; + + /** + * 模型模板类型 + */ + private ModelTemplateTypeEnum modelTemplateType; + /** + * 指标编码 + */ + private String indicatorCode; +} diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/ModelCommonService.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/ModelCommonService.java new file mode 100644 index 0000000..d581c18 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/ModelCommonService.java @@ -0,0 +1,45 @@ +package com.ruoyi.cms.service; + +import com.ruoyi.cms.constant.enums.ModelTemplateTypeEnum; +import com.ruoyi.cms.domain.dto.ModelExportParamDto; +import com.ruoyi.common.core.domain.AjaxResult; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; + +/** + * 模型通用 + * + * @author 马宝龙 + * @date 2026/7/2 + */ +public interface ModelCommonService { + + /** + * 下载导入模板 + * + * @param response 响应体 + * @param modelTemplateType 模型模板类型 + */ + void template(HttpServletResponse response, ModelTemplateTypeEnum modelTemplateType); + + /** + * 导入数据 + * + * @param file 文件 + * @param modelTemplateType 模型模板类型 + * @return 导入结果 + */ + String importExcel(MultipartFile file, + ModelTemplateTypeEnum modelTemplateType); + + /** + * 导出数据 + * + * @param response 响应体 + * @param dto 导出参数 + */ + void exportExcel(HttpServletResponse response,ModelExportParamDto dto); +} diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/ModelWarningIndicatorDataService.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/ModelWarningIndicatorDataService.java index 6af68cc..70c7f02 100644 --- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/ModelWarningIndicatorDataService.java +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/ModelWarningIndicatorDataService.java @@ -1,7 +1,11 @@ package com.ruoyi.cms.service; +import com.ruoyi.cms.constant.enums.ModelTemplateTypeEnum; import com.ruoyi.cms.domain.ModelWarningIndicatorData; +import com.ruoyi.cms.domain.dto.ModelExportParamDto; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletResponse; import java.util.List; /** @@ -48,4 +52,21 @@ public interface ModelWarningIndicatorDataService { * @return 列表 */ List list(ModelWarningIndicatorData modelWarningIndicatorData); + + + /** + * 导入数据 + * + * @param file 文件 + * @return 导入结果 + */ + String importExcel(MultipartFile file); + + /** + * 导出数据 + * + * @param response 响应体 + * @param dto 导出参数 + */ + void exportExcel(HttpServletResponse response, ModelExportParamDto dto); } \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/ModelCommonServiceImpl.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/ModelCommonServiceImpl.java new file mode 100644 index 0000000..09bb447 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/ModelCommonServiceImpl.java @@ -0,0 +1,113 @@ +package com.ruoyi.cms.service.impl; + +import com.ruoyi.cms.constant.enums.ModelTemplateTypeEnum; +import com.ruoyi.cms.domain.dto.ModelExportParamDto; +import com.ruoyi.cms.service.ModelCommonService; +import com.ruoyi.cms.service.ModelWarningIndicatorDataService; +import com.ruoyi.common.exception.ServiceException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +/** + * 模型通用 + * + * @author 马宝龙 + * @date 2026/7/2 + */ +@Slf4j +@Service +@Validated +@RequiredArgsConstructor +public class ModelCommonServiceImpl implements ModelCommonService { + + private final ModelWarningIndicatorDataService modelWarningIndicatorDataService; + + @Override + public void template(HttpServletResponse response, + ModelTemplateTypeEnum modelTemplateType) { + // 1. 构建资源文件路径(固定在resources/template) + String pathFile = modelTemplateType.getDesc() + ".xlsx"; + String filePath = "template/" + pathFile; + Resource resource = new ClassPathResource(filePath); + + // 2. 校验文件是否存在 + if (!resource.exists()) { + throw new ServiceException("模板文件不存在"); + } + + try (InputStream in = resource.getInputStream(); OutputStream out = response.getOutputStream()) { + + // 3. 设置响应头,使用传入的文件名作为下载名称 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding(StandardCharsets.UTF_8.name()); + // 对文件名编码,解决中文乱码 + String encodedFileName = URLEncoder.encode(pathFile, StandardCharsets.UTF_8.name()) + .replaceAll("\\+", "%20"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + encodedFileName); + + // 4. 读取文件流并写入响应 + byte[] buffer = new byte[1024]; + int len; + while ((len = in.read(buffer)) > 0) { + out.write(buffer, 0, len); + } + out.flush(); + } catch (IOException e) { + log.error("文件下载失败", e); + throw new ServiceException("文件下载失败"); + } + } + + @Override + public String importExcel(MultipartFile file, ModelTemplateTypeEnum modelTemplateType) { + if (Objects.isNull(modelTemplateType)) { + throw new ServiceException("模板类型不能为空"); + } + switch (modelTemplateType) { + case MODEL_WARNING_INDICATOR_DATA: + return modelWarningIndicatorDataService.importExcel(file); + default: + throw new ServiceException("模板类型错误"); + } + } + + @Override + public void exportExcel(HttpServletResponse response, ModelExportParamDto dto) { + + if (Objects.isNull(dto) || Objects.isNull(dto.getModelTemplateType())) { + throw new ServiceException("模板类型不能为空"); + } + switch (dto.getModelTemplateType()) { + case MODEL_WARNING_INDICATOR_DATA: + modelWarningIndicatorDataService.exportExcel(response, dto); + break; + default: + throw new ServiceException("模板类型错误"); + } + try { + String pathFile = dto.getModelTemplateType().getDesc() + ".xlsx"; + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding(StandardCharsets.UTF_8.name()); + // 对文件名编码,解决中文乱码 + String encodedFileName = URLEncoder.encode(pathFile, StandardCharsets.UTF_8.name()) + .replaceAll("\\+", "%20"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + encodedFileName); + } catch (Exception e) { + log.error("文件参数设置失败", e); + throw new ServiceException("文件参数设置失败"); + } + } +} diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/ModelWarningIndicatorDataServiceImpl.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/ModelWarningIndicatorDataServiceImpl.java index cb80dd9..8f8c55e 100644 --- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/ModelWarningIndicatorDataServiceImpl.java +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/ModelWarningIndicatorDataServiceImpl.java @@ -1,15 +1,22 @@ package com.ruoyi.cms.service.impl; +import com.ruoyi.cms.constant.enums.ModelTemplateTypeEnum; import com.ruoyi.cms.domain.ModelWarningIndicatorData; +import com.ruoyi.cms.domain.dto.ModelExportParamDto; import com.ruoyi.cms.mapper.ModelWarningIndicatorDataMapper; import com.ruoyi.cms.service.ModelWarningIndicatorDataService; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanValidators; +import com.ruoyi.common.utils.poi.ExcelUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Validator; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -27,6 +34,7 @@ import java.util.Optional; public class ModelWarningIndicatorDataServiceImpl implements ModelWarningIndicatorDataService { private final ModelWarningIndicatorDataMapper modelWarningIndicatorDataMapper; + private final Validator validator; @Override public void create(ModelWarningIndicatorData modelWarningIndicatorData) { @@ -126,4 +134,70 @@ public class ModelWarningIndicatorDataServiceImpl implements ModelWarningIndicat public List list(ModelWarningIndicatorData modelWarningIndicatorData) { return modelWarningIndicatorDataMapper.list(modelWarningIndicatorData); } + + @Override + public String importExcel(MultipartFile file) { + try { + ExcelUtil util = new ExcelUtil<>(ModelWarningIndicatorData.class); + List list = util.importExcel(file.getInputStream()); + + ModelWarningIndicatorData exists; + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + for (ModelWarningIndicatorData item : list) { + try { + exists = modelWarningIndicatorDataMapper.findUnique(item); + if (exists == null) { + BeanValidators.validateWithException(validator, item); + modelWarningIndicatorDataMapper.insert(item); + successNum++; + String msg = + "
" + successNum + "、" + "指标:" + item.getIndicatorCode() + "(" + item.getRegionName() + ")" + item.getYear() + "年" + item.getMonth() + "月数据导入成功。"; + successMsg.append(msg); + } else { + item.setId(exists.getId()); + BeanValidators.validateWithException(validator, item); + modelWarningIndicatorDataMapper.update(item); + successNum++; + String msg = + "
" + successNum + "、" + "指标:" + item.getIndicatorCode() + "(" + item.getRegionName() + ")" + item.getYear() + "年" + item.getMonth() + "月数据更新成功。"; + successMsg.append(msg); + } + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、" + item.getYear() + "年" + item.getMonth() + "月数据导入失败:"; + failureMsg.append(msg).append(e.getMessage()); + log.error(msg, e); + } + } + if (failureNum > 0) { + failureMsg.insert(0, "导入失败!共 " + failureNum + " 条数据出现问题,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } else { + successMsg.insert(0, "数据已全部导入成功!共 " + successNum + " 条:"); + } + return successMsg.toString(); + } catch (Exception e) { + log.error("数据导入失败", e); + throw new ServiceException("数据导入失败"); + } + } + + @Override + public void exportExcel(HttpServletResponse response, ModelExportParamDto dto) { + try { + + List list = list(ModelWarningIndicatorData.builder() + .indicatorCode(dto.getIndicatorCode()) + .build()); + ExcelUtil util = new ExcelUtil<>(ModelWarningIndicatorData.class); + util.exportExcel(response, list, ModelTemplateTypeEnum.MODEL_WARNING_INDICATOR_DATA.getDesc()); + + } catch (Exception e) { + log.error("数据导出失败", e); + throw new ServiceException("数据导出失败"); + } + } } \ No newline at end of file diff --git a/ruoyi-bussiness/src/main/resources/template/劳动力动态监测系统-就业失业预警指标数据.xlsx b/ruoyi-bussiness/src/main/resources/template/劳动力动态监测系统-就业失业预警指标数据.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8caf773c6be59115c7aa00fad40cd46ab1b916f0 GIT binary patch literal 10471 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∋{E(@A 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