From 3a8538fc6a9a9c40430394470bc6a14516aa34e2 Mon Sep 17 00:00:00 2001 From: lcj <2331845269@qq.com> Date: Thu, 17 Apr 2025 17:40:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=A0=B9=E6=8D=AEword?= =?UTF-8?q?=E6=A8=A1=E7=89=88=E7=94=9F=E6=88=90word=E6=96=B9=E6=B3=95?= =?UTF-8?q?=EF=BC=9B=E6=96=B0=E5=A2=9E=E8=B4=A8=E9=87=8F=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3=EF=BC=9B=E4=BC=98=E5=8C=96?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E6=94=B9HSE=E7=AE=A1=E7=90=86=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E4=BB=A3=E7=A0=81=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../template/安全生产监督检查通知书模版.docx | Bin 0 -> 14320 bytes .../docs/template/整改通知单模版.docx | Bin 0 -> 13885 bytes .../docs/template/施工日志模版.docx | Bin 0 -> 13122 bytes .../src/main/resources/application.yml | 6 +- .../dromara/common/oss/core/OssClient.java | 37 ++ .../quality/constant/QualityConstant.java | 47 ++ .../BusQualityConstructionLogController.java | 119 ++++ .../BusQualityInspectionController.java | 119 ++++ .../domain/BusQualityConstructionLog.java | 74 +++ .../quality/domain/BusQualityInspection.java | 129 +++++ .../enums/QualityInspectionStatusEnum.java | 26 + .../QualityConstructionLogCreateReq.java | 51 ++ .../QualityConstructionLogQueryReq.java | 31 ++ .../QualityConstructionLogUpdateReq.java | 48 ++ .../QualityInspectionCreateReq.java | 64 +++ .../QualityInspectionQueryReq.java | 38 ++ .../QualityInspectionUpdateReq.java | 109 ++++ .../vo/BusQualityConstructionLogVo.java | 85 +++ .../domain/vo/BusQualityInspectionVo.java | 157 ++++++ .../BusQualityConstructionLogMapper.java | 15 + .../mapper/BusQualityInspectionMapper.java | 15 + .../IBusQualityConstructionLogService.java | 108 ++++ .../service/IBusQualityInspectionService.java | 108 ++++ .../BusQualityConstructionLogServiceImpl.java | 461 +++++++++++++++ .../impl/BusQualityInspectionServiceImpl.java | 524 ++++++++++++++++++ .../safety/constant/SafetyConstant.java | 34 ++ .../BusSafetyInspectionController.java | 11 + .../SafetyInspectionQueryReq.java | 56 -- .../service/IBusSafetyInspectionService.java | 9 + .../impl/BusSafetyInspectionServiceImpl.java | 225 +++++++- .../service/impl/BusSafetyLogServiceImpl.java | 2 +- .../java/org/dromara/utils/DocumentUtils.java | 100 ++++ .../BusQualityConstructionLogMapper.xml | 7 + .../quality/BusQualityInspectionMapper.xml | 7 + RuoYi-Vue-Plus/script/sql/xinnengyuan.sql | 52 ++ 35 files changed, 2788 insertions(+), 86 deletions(-) create mode 100644 RuoYi-Vue-Plus/docs/template/安全生产监督检查通知书模版.docx create mode 100644 RuoYi-Vue-Plus/docs/template/整改通知单模版.docx create mode 100644 RuoYi-Vue-Plus/docs/template/施工日志模版.docx create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/constant/QualityConstant.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/controller/BusQualityConstructionLogController.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/controller/BusQualityInspectionController.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/BusQualityConstructionLog.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/BusQualityInspection.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/enums/QualityInspectionStatusEnum.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityconstructionlog/QualityConstructionLogCreateReq.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityconstructionlog/QualityConstructionLogQueryReq.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityconstructionlog/QualityConstructionLogUpdateReq.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityinspection/QualityInspectionCreateReq.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityinspection/QualityInspectionQueryReq.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityinspection/QualityInspectionUpdateReq.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/vo/BusQualityConstructionLogVo.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/vo/BusQualityInspectionVo.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/mapper/BusQualityConstructionLogMapper.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/mapper/BusQualityInspectionMapper.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/service/IBusQualityConstructionLogService.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/service/IBusQualityInspectionService.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/service/impl/BusQualityConstructionLogServiceImpl.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/service/impl/BusQualityInspectionServiceImpl.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/constant/SafetyConstant.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/utils/DocumentUtils.java create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/quality/BusQualityConstructionLogMapper.xml create mode 100644 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/quality/BusQualityInspectionMapper.xml diff --git a/RuoYi-Vue-Plus/docs/template/安全生产监督检查通知书模版.docx b/RuoYi-Vue-Plus/docs/template/安全生产监督检查通知书模版.docx new file mode 100644 index 0000000000000000000000000000000000000000..d46710a67a225bc69c0839c08040326fa5450344 GIT binary patch literal 14320 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 O0!+T0Pg;z>e*Hi7u5^t6 literal 0 HcmV?d00001 diff --git a/RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/application.yml b/RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/application.yml index 72e71df1..d3c96139 100644 --- a/RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/application.yml +++ b/RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/application.yml @@ -222,9 +222,11 @@ springdoc: packages-to-scan: org.dromara.machinery - group: 6.安全模块 packages-to-scan: org.dromara.safety - - group: 7.代码生成模块 + - group: 7.质量模块 + packages-to-scan: org.dromara.quality + - group: 8.代码生成模块 packages-to-scan: org.dromara.generator - - group: 8.工作流模块 + - group: 9.工作流模块 packages-to-scan: org.dromara.workflow # 防止XSS攻击 diff --git a/RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java b/RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java index 8ff11c96..b43b00de 100644 --- a/RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java +++ b/RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java @@ -3,6 +3,7 @@ package org.dromara.common.oss.core; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.IdUtil; import org.dromara.common.core.constant.Constants; +import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.DateUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.file.FileUtils; @@ -31,6 +32,7 @@ import java.net.URI; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.time.Duration; import java.util.function.Consumer; @@ -235,6 +237,41 @@ public class OssClient { return tempFilePath; } + /** + * 下载文件从 Amazon S3 到临时目录 + * + * @param path 文件在 Amazon S3 中的对象键 + * @param fileName 文件名 + * @param filePath 文件路径 + * @return 下载后的文件在本地的临时路径 + * @throws OssException 如果下载失败,抛出自定义异常 + */ + public String fileDownload(String path, String fileName, String filePath) { + // 构建临时文件 + Path targetDir = Paths.get(filePath); + try { + if (Files.notExists(targetDir)) { + Files.createDirectories(targetDir); + } + } catch (IOException e) { + throw new ServiceException("无法创建目标目录: " + targetDir + e); + } + // 拼出本地完整路径 + Path destinationPath = targetDir.resolve(fileName); + // 使用 S3TransferManager 下载文件 + FileDownload downloadFile = transferManager.downloadFile( + x -> x.getObjectRequest( + y -> y.bucket(properties.getBucketName()) + .key(removeBaseUrl(path)) + .build()) + .addTransferListener(LoggingTransferListener.create()) + .destination(destinationPath) + .build()); + // 等待文件下载操作完成 + downloadFile.completionFuture().join(); + return filePath + "/" + fileName; + } + /** * 下载文件从 Amazon S3 到 输出流 * diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/constant/QualityConstant.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/constant/QualityConstant.java new file mode 100644 index 00000000..85540e25 --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/constant/QualityConstant.java @@ -0,0 +1,47 @@ +package org.dromara.quality.constant; + +import org.dromara.common.core.utils.DateUtils; +import org.dromara.quality.domain.BusQualityConstructionLog; +import org.dromara.quality.domain.BusQualityInspection; + +import java.text.SimpleDateFormat; + +/** + * @author lcj + * @date 2025/4/17 14:36 + */ +public interface QualityConstant { + + String QUALITY_INSPECTION_CHECK_TYPE = "quality_inspection_check_type"; + + String QUALITY_INSPECTION_STATUS_TYPE = "quality_inspection_status_type"; + + String QUALITY_CONSTRUCTION_LOG_TEMPLATE_PATH = "docs/template/施工日志模版.docx"; + + String QUALITY_CONSTRUCTION_LOG_FILE_URL = "docs/quality/constructionLog/"; + + static String getQualityConstructionLogFileUrl(BusQualityConstructionLog qualityConstructionLog) { + String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(qualityConstructionLog.getUpdateTime()); + return String.format("%s%s/%s", QUALITY_CONSTRUCTION_LOG_FILE_URL, qualityConstructionLog.getId(), timestamp); + } + + static String getQualityConstructionLogFileName(BusQualityConstructionLog qualityConstructionLog) { + String createDate = DateUtils.formatDate(qualityConstructionLog.getCreateTime()); + return String.format("施工日志(%s).docx", createDate); + } + + String QUALITY_INSPECTION_TEMPLATE_PATH = "docs/template/整改通知单模版.docx"; + + String QUALITY_INSPECTION_FILE_URL = "docs/quality/inspection/"; + + static String getQualityInspectionFileUrl(BusQualityInspection qualityInspection) { + String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(qualityInspection.getUpdateTime()); + return String.format("%s%s/%s", QUALITY_INSPECTION_FILE_URL, qualityInspection.getId(), timestamp); + } + + static String getQualityInspectionFileName(BusQualityInspection qualityInspection) { + String createDate = DateUtils.formatDate(qualityInspection.getCreateTime()); + return String.format("整改通知单(%s).docx", createDate); + } + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/controller/BusQualityConstructionLogController.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/controller/BusQualityConstructionLogController.java new file mode 100644 index 00000000..6917c039 --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/controller/BusQualityConstructionLogController.java @@ -0,0 +1,119 @@ +package org.dromara.quality.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.quality.domain.req.qualityconstructionlog.QualityConstructionLogCreateReq; +import org.dromara.quality.domain.req.qualityconstructionlog.QualityConstructionLogQueryReq; +import org.dromara.quality.domain.req.qualityconstructionlog.QualityConstructionLogUpdateReq; +import org.dromara.quality.domain.vo.BusQualityConstructionLogVo; +import org.dromara.quality.service.IBusQualityConstructionLogService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 质量-施工日志 + * + * @author lcj + * @date 2025-04-16 + */ +@Validated +@RestController +@RequestMapping("/quality/qualityConstructionLog") +public class BusQualityConstructionLogController extends BaseController { + + @Resource + private IBusQualityConstructionLogService busQualityConstructionLogService; + + /** + * 查询质量-施工日志列表 + */ + @SaCheckPermission("quality:qualityConstructionLog:list") + @GetMapping("/list") + public TableDataInfo list(QualityConstructionLogQueryReq req, PageQuery pageQuery) { + return busQualityConstructionLogService.queryPageList(req, pageQuery); + } + + /** + * 导出质量-施工日志列表 + */ + @SaCheckPermission("quality:qualityConstructionLog:export") + @Log(title = "质量-施工日志", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(QualityConstructionLogQueryReq req, HttpServletResponse response) { + List list = busQualityConstructionLogService.queryList(req); + ExcelUtil.exportExcel(list, "质量-施工日志", BusQualityConstructionLogVo.class, response); + } + + /** + * 根据主键导出质量-施工日志 + */ + @SaCheckPermission("quality:qualityConstructionLog:export") + @Log(title = "质量-施工日志", businessType = BusinessType.EXPORT) + @PostMapping("/export/word") + public void exportWordById(@NotNull(message = "主键不能为空") Long id, + HttpServletResponse response) { + busQualityConstructionLogService.exportWordById(id, response); + } + + /** + * 获取质量-施工日志详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("quality:qualityConstructionLog:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(busQualityConstructionLogService.queryById(id)); + } + + /** + * 新增质量-施工日志 + */ + @SaCheckPermission("quality:qualityConstructionLog:add") + @Log(title = "质量-施工日志", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody QualityConstructionLogCreateReq req) { + return R.ok(busQualityConstructionLogService.insertByBo(req)); + } + + /** + * 修改质量-施工日志 + */ + @SaCheckPermission("quality:qualityConstructionLog:edit") + @Log(title = "质量-施工日志", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody QualityConstructionLogUpdateReq req) { + return toAjax(busQualityConstructionLogService.updateByBo(req)); + } + + /** + * 删除质量-施工日志 + * + * @param ids 主键串 + */ + @SaCheckPermission("quality:qualityConstructionLog:remove") + @Log(title = "质量-施工日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(busQualityConstructionLogService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/controller/BusQualityInspectionController.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/controller/BusQualityInspectionController.java new file mode 100644 index 00000000..b7335822 --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/controller/BusQualityInspectionController.java @@ -0,0 +1,119 @@ +package org.dromara.quality.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.quality.domain.req.qualityinspection.QualityInspectionCreateReq; +import org.dromara.quality.domain.req.qualityinspection.QualityInspectionQueryReq; +import org.dromara.quality.domain.req.qualityinspection.QualityInspectionUpdateReq; +import org.dromara.quality.domain.vo.BusQualityInspectionVo; +import org.dromara.quality.service.IBusQualityInspectionService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 质量-检查工单 + * + * @author lcj + * @date 2025-04-16 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/quality/qualityInspection") +public class BusQualityInspectionController extends BaseController { + + private final IBusQualityInspectionService busQualityInspectionService; + + /** + * 查询质量-检查工单列表 + */ + @SaCheckPermission("quality:qualityInspection:list") + @GetMapping("/list") + public TableDataInfo list(QualityInspectionQueryReq req, PageQuery pageQuery) { + return busQualityInspectionService.queryPageList(req, pageQuery); + } + + /** + * 导出质量-检查工单列表 + */ + @SaCheckPermission("quality:qualityInspection:export") + @Log(title = "质量-检查工单", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(QualityInspectionQueryReq req, HttpServletResponse response) { + List list = busQualityInspectionService.queryList(req); + ExcelUtil.exportExcel(list, "质量-检查工单", BusQualityInspectionVo.class, response); + } + + /** + * 根据主键导出质量-检查工单 + */ + @SaCheckPermission("quality:qualityInspection:export") + @Log(title = "质量-检查工单", businessType = BusinessType.EXPORT) + @PostMapping("/export/word") + public void exportWordById(@NotNull(message = "主键不能为空") Long id, + HttpServletResponse response) { + busQualityInspectionService.exportWordById(id, response); + } + + /** + * 获取质量-检查工单详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("quality:qualityInspection:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(busQualityInspectionService.queryById(id)); + } + + /** + * 新增质量-检查工单 + */ + @SaCheckPermission("quality:qualityInspection:add") + @Log(title = "质量-检查工单", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody QualityInspectionCreateReq req) { + return R.ok(busQualityInspectionService.insertByBo(req)); + } + + /** + * 修改质量-检查工单 + */ + @SaCheckPermission("quality:qualityInspection:edit") + @Log(title = "质量-检查工单", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody QualityInspectionUpdateReq req) { + return toAjax(busQualityInspectionService.updateByBo(req)); + } + + /** + * 删除质量-检查工单 + * + * @param ids 主键串 + */ + @SaCheckPermission("quality:qualityInspection:remove") + @Log(title = "质量-检查工单", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(busQualityInspectionService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/BusQualityConstructionLog.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/BusQualityConstructionLog.java new file mode 100644 index 00000000..a8df87b6 --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/BusQualityConstructionLog.java @@ -0,0 +1,74 @@ +package org.dromara.quality.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 质量-施工日志对象 bus_quality_construction_log + * + * @author lcj + * @date 2025-04-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("bus_quality_construction_log") +public class BusQualityConstructionLog extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键id + */ + @TableId(value = "id") + private Long id; + + /** + * 项目id + */ + private Long projectId; + + /** + * 发生日期 + */ + private Date happenDate; + + /** + * 生产情况 + */ + private String productionStatus; + + /** + * 技术质量安全工作 + */ + private String technologyQuality; + + /** + * 附件 + */ + private String file; + + /** + * 备注 + */ + private String remark; + + /** + * 删除时间 + */ + private Date deletedAt; + + /** + * 是否删除(0正常 1删除) + */ + @TableLogic + private Long isDelete; + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/BusQualityInspection.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/BusQualityInspection.java new file mode 100644 index 00000000..323951c4 --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/BusQualityInspection.java @@ -0,0 +1,129 @@ +package org.dromara.quality.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 质量-检查工单对象 bus_quality_inspection + * + * @author lcj + * @date 2025-04-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("bus_quality_inspection") +public class BusQualityInspection extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键id + */ + @TableId(value = "id") + private Long id; + + /** + * 项目id + */ + private Long projectId; + + /** + * 巡检类型 + */ + private String inspectionType; + + /** + * 巡检标题 + */ + private String inspectionHeadline; + + /** + * 巡检结果 + */ + private String inspectionResult; + + /** + * 工单状态(1通知 2整改 3验证) + */ + private String inspectionStatus; + + /** + * 巡检附件 + */ + private String inspectionFile; + + /** + * 班组id + */ + private Long teamId; + + /** + * 整改人(班组长) + */ + private Long corrector; + + /** + * 是否回复(1回复 2不回复) + */ + private String isReply; + + /** + * 回复期限日期 + */ + private Date replyPeriodDate; + + /** + * 整改反馈 + */ + private String rectificationResult; + + /** + * 整改时间 + */ + private Date rectificationTime; + + /** + * 整改附件 + */ + private String rectificationFile; + + /** + * 验证结果 + */ + private String verificationResult; + + /** + * 验证状态(1通过 2未通过) + */ + private String verificationType; + + /** + * 验证时间 + */ + private Date verificationTime; + + /** + * 备注 + */ + private String remark; + + /** + * 删除时间 + */ + private Date deletedAt; + + /** + * 是否删除(0正常 1删除) + */ + @TableLogic + private Long isDelete; + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/enums/QualityInspectionStatusEnum.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/enums/QualityInspectionStatusEnum.java new file mode 100644 index 00000000..7058762b --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/enums/QualityInspectionStatusEnum.java @@ -0,0 +1,26 @@ +package org.dromara.quality.domain.enums; + +import lombok.Getter; + +/** + * @author lcj + * @date 2025/4/16 14:26 + */ +@Getter +public enum QualityInspectionStatusEnum { + + INFORM("通知", "1"), + RECTIFICATION("整改", "2"), + VERIFICATION("验证", "3"), + PASS("通过", "4"); + + private final String text; + + private final String value; + + QualityInspectionStatusEnum(String text, String value) { + this.text = text; + this.value = value; + } + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityconstructionlog/QualityConstructionLogCreateReq.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityconstructionlog/QualityConstructionLogCreateReq.java new file mode 100644 index 00000000..b66f17be --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityconstructionlog/QualityConstructionLogCreateReq.java @@ -0,0 +1,51 @@ +package org.dromara.quality.domain.req.qualityconstructionlog; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * @author lcj + * @date 2025/4/16 11:54 + */ +@Data +public class QualityConstructionLogCreateReq implements Serializable { + + @Serial + private static final long serialVersionUID = -9122588005233479105L; + + /** + * 项目id + */ + private Long projectId; + + /** + * 发生日期 + */ + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") + private Date happenDate; + + /** + * 生产情况 + */ + private String productionStatus; + + /** + * 技术质量安全工作 + */ + private String technologyQuality; + + /** + * 附件 + */ + private String file; + + /** + * 备注 + */ + private String remark; + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityconstructionlog/QualityConstructionLogQueryReq.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityconstructionlog/QualityConstructionLogQueryReq.java new file mode 100644 index 00000000..a3c3a8ae --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityconstructionlog/QualityConstructionLogQueryReq.java @@ -0,0 +1,31 @@ +package org.dromara.quality.domain.req.qualityconstructionlog; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * @author lcj + * @date 2025/4/16 11:55 + */ +@Data +public class QualityConstructionLogQueryReq implements Serializable { + + @Serial + private static final long serialVersionUID = -4555254995729496353L; + + /** + * 项目id + */ + private Long projectId; + + /** + * 发生日期 + */ + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") + private Date happenDate; + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityconstructionlog/QualityConstructionLogUpdateReq.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityconstructionlog/QualityConstructionLogUpdateReq.java new file mode 100644 index 00000000..3f5fe465 --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityconstructionlog/QualityConstructionLogUpdateReq.java @@ -0,0 +1,48 @@ +package org.dromara.quality.domain.req.qualityconstructionlog; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author lcj + * @date 2025/4/16 11:57 + */ +@Data +public class QualityConstructionLogUpdateReq implements Serializable { + + @Serial + private static final long serialVersionUID = 5378506292803717193L; + + /** + * 主键id + */ + private Long id; + + /** + * 项目id + */ + private Long projectId; + + /** + * 生产情况 + */ + private String productionStatus; + + /** + * 技术质量安全工作 + */ + private String technologyQuality; + + /** + * 附件 + */ + private String file; + + /** + * 备注 + */ + private String remark; + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityinspection/QualityInspectionCreateReq.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityinspection/QualityInspectionCreateReq.java new file mode 100644 index 00000000..1be78cc4 --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityinspection/QualityInspectionCreateReq.java @@ -0,0 +1,64 @@ +package org.dromara.quality.domain.req.qualityinspection; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * @author lcj + * @date 2025/4/16 14:03 + */ +@Data +public class QualityInspectionCreateReq implements Serializable { + + @Serial + private static final long serialVersionUID = -4466466614771971424L; + + /** + * 项目id + */ + private Long projectId; + + /** + * 巡检类型 + */ + private String inspectionType; + + /** + * 巡检标题 + */ + private String inspectionHeadline; + + /** + * 巡检结果 + */ + private String inspectionResult; + + /** + * 巡检附件 + */ + private String inspectionFile; + + /** + * 班组id + */ + private Long teamId; + + /** + * 整改人(班组长) + */ + private Long corrector; + + /** + * 回复期限日期 + */ + private Date replyPeriodDate; + + /** + * 备注 + */ + private String remark; + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityinspection/QualityInspectionQueryReq.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityinspection/QualityInspectionQueryReq.java new file mode 100644 index 00000000..f944336d --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityinspection/QualityInspectionQueryReq.java @@ -0,0 +1,38 @@ +package org.dromara.quality.domain.req.qualityinspection; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author lcj + * @date 2025/4/16 14:04 + */ +@Data +public class QualityInspectionQueryReq implements Serializable { + + @Serial + private static final long serialVersionUID = 3468757869732753393L; + + /** + * 项目id + */ + private Long projectId; + + /** + * 巡检类型 + */ + private String inspectionType; + + /** + * 工单状态(1通知 2整改 3验证) + */ + private String inspectionStatus; + + /** + * 班组id + */ + private Long teamId; + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityinspection/QualityInspectionUpdateReq.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityinspection/QualityInspectionUpdateReq.java new file mode 100644 index 00000000..e84ca0d9 --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/req/qualityinspection/QualityInspectionUpdateReq.java @@ -0,0 +1,109 @@ +package org.dromara.quality.domain.req.qualityinspection; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * @author lcj + * @date 2025/4/16 14:04 + */ +@Data +public class QualityInspectionUpdateReq implements Serializable { + + @Serial + private static final long serialVersionUID = -8931016399391922936L; + + /** + * 主键id + */ + private Long id; + + /** + * 项目id + */ + private Long projectId; + + /** + * 巡检类型 + */ + private String inspectionType; + + /** + * 巡检标题 + */ + private String inspectionHeadline; + + /** + * 巡检结果 + */ + private String inspectionResult; + + /** + * 工单状态(1通知 2整改 3验证) + */ + private String inspectionStatus; + + /** + * 巡检附件 + */ + private String inspectionFile; + + /** + * 班组id + */ + private Long teamId; + + /** + * 整改人(班组长) + */ + private Long corrector; + + /** + * 是否回复(1回复 2不回复) + */ + private String isReply; + + /** + * 回复期限日期 + */ + private Date replyPeriodDate; + + /** + * 整改反馈 + */ + private String rectificationResult; + + /** + * 整改时间 + */ + private Date rectificationTime; + + /** + * 整改附件 + */ + private String rectificationFile; + + /** + * 验证结果 + */ + private String verificationResult; + + /** + * 验证状态(1通过 2未通过) + */ + private String verificationType; + + /** + * 验证时间 + */ + private Date verificationTime; + + /** + * 备注 + */ + private String remark; + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/vo/BusQualityConstructionLogVo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/vo/BusQualityConstructionLogVo.java new file mode 100644 index 00000000..7df12069 --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/vo/BusQualityConstructionLogVo.java @@ -0,0 +1,85 @@ +package org.dromara.quality.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.quality.domain.BusQualityConstructionLog; +import org.dromara.system.domain.vo.SysOssVo; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + + +/** + * 质量-施工日志视图对象 bus_quality_construction_log + * + * @author lcj + * @date 2025-04-16 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = BusQualityConstructionLog.class) +public class BusQualityConstructionLogVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键id + */ + private Long id; + + /** + * 项目名称 + */ + private String projectName; + + /** + * 发生日期 + */ + @ExcelProperty(value = "发生日期") + private Date happenDate; + + /** + * 生产情况 + */ + @ExcelProperty(value = "生产情况") + private String productionStatus; + + /** + * 技术质量安全工作 + */ + @ExcelProperty(value = "技术质量安全工作") + private String technologyQuality; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 附件 + */ + private String file; + + /** + * 附件列表 + */ + private List fileList; + + /** + * 创建者 + */ + @ExcelProperty(value = "创建者") + private String createBy; + + /** + * 创建时间 + */ + private Date createTime; + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/vo/BusQualityInspectionVo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/vo/BusQualityInspectionVo.java new file mode 100644 index 00000000..f30f6e22 --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/domain/vo/BusQualityInspectionVo.java @@ -0,0 +1,157 @@ +package org.dromara.quality.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import org.dromara.quality.domain.BusQualityInspection; +import org.dromara.system.domain.vo.SysOssVo; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + + +/** + * 质量-检查工单视图对象 bus_quality_inspection + * + * @author lcj + * @date 2025-04-16 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = BusQualityInspection.class) +public class BusQualityInspectionVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long id; + + /** + * 项目名称 + */ + private String projectName; + + /** + * 巡检类型 + */ + @ExcelProperty(value = "巡检类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "quality_inspection_check_type") + private String inspectionType; + + /** + * 巡检标题 + */ + @ExcelProperty(value = "巡检标题") + private String inspectionHeadline; + + /** + * 巡检结果 + */ + @ExcelProperty(value = "巡检结果") + private String inspectionResult; + + /** + * 工单状态(1通知 2整改 3验证) + */ + @ExcelProperty(value = "工单状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "quality_inspection_status_type") + private String inspectionStatus; + + /** + * 巡检附件 + */ + private String inspectionFile; + + /** + * 巡检附件列表 + */ + private List inspectionFileList; + + /** + * 班组id + */ + private Long teamId; + + /** + * 整改人(班组长) + */ + private Long corrector; + + /** + * 整改人姓名 + */ + private String correctorName; + + /** + * 是否回复(1回复 2不回复) + */ + private String isReply; + + /** + * 回复期限日期 + */ + private Date replyPeriodDate; + + /** + * 整改反馈 + */ + private String rectificationResult; + + /** + * 整改时间 + */ + private Date rectificationTime; + + /** + * 整改附件 + */ + private String rectificationFile; + + /** + * 整改附件列表 + */ + private List rectificationFileList; + + /** + * 验证结果 + */ + private String verificationResult; + + /** + * 验证状态(1通过 2未通过) + */ + private String verificationType; + + /** + * 验证时间 + */ + private Date verificationTime; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者 + */ + @ExcelProperty(value = "创建者") + private String createBy; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/mapper/BusQualityConstructionLogMapper.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/mapper/BusQualityConstructionLogMapper.java new file mode 100644 index 00000000..040fa5c2 --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/mapper/BusQualityConstructionLogMapper.java @@ -0,0 +1,15 @@ +package org.dromara.quality.mapper; + +import org.dromara.quality.domain.BusQualityConstructionLog; +import org.dromara.quality.domain.vo.BusQualityConstructionLogVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 质量-施工日志Mapper接口 + * + * @author lcj + * @date 2025-04-16 + */ +public interface BusQualityConstructionLogMapper extends BaseMapperPlus { + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/mapper/BusQualityInspectionMapper.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/mapper/BusQualityInspectionMapper.java new file mode 100644 index 00000000..47571d80 --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/mapper/BusQualityInspectionMapper.java @@ -0,0 +1,15 @@ +package org.dromara.quality.mapper; + +import org.dromara.quality.domain.BusQualityInspection; +import org.dromara.quality.domain.vo.BusQualityInspectionVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 质量-检查工单Mapper接口 + * + * @author lcj + * @date 2025-04-16 + */ +public interface BusQualityInspectionMapper extends BaseMapperPlus { + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/service/IBusQualityConstructionLogService.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/service/IBusQualityConstructionLogService.java new file mode 100644 index 00000000..0791c7af --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/service/IBusQualityConstructionLogService.java @@ -0,0 +1,108 @@ +package org.dromara.quality.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 jakarta.servlet.http.HttpServletResponse; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.quality.domain.BusQualityConstructionLog; +import org.dromara.quality.domain.req.qualityconstructionlog.QualityConstructionLogCreateReq; +import org.dromara.quality.domain.req.qualityconstructionlog.QualityConstructionLogQueryReq; +import org.dromara.quality.domain.req.qualityconstructionlog.QualityConstructionLogUpdateReq; +import org.dromara.quality.domain.vo.BusQualityConstructionLogVo; + +import java.util.Collection; +import java.util.List; + +/** + * 质量-施工日志Service接口 + * + * @author lcj + * @date 2025-04-16 + */ +public interface IBusQualityConstructionLogService extends IService { + + /** + * 查询质量-施工日志 + * + * @param id 主键 + * @return 质量-施工日志 + */ + BusQualityConstructionLogVo queryById(Long id); + + /** + * 分页查询质量-施工日志列表 + * + * @param req 查询条件 + * @param pageQuery 分页参数 + * @return 质量-施工日志分页列表 + */ + TableDataInfo queryPageList(QualityConstructionLogQueryReq req, PageQuery pageQuery); + + /** + * 查询符合条件的质量-施工日志列表 + * + * @param req 查询条件 + * @return 质量-施工日志列表 + */ + List queryList(QualityConstructionLogQueryReq req); + + /** + * 新增质量-施工日志 + * + * @param req 质量-施工日志 + * @return 新增日志主键 + */ + Long insertByBo(QualityConstructionLogCreateReq req); + + /** + * 修改质量-施工日志 + * + * @param req 质量-施工日志 + * @return 是否修改成功 + */ + Boolean updateByBo(QualityConstructionLogUpdateReq req); + + /** + * 校验并批量删除质量-施工日志信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 获取质量-施工日志视图对象 + * + * @param qualityConstructionLog 质量-施工日志对象 + * @return 质量-施工日志视图对象 + */ + BusQualityConstructionLogVo getVo(BusQualityConstructionLog qualityConstructionLog); + + /** + * 获取质量-施工日志查询条件封装 + * + * @param req 质量-施工日志查询条件 + * @return 质量-施工日志查询条件封装 + */ + LambdaQueryWrapper buildQueryWrapper(QualityConstructionLogQueryReq req); + + /** + * 获取质量-施工日志分页对象视图 + * + * @param qualityConstructionLogPage 质量-施工日志分页对象 + * @return 质量-施工日志分页对象视图 + */ + Page getVoPage(Page qualityConstructionLogPage); + + /** + * 导出质量-施工日志 + * + * @param id 主键 + * @param response 响应 + */ + void exportWordById(Long id, HttpServletResponse response); + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/service/IBusQualityInspectionService.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/service/IBusQualityInspectionService.java new file mode 100644 index 00000000..3a519ed5 --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/service/IBusQualityInspectionService.java @@ -0,0 +1,108 @@ +package org.dromara.quality.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 jakarta.servlet.http.HttpServletResponse; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.quality.domain.BusQualityInspection; +import org.dromara.quality.domain.req.qualityinspection.QualityInspectionCreateReq; +import org.dromara.quality.domain.req.qualityinspection.QualityInspectionQueryReq; +import org.dromara.quality.domain.req.qualityinspection.QualityInspectionUpdateReq; +import org.dromara.quality.domain.vo.BusQualityInspectionVo; + +import java.util.Collection; +import java.util.List; + +/** + * 质量-检查工单Service接口 + * + * @author lcj + * @date 2025-04-16 + */ +public interface IBusQualityInspectionService extends IService { + + /** + * 查询质量-检查工单 + * + * @param id 主键 + * @return 质量-检查工单 + */ + BusQualityInspectionVo queryById(Long id); + + /** + * 分页查询质量-检查工单列表 + * + * @param req 查询条件 + * @param pageQuery 分页参数 + * @return 质量-检查工单分页列表 + */ + TableDataInfo queryPageList(QualityInspectionQueryReq req, PageQuery pageQuery); + + /** + * 查询符合条件的质量-检查工单列表 + * + * @param req 查询条件 + * @return 质量-检查工单列表 + */ + List queryList(QualityInspectionQueryReq req); + + /** + * 新增质量-检查工单 + * + * @param req 质量-检查工单 + * @return 新增检查工单id + */ + Long insertByBo(QualityInspectionCreateReq req); + + /** + * 修改质量-检查工单 + * + * @param req 质量-检查工单 + * @return 是否修改成功 + */ + Boolean updateByBo(QualityInspectionUpdateReq req); + + /** + * 校验并批量删除质量-检查工单信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 获取质量-检查工单视图对象 + * + * @param qualityInspection 质量-检查工单对象 + * @return 质量-检查工单视图对象 + */ + BusQualityInspectionVo getVo(BusQualityInspection qualityInspection); + + /** + * 获取质量-检查工单查询条件封装 + * + * @param req 质量-检查工单查询条件 + * @return 质量-检查工单查询条件封装 + */ + LambdaQueryWrapper buildQueryWrapper(QualityInspectionQueryReq req); + + /** + * 获取质量-检查工单分页对象视图 + * + * @param qualityInspectionPage 质量-检查工单分页对象 + * @return 质量-检查工单分页对象视图 + */ + Page getVoPage(Page qualityInspectionPage); + + /** + * 导出质量-检查工单 + * + * @param id 质量-检查工单id + * @param response HttpServletResponse + */ + void exportWordById(Long id, HttpServletResponse response); + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/service/impl/BusQualityConstructionLogServiceImpl.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/service/impl/BusQualityConstructionLogServiceImpl.java new file mode 100644 index 00000000..d66cb36a --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/service/impl/BusQualityConstructionLogServiceImpl.java @@ -0,0 +1,461 @@ +package org.dromara.quality.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.xwpf.usermodel.*; +import org.dromara.common.core.constant.HttpStatus; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.DateUtils; +import org.dromara.common.core.utils.ObjectUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.oss.core.OssClient; +import org.dromara.common.oss.exception.OssException; +import org.dromara.common.oss.factory.OssFactory; +import org.dromara.project.domain.BusProject; +import org.dromara.project.service.IBusProjectService; +import org.dromara.quality.constant.QualityConstant; +import org.dromara.quality.domain.BusQualityConstructionLog; +import org.dromara.quality.domain.req.qualityconstructionlog.QualityConstructionLogCreateReq; +import org.dromara.quality.domain.req.qualityconstructionlog.QualityConstructionLogQueryReq; +import org.dromara.quality.domain.req.qualityconstructionlog.QualityConstructionLogUpdateReq; +import org.dromara.quality.domain.vo.BusQualityConstructionLogVo; +import org.dromara.quality.mapper.BusQualityConstructionLogMapper; +import org.dromara.quality.service.IBusQualityConstructionLogService; +import org.dromara.system.domain.vo.SysOssVo; +import org.dromara.system.domain.vo.SysUserVo; +import org.dromara.system.service.ISysOssService; +import org.dromara.system.service.ISysUserService; +import org.dromara.utils.DocumentUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.stream.Collectors; +import java.util.zip.ZipOutputStream; + +/** + * 质量-施工日志Service业务层处理 + * + * @author lcj + * @date 2025-04-16 + */ +@Service +public class BusQualityConstructionLogServiceImpl extends ServiceImpl + implements IBusQualityConstructionLogService { + + @Resource + private IBusProjectService projectService; + + @Resource + private ISysUserService userService; + + @Resource + private ISysOssService ossService; + + /** + * 查询质量-施工日志 + * + * @param id 主键 + * @return 质量-施工日志 + */ + @Override + public BusQualityConstructionLogVo queryById(Long id) { + BusQualityConstructionLog qualityConstructionLog = this.getById(id); + if (qualityConstructionLog == null) { + throw new ServiceException("施工日志信息不存在", HttpStatus.NOT_FOUND); + } + return this.getVo(qualityConstructionLog); + } + + /** + * 分页查询质量-施工日志列表 + * + * @param req 查询条件 + * @param pageQuery 分页参数 + * @return 质量-施工日志分页列表 + */ + @Override + public TableDataInfo queryPageList(QualityConstructionLogQueryReq req, PageQuery pageQuery) { + Page result = this.page(pageQuery.build(), buildQueryWrapper(req)); + return TableDataInfo.build(this.getVoPage(result)); + } + + /** + * 查询符合条件的质量-施工日志列表 + * + * @param req 查询条件 + * @return 质量-施工日志列表 + */ + @Override + public List queryList(QualityConstructionLogQueryReq req) { + LambdaQueryWrapper lqw = buildQueryWrapper(req); + return this.list(lqw).stream().map(this::getVo).toList(); + } + + /** + * 新增质量-施工日志 + * + * @param req 质量-施工日志 + * @return 新增日志主键 + */ + @Override + public Long insertByBo(QualityConstructionLogCreateReq req) { + // 将实体类和 DTO 进行转换 + BusQualityConstructionLog qualityConstructionLog = new BusQualityConstructionLog(); + BeanUtils.copyProperties(req, qualityConstructionLog); + // 数据校验 + validEntityBeforeSave(qualityConstructionLog, true); + // 写入数据库 + boolean save = this.save(qualityConstructionLog); + if (!save) { + throw new ServiceException("新增施工日志信息失败,数据库异常", HttpStatus.ERROR); + } + // 返回新写入的数据 + return qualityConstructionLog.getId(); + } + + /** + * 修改质量-施工日志 + * + * @param req 质量-施工日志 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(QualityConstructionLogUpdateReq req) { + // 将实体类和 DTO 进行转换 + BusQualityConstructionLog qualityConstructionLog = new BusQualityConstructionLog(); + BeanUtils.copyProperties(req, qualityConstructionLog); + // 数据校验 + validEntityBeforeSave(qualityConstructionLog, false); + // 判断是否存在 + BusQualityConstructionLog oldQualityConstructionLog = this.getById(qualityConstructionLog.getId()); + if (oldQualityConstructionLog == null) { + throw new ServiceException("修改施工日志信息失败,数据不存在", HttpStatus.NOT_FOUND); + } + // 操作数据库 + return this.updateById(qualityConstructionLog); + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(BusQualityConstructionLog entity, Boolean create) { + // TODO 做一些数据校验,如唯一约束 + Long projectId = entity.getProjectId(); + if (create) { + if (projectId == null) { + throw new ServiceException("项目id不能为空", HttpStatus.BAD_REQUEST); + } + } + if (projectId != null && projectService.getById(projectId) == null) { + throw new ServiceException("对应项目不存在", HttpStatus.NOT_FOUND); + } + } + + /** + * 校验并批量删除质量-施工日志信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // TODO 做一些业务上的校验,判断是否需要校验 + } + return this.removeBatchByIds(ids); + } + + /** + * 获取质量-施工日志视图对象 + * + * @param qualityConstructionLog 质量-施工日志对象 + * @return 质量-施工日志视图对象 + */ + @Override + public BusQualityConstructionLogVo getVo(BusQualityConstructionLog qualityConstructionLog) { + // 对象转封装类 + BusQualityConstructionLogVo qualityConstructionLogVo = new BusQualityConstructionLogVo(); + if (qualityConstructionLog == null) { + return qualityConstructionLogVo; + } + BeanUtils.copyProperties(qualityConstructionLog, qualityConstructionLogVo); + // 关联项目信息 + Long projectId = qualityConstructionLog.getProjectId(); + if (projectId != null) { + qualityConstructionLogVo.setProjectName(projectService.getById(projectId).getProjectName()); + } + // 关联创建用户信息 + Long createBy = qualityConstructionLog.getCreateBy(); + if (createBy != null) { + SysUserVo sysUserVo = userService.selectUserById(createBy); + qualityConstructionLogVo.setCreateBy(sysUserVo.getNickName()); + } + // 关联附件信息 + String file = qualityConstructionLog.getFile(); + if (StringUtils.isNotBlank(file)) { + List ossIdList = Arrays.stream(file.split(",")).map(Long::parseLong).toList(); + List ossVoList = ossService.listByIds(ossIdList); + qualityConstructionLogVo.setFileList(ossVoList); + } + return qualityConstructionLogVo; + } + + /** + * 获取质量-施工日志查询条件封装 + * + * @param req 质量-施工日志查询条件 + * @return 质量-施工日志查询条件封装 + */ + @Override + public LambdaQueryWrapper buildQueryWrapper(QualityConstructionLogQueryReq req) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + if (req == null) { + return lqw; + } + Long projectId = req.getProjectId(); + Date happenDate = req.getHappenDate(); + // 精准查询 + lqw.eq(ObjectUtils.isNotEmpty(projectId), BusQualityConstructionLog::getProjectId, projectId); + lqw.eq(ObjectUtils.isNotEmpty(happenDate), BusQualityConstructionLog::getHappenDate, happenDate); + return lqw; + } + + /** + * 获取质量-施工日志分页对象视图 + * + * @param qualityConstructionLogPage 质量-施工日志分页对象 + * @return 质量-施工日志分页对象视图 + */ + @Override + public Page getVoPage(Page qualityConstructionLogPage) { + // 获取质量-施工日志列表 + List qualityConstructionLogList = qualityConstructionLogPage.getRecords(); + // 添加分页信息 + Page qualityConstructionLogVoPage = new Page<>( + qualityConstructionLogPage.getCurrent(), + qualityConstructionLogPage.getSize(), + qualityConstructionLogPage.getTotal()); + if (CollUtil.isEmpty(qualityConstructionLogList)) { + return qualityConstructionLogVoPage; + } + // 获取项目名称 + List projectIdList = qualityConstructionLogList.stream().map(BusQualityConstructionLog::getProjectId).distinct().toList(); + if (projectIdList.size() != 1) { + throw new ServiceException("仅能查询单个项目下的施工日志", HttpStatus.BAD_REQUEST); + } + BusProject project = projectService.getById(projectIdList.get(0)); + if (project == null) { + throw new ServiceException("项目不存在", HttpStatus.NOT_FOUND); + } + // 获取创建用户信息 + List createByList = qualityConstructionLogList.stream().map(BusQualityConstructionLog::getCreateBy).distinct().toList(); + List userVoList = userService.selectUserByIds(createByList, null); + Map userMap = userVoList.stream().collect(Collectors.toMap(SysUserVo::getUserId, SysUserVo::getNickName)); + // 获取附件信息 + List ossIdList = qualityConstructionLogList.stream().map(BusQualityConstructionLog::getFile).filter(StringUtils::isNotBlank) + .flatMap(fileId -> Arrays.stream(fileId.split(",")).map(Long::parseLong)).distinct().toList(); + Map> ossMap = ossService.listByIds(ossIdList) + .stream().collect(Collectors.groupingBy(SysOssVo::getOssId)); + // 对象列表 => 封装对象列表 + List qualityConstructionLogVoList = qualityConstructionLogList.stream().map(qualityConstructionLog -> { + BusQualityConstructionLogVo qualityConstructionLogVo = new BusQualityConstructionLogVo(); + // 对象转封装类 + BeanUtils.copyProperties(qualityConstructionLog, qualityConstructionLogVo); + // 项目名称 + qualityConstructionLogVo.setProjectName(project.getProjectName()); + // 创建用户姓名 + Long createBy = qualityConstructionLog.getCreateBy(); + String createByName = null; + if (userMap.containsKey(createBy)) { + createByName = userMap.get(createBy); + } + qualityConstructionLogVo.setCreateBy(createByName); + // 附件信息 + String file = qualityConstructionLog.getFile(); + List fileList = new ArrayList<>(); + if (StringUtils.isNotBlank(file)) { + List fileIdList = Arrays.stream(file.split(",")).map(Long::parseLong).toList(); + for (Long fileId : fileIdList) { + if (ossMap.containsKey(fileId)) { + fileList.add(ossMap.get(fileId).get(0)); + } + } + } + qualityConstructionLogVo.setFileList(fileList); + return qualityConstructionLogVo; + }).toList(); + qualityConstructionLogVoPage.setRecords(qualityConstructionLogVoList); + return qualityConstructionLogVoPage; + } + + /** + * 导出质量-施工日志 + * + * @param id 主键 + * @param response 响应 + */ + @Override + public void exportWordById(Long id, HttpServletResponse response) { + BusQualityConstructionLog qualityConstructionLog = this.getById(id); + if (qualityConstructionLog == null) { + throw new ServiceException("质量-施工日志不存在"); + } + Map replacementMap = getReplacementMap(qualityConstructionLog); + Path targetDir = Paths.get(QualityConstant.getQualityConstructionLogFileUrl(qualityConstructionLog)); + try (FileInputStream fis = new FileInputStream(QualityConstant.QUALITY_CONSTRUCTION_LOG_TEMPLATE_PATH); + XWPFDocument document = new XWPFDocument(fis)) { + // 替换段落中的文本 + for (XWPFParagraph paragraph : document.getParagraphs()) { + replaceInParagraph(paragraph, replacementMap, document, qualityConstructionLog); + } + // 替换表格中的文本(如果模板中有表格) + for (XWPFTable table : document.getTables()) { + for (XWPFTableRow row : table.getRows()) { + for (XWPFTableCell cell : row.getTableCells()) { + for (XWPFParagraph paragraph : cell.getParagraphs()) { + replaceInParagraph(paragraph, replacementMap, document, qualityConstructionLog); + } + } + } + } + // 创建目标目录 + if (!Files.exists(targetDir)) { + Files.createDirectories(targetDir); + } + // 组合目标文件名 + String fileName = QualityConstant.getQualityConstructionLogFileName(qualityConstructionLog); + // 保存修改后的文件 + try (FileOutputStream fos = new FileOutputStream(targetDir.resolve(fileName).toFile())) { + document.write(fos); + } + } catch (IOException | InvalidFormatException e) { + throw new OssException("生成Word文件失败,错误信息: " + e.getMessage()); + } + // 设置响应头 + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8"); + try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) { + DocumentUtils.zipDirectory(targetDir, targetDir, zos); + zos.flush(); + } catch (Exception e) { + throw new OssException("生成ZIP文件失败,错误信息: " + e.getMessage()); + } + } + + /** + * 根据实体获取Word的文本Map + * + * @param qualityConstructionLog 施工日志对象 + * @return Map + */ + public Map getReplacementMap(BusQualityConstructionLog qualityConstructionLog) { + Map replacementMap = new HashMap<>(); + String createName = userService.selectUserById(qualityConstructionLog.getCreateBy()).getNickName(); + replacementMap.put("${createName}", createName); + Date createTime = qualityConstructionLog.getCreateTime(); + replacementMap.put("${createTime}", createTime != null ? DateUtils.formatDateTime(createTime) : ""); + String projectName = projectService.getById(qualityConstructionLog.getProjectId()).getProjectName(); + replacementMap.put("${projectName}", projectName); + Date happenDate = qualityConstructionLog.getHappenDate(); + replacementMap.put("${happenDate}", happenDate != null ? DateUtils.formatDate(happenDate) : ""); + replacementMap.put("${productionStatus}", qualityConstructionLog.getProductionStatus()); + replacementMap.put("${technologyQuality}", qualityConstructionLog.getTechnologyQuality()); + replacementMap.put("${remark}", qualityConstructionLog.getRemark()); + replacementMap.put("${file}", qualityConstructionLog.getFile()); + return replacementMap; + } + + /** + * 替换段落中所有文本运行的占位符内容,对于 checkFile 和 rectificationFile + * 插入图片或超链接(附件)展示。 + * + * @param paragraph 当前段落 + * @param replacements 占位符与替换内容的映射 + * @param document 当前文档,用于插入图片或创建超链接 + */ + public void replaceInParagraph(XWPFParagraph paragraph, + Map replacements, + XWPFDocument document, + BusQualityConstructionLog qualityConstructionLog) throws InvalidFormatException, IOException { + // 先拷贝 paragraph 里所有的 run + List runs = new ArrayList<>(paragraph.getRuns()); + // 在拷贝上遍历,修改原 paragraph(增删 run)都不会抛 CME + for (XWPFRun run : runs) { + String text = run.getText(0); + if (text != null) { + // 针对每个占位符进行检查 + for (Map.Entry entry : replacements.entrySet()) { + String placeholder = entry.getKey(); + String value = entry.getValue(); + if (text.contains(placeholder)) { + // 针对 file 进行特殊处理 + if (placeholder.equals("${file}")) { + // 判断该 run 中的文本是否仅包含该占位符(建议模板中独占一行) + if (text.trim().equals(placeholder)) { + // 清空原有文本 + run.setText("", 0); + // 根据附件的后缀决定以图片或超链接展示 + if (StringUtils.isBlank(value)) { + continue; + } + // 获取附件的ossId + List ossIdList = Arrays.stream(value.split(",")).map(Long::parseLong).toList(); + List ossVoList = ossService.listByIds(ossIdList); + String baseFile = QualityConstant.getQualityConstructionLogFileUrl(qualityConstructionLog) + "/file"; + for (SysOssVo ossVo : ossVoList) { + String lowerVal = ossVo.getUrl().toLowerCase(); + OssClient storage = OssFactory.instance(ossVo.getService()); + String fileDownload = storage.fileDownload(ossVo.getUrl(), ossVo.getOriginalName(), baseFile); + if (lowerVal.endsWith(".png") || lowerVal.endsWith(".jpg") || lowerVal.endsWith(".jpeg") || lowerVal.endsWith(".gif")) { + try { + DocumentUtils.insertImageDynamic(run, fileDownload, document, 300); + } catch (Exception e) { + throw new ServiceException("插入图片失败"); + } + } else { + // —— 非图片:插入超链接 —— + XWPFHyperlinkRun link = paragraph.createHyperlinkRun(ossVo.getUrl()); + link.setText(ossVo.getOriginalName()); + link.setColor("0000FF"); + link.setUnderline(UnderlinePatterns.SINGLE); + } + } + } else { + // 如果占位符与其它文本混合,可进行文本替换,提示附件展示请单独使用占位符 + text = text.replace(placeholder, "[附件]"); + run.setText(text, 0); + } + } else { + // 普通文本占位符直接替换 + if (StringUtils.isBlank(value)) { + // 如果填入值为空,清空原有文本 + run.setText("", 0); + continue; + } + text = text.replace(placeholder, value); + run.setText(text, 0); + } + } + } + } + } + } + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/service/impl/BusQualityInspectionServiceImpl.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/service/impl/BusQualityInspectionServiceImpl.java new file mode 100644 index 00000000..e563f4d7 --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/quality/service/impl/BusQualityInspectionServiceImpl.java @@ -0,0 +1,524 @@ +package org.dromara.quality.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.xwpf.usermodel.*; +import org.dromara.common.core.constant.HttpStatus; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.DateUtils; +import org.dromara.common.core.utils.ObjectUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.oss.core.OssClient; +import org.dromara.common.oss.exception.OssException; +import org.dromara.common.oss.factory.OssFactory; +import org.dromara.project.domain.BusConstructionUser; +import org.dromara.project.domain.BusProject; +import org.dromara.project.service.IBusConstructionUserService; +import org.dromara.project.service.IBusProjectService; +import org.dromara.quality.constant.QualityConstant; +import org.dromara.quality.domain.BusQualityInspection; +import org.dromara.quality.domain.enums.QualityInspectionStatusEnum; +import org.dromara.quality.domain.req.qualityinspection.QualityInspectionCreateReq; +import org.dromara.quality.domain.req.qualityinspection.QualityInspectionQueryReq; +import org.dromara.quality.domain.req.qualityinspection.QualityInspectionUpdateReq; +import org.dromara.quality.domain.vo.BusQualityInspectionVo; +import org.dromara.quality.mapper.BusQualityInspectionMapper; +import org.dromara.quality.service.IBusQualityInspectionService; +import org.dromara.system.domain.vo.SysOssVo; +import org.dromara.system.domain.vo.SysUserVo; +import org.dromara.system.service.ISysDictDataService; +import org.dromara.system.service.ISysOssService; +import org.dromara.system.service.ISysUserService; +import org.dromara.utils.DocumentUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.stream.Collectors; +import java.util.zip.ZipOutputStream; + +/** + * 质量-检查工单Service业务层处理 + * + * @author lcj + * @date 2025-04-16 + */ +@Service +public class BusQualityInspectionServiceImpl extends ServiceImpl + implements IBusQualityInspectionService { + + @Resource + private IBusProjectService projectService; + + @Resource + private ISysUserService userService; + + @Resource + private ISysOssService ossService; + + @Resource + private IBusConstructionUserService constructionUserService; + + @Resource + private ISysDictDataService dictDataService; + + /** + * 查询质量-检查工单 + * + * @param id 主键 + * @return 质量-检查工单 + */ + @Override + public BusQualityInspectionVo queryById(Long id) { + BusQualityInspection qualityInspection = this.getById(id); + if (qualityInspection == null) { + throw new ServiceException("检查工单信息不存在", HttpStatus.NOT_FOUND); + } + return this.getVo(qualityInspection); + } + + /** + * 分页查询质量-检查工单列表 + * + * @param req 查询条件 + * @param pageQuery 分页参数 + * @return 质量-检查工单分页列表 + */ + @Override + public TableDataInfo queryPageList(QualityInspectionQueryReq req, PageQuery pageQuery) { + Page result = this.page(pageQuery.build(), buildQueryWrapper(req)); + return TableDataInfo.build(this.getVoPage(result)); + } + + /** + * 查询符合条件的质量-检查工单列表 + * + * @param req 查询条件 + * @return 质量-检查工单列表 + */ + @Override + public List queryList(QualityInspectionQueryReq req) { + LambdaQueryWrapper lqw = buildQueryWrapper(req); + return this.list(lqw).stream().map(this::getVo).toList(); + } + + /** + * 新增质量-检查工单 + * + * @param req 质量-检查工单 + * @return 新增检查工单id + */ + @Override + public Long insertByBo(QualityInspectionCreateReq req) { + // 将实体类和 DTO 进行转换 + BusQualityInspection qualityInspection = new BusQualityInspection(); + BeanUtils.copyProperties(req, qualityInspection); + // 数据校验 + validEntityBeforeSave(qualityInspection, true); + // 填充默认值 + qualityInspection.setInspectionStatus(QualityInspectionStatusEnum.INFORM.getValue()); + // 写入数据库 + boolean save = this.save(qualityInspection); + if (!save) { + throw new ServiceException("新增检查工单信息失败,数据库异常", HttpStatus.ERROR); + } + // 返回新写入的数据 + return qualityInspection.getId(); + } + + /** + * 修改质量-检查工单 + * + * @param req 质量-检查工单 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(QualityInspectionUpdateReq req) { + // 将实体类和 DTO 进行转换 + BusQualityInspection qualityInspection = new BusQualityInspection(); + BeanUtils.copyProperties(req, qualityInspection); + // 数据校验 + validEntityBeforeSave(qualityInspection, false); + // 判断是否存在 + BusQualityInspection oldQualityInspection = this.getById(qualityInspection.getId()); + if (oldQualityInspection == null) { + throw new ServiceException("修改检查工单信息失败,数据不存在", HttpStatus.NOT_FOUND); + } + // 操作数据库 + return this.updateById(qualityInspection); + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(BusQualityInspection entity, Boolean create) { + // TODO 做一些数据校验,如唯一约束 + Long projectId = entity.getProjectId(); + if (create) { + if (projectId == null) { + throw new ServiceException("项目id不能为空", HttpStatus.BAD_REQUEST); + } + } + if (projectId != null && projectService.getById(projectId) == null) { + throw new ServiceException("对应项目不存在", HttpStatus.NOT_FOUND); + } + } + + /** + * 校验并批量删除质量-检查工单信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // TODO 做一些业务上的校验,判断是否需要校验 + } + return this.removeBatchByIds(ids); + } + + /** + * 获取质量-检查工单视图对象 + * + * @param qualityInspection 质量-检查工单对象 + * @return 质量-检查工单视图对象 + */ + @Override + public BusQualityInspectionVo getVo(BusQualityInspection qualityInspection) { + // 对象转封装类 + BusQualityInspectionVo qualityInspectionVo = new BusQualityInspectionVo(); + if (qualityInspection == null) { + return qualityInspectionVo; + } + BeanUtils.copyProperties(qualityInspection, qualityInspectionVo); + // 关联项目信息 + Long projectId = qualityInspection.getProjectId(); + if (projectId != null) { + qualityInspectionVo.setProjectName(projectService.getById(projectId).getProjectName()); + } + // 关联创建用户信息 + Long createBy = qualityInspection.getCreateBy(); + if (createBy != null) { + SysUserVo sysUserVo = userService.selectUserById(createBy); + qualityInspectionVo.setCreateBy(sysUserVo.getNickName()); + } + // 关联整改人信息 + Long corrector = qualityInspection.getCorrector(); + if (corrector != null) { + String userName = constructionUserService.getById(corrector).getUserName(); + qualityInspectionVo.setCorrectorName(userName); + } + // 关联附件信息 + String inspectionFile = qualityInspection.getInspectionFile(); + if (StringUtils.isNotBlank(inspectionFile)) { + List ossIdList = Arrays.stream(inspectionFile.split(",")).map(Long::parseLong).toList(); + List ossVoList = ossService.listByIds(ossIdList); + qualityInspectionVo.setInspectionFileList(ossVoList); + } + String rectificationFile = qualityInspection.getRectificationFile(); + if (StringUtils.isNotBlank(rectificationFile)) { + List ossIdList = Arrays.stream(rectificationFile.split(",")).map(Long::parseLong).toList(); + List ossVoList = ossService.listByIds(ossIdList); + qualityInspectionVo.setRectificationFileList(ossVoList); + } + return qualityInspectionVo; + } + + /** + * 获取质量-检查工单查询条件封装 + * + * @param req 质量-检查工单查询条件 + * @return 质量-检查工单查询条件封装 + */ + @Override + public LambdaQueryWrapper buildQueryWrapper(QualityInspectionQueryReq req) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + if (req == null) { + return lqw; + } + Long projectId = req.getProjectId(); + String inspectionType = req.getInspectionType(); + String inspectionStatus = req.getInspectionStatus(); + Long teamId = req.getTeamId(); + // 精确查询 + lqw.eq(ObjectUtils.isNotEmpty(projectId), BusQualityInspection::getProjectId, projectId); + lqw.eq(ObjectUtils.isNotEmpty(inspectionType), BusQualityInspection::getInspectionType, inspectionType); + lqw.eq(ObjectUtils.isNotEmpty(inspectionStatus), BusQualityInspection::getInspectionStatus, inspectionStatus); + lqw.eq(ObjectUtils.isNotEmpty(teamId), BusQualityInspection::getTeamId, teamId); + return lqw; + } + + /** + * 获取质量-检查工单分页对象视图 + * + * @param qualityInspectionPage 质量-检查工单分页对象 + * @return 质量-检查工单分页对象视图 + */ + @Override + public Page getVoPage(Page qualityInspectionPage) { + // 获取质量-检查工单列表 + List qualityInspectionList = qualityInspectionPage.getRecords(); + // 添加分页信息 + Page qualityInspectionVoPage = new Page<>( + qualityInspectionPage.getCurrent(), + qualityInspectionPage.getSize(), + qualityInspectionPage.getTotal()); + if (CollUtil.isEmpty(qualityInspectionList)) { + return qualityInspectionVoPage; + } + // 获取项目名称 + List projectIdList = qualityInspectionList.stream().map(BusQualityInspection::getProjectId).distinct().toList(); + if (projectIdList.size() != 1) { + throw new ServiceException("仅能查询单个项目下的施工日志", HttpStatus.BAD_REQUEST); + } + BusProject project = projectService.getById(projectIdList.get(0)); + if (project == null) { + throw new ServiceException("项目不存在", HttpStatus.NOT_FOUND); + } + // 获取整改人信息 + List correctorList = qualityInspectionList.stream().map(BusQualityInspection::getCorrector).distinct().toList(); + Map correctorMap = constructionUserService.listByIds(correctorList) + .stream().collect(Collectors.toMap(BusConstructionUser::getId, BusConstructionUser::getUserName)); + // 获取创建用户信息 + List createByList = qualityInspectionList.stream().map(BusQualityInspection::getCreateBy).distinct().toList(); + List userVoList = userService.selectUserByIds(createByList, null); + Map userMap = userVoList.stream().collect(Collectors.toMap(SysUserVo::getUserId, SysUserVo::getNickName)); + // 获取附件信息 + List ossInspectionFileIdList = qualityInspectionList.stream().map(BusQualityInspection::getInspectionFile).filter(StringUtils::isNotBlank) + .flatMap(fileId -> Arrays.stream(fileId.split(",")).map(Long::parseLong)).distinct().toList(); + List ossIdList = new ArrayList<>(ossInspectionFileIdList); + List ossRectificationFileIdList = qualityInspectionList.stream().map(BusQualityInspection::getRectificationFile).filter(StringUtils::isNotBlank) + .flatMap(fileId -> Arrays.stream(fileId.split(",")).map(Long::parseLong)).distinct().toList(); + ossIdList.addAll(ossRectificationFileIdList); + Map> ossMap = ossService.listByIds(ossIdList) + .stream().collect(Collectors.groupingBy(SysOssVo::getOssId)); + // 对象列表 => 封装对象列表 + List qualityInspectionVoList = qualityInspectionList.stream().map(qualityInspection -> { + BusQualityInspectionVo qualityInspectionVo = new BusQualityInspectionVo(); + BeanUtils.copyProperties(qualityInspection, qualityInspectionVo); + qualityInspectionVo.setProjectName(project.getProjectName()); + // 关联整改人信息 + Long corrector = qualityInspection.getCorrector(); + String correctorName = null; + if (correctorMap.containsKey(corrector)) { + correctorName = correctorMap.get(corrector); + } + qualityInspectionVo.setCorrectorName(correctorName); + // 关联创建用户信息 + Long createBy = qualityInspection.getCreateBy(); + String createByName = null; + if (userMap.containsKey(createBy)) { + createByName = userMap.get(createBy); + } + qualityInspectionVo.setCreateBy(createByName); + // 关联附件信息 + String inspectionFile = qualityInspection.getInspectionFile(); + List inspectionFileList = new ArrayList<>(); + if (StringUtils.isNotBlank(inspectionFile)) { + List inspectionFileIdList = Arrays.stream(inspectionFile.split(",")).map(Long::parseLong).toList(); + for (Long id : inspectionFileIdList) { + if (ossMap.containsKey(id)) { + inspectionFileList.add(ossMap.get(id).get(0)); + } + } + } + qualityInspectionVo.setInspectionFileList(inspectionFileList); + String rectificationFile = qualityInspection.getRectificationFile(); + List rectificationFileList = new ArrayList<>(); + if (StringUtils.isNotBlank(rectificationFile)) { + List rectificationFileIdList = Arrays.stream(rectificationFile.split(",")).map(Long::parseLong).toList(); + for (Long id : rectificationFileIdList) { + if (ossMap.containsKey(id)) { + rectificationFileList.add(ossMap.get(id).get(0)); + } + } + } + qualityInspectionVo.setRectificationFileList(rectificationFileList); + return qualityInspectionVo; + }).toList(); + qualityInspectionVoPage.setRecords(qualityInspectionVoList); + return qualityInspectionVoPage; + } + + /** + * 导出质量-检查工单 + * + * @param id 质量-检查工单id + * @param response HttpServletResponse + */ + @Override + public void exportWordById(Long id, HttpServletResponse response) { + BusQualityInspection qualityInspection = this.getById(id); + if (qualityInspection == null) { + throw new ServiceException("质量-检查工单不存在"); + } + Map replacementMap = getReplacementMap(qualityInspection); + Path targetDir = Paths.get(QualityConstant.getQualityInspectionFileUrl(qualityInspection)); + try (FileInputStream fis = new FileInputStream(QualityConstant.QUALITY_INSPECTION_TEMPLATE_PATH); + XWPFDocument document = new XWPFDocument(fis)) { + // 替换段落中的文本 + for (XWPFParagraph paragraph : document.getParagraphs()) { + replaceInParagraph(paragraph, replacementMap, document, qualityInspection); + } + // 替换表格中的文本(如果模板中有表格) + for (XWPFTable table : document.getTables()) { + for (XWPFTableRow row : table.getRows()) { + for (XWPFTableCell cell : row.getTableCells()) { + for (XWPFParagraph paragraph : cell.getParagraphs()) { + replaceInParagraph(paragraph, replacementMap, document, qualityInspection); + } + } + } + } + // 创建目标目录 + if (!Files.exists(targetDir)) { + Files.createDirectories(targetDir); + } + // 组合目标文件名 + String fileName = QualityConstant.getQualityInspectionFileName(qualityInspection); + // 保存修改后的文件 + try (FileOutputStream fos = new FileOutputStream(targetDir.resolve(fileName).toFile())) { + document.write(fos); + } + } catch (IOException | InvalidFormatException e) { + throw new OssException("生成Word文件失败,错误信息: " + e.getMessage()); + } + // 设置响应头 + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8"); + try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) { + DocumentUtils.zipDirectory(targetDir, targetDir, zos); + zos.flush(); + } catch (Exception e) { + throw new OssException("生成ZIP文件失败,错误信息: " + e.getMessage()); + } + } + + /** + * 根据实体获取Word的文本Map + * + * @param qualityInspection 质量-检查工单对象 + * @return Map + */ + public Map getReplacementMap(BusQualityInspection qualityInspection) { + Map replacementMap = new HashMap<>(); + String createName = userService.selectUserById(qualityInspection.getCreateBy()).getNickName(); + replacementMap.put("${createName}", createName); + Date createTime = qualityInspection.getCreateTime(); + replacementMap.put("${createTime}", createTime != null ? DateUtils.formatDateTime(createTime) : ""); + String projectName = projectService.getById(qualityInspection.getProjectId()).getProjectName(); + replacementMap.put("${projectName}", projectName); + replacementMap.put("${inspectionHeadline}", qualityInspection.getInspectionHeadline()); + String correctorName = constructionUserService.getById(qualityInspection.getCorrector()).getUserName(); + replacementMap.put("${correctorName}", correctorName); + Date rectificationTime = qualityInspection.getRectificationTime(); + replacementMap.put("${rectificationTime}", rectificationTime != null ? DateUtils.formatDateTime(rectificationTime) : ""); + String inspectionType = dictDataService.selectDictLabel(QualityConstant.QUALITY_INSPECTION_CHECK_TYPE, qualityInspection.getInspectionType()); + replacementMap.put("${inspectionType}", inspectionType); + Date replyPeriodDate = qualityInspection.getReplyPeriodDate(); + replacementMap.put("${replyPeriodDate}", replyPeriodDate != null ? DateUtils.formatDate(replyPeriodDate) : ""); + replacementMap.put("${inspectionResult}", qualityInspection.getInspectionResult()); + replacementMap.put("${inspectionFile}", qualityInspection.getInspectionFile()); + String inspectionStatus = dictDataService.selectDictLabel(QualityConstant.QUALITY_INSPECTION_STATUS_TYPE, qualityInspection.getInspectionStatus()); + replacementMap.put("${inspectionStatus}", inspectionStatus); + replacementMap.put("${rectificationResult}", qualityInspection.getRectificationResult()); + replacementMap.put("${rectificationFile}", qualityInspection.getRectificationFile()); + replacementMap.put("${verificationResult}", qualityInspection.getVerificationResult()); + return replacementMap; + } + + /** + * 替换段落中所有文本运行的占位符内容,对于 checkFile 和 rectificationFile + * 插入图片或超链接(附件)展示。 + * + * @param paragraph 当前段落 + * @param replacements 占位符与替换内容的映射 + * @param document 当前文档,用于插入图片或创建超链接 + */ + public void replaceInParagraph(XWPFParagraph paragraph, + Map replacements, + XWPFDocument document, + BusQualityInspection qualityInspection) + throws InvalidFormatException, IOException { + // 先拷贝 paragraph 里所有的 run + List runs = new ArrayList<>(paragraph.getRuns()); + // 在拷贝上遍历,修改原 paragraph(增删 run)都不会抛 CME + for (XWPFRun run : runs) { + String text = run.getText(0); + if (text != null) { + // 针对每个占位符进行检查 + for (Map.Entry entry : replacements.entrySet()) { + String placeholder = entry.getKey(); + String value = entry.getValue(); + if (text.contains(placeholder)) { + // 针对 file 进行特殊处理 + if (placeholder.equals("${inspectionFile}") || placeholder.equals("${rectificationFile}")) { + // 判断该 run 中的文本是否仅包含该占位符(建议模板中独占一行) + if (text.trim().equals(placeholder)) { + // 清空原有文本 + run.setText("", 0); + // 根据附件的后缀决定以图片或超链接展示 + if (StringUtils.isBlank(value)) { + continue; + } + // 获取附件的ossId + List ossIdList = Arrays.stream(value.split(",")).map(Long::parseLong).toList(); + List ossVoList = ossService.listByIds(ossIdList); + String baseFile = QualityConstant.getQualityInspectionFileUrl(qualityInspection) + "/file"; + for (SysOssVo ossVo : ossVoList) { + String lowerVal = ossVo.getUrl().toLowerCase(); + OssClient storage = OssFactory.instance(ossVo.getService()); + String fileDownload = storage.fileDownload(ossVo.getUrl(), ossVo.getOriginalName(), baseFile); + if (lowerVal.endsWith(".png") || lowerVal.endsWith(".jpg") || lowerVal.endsWith(".jpeg") || lowerVal.endsWith(".gif")) { + try { + DocumentUtils.insertImageDynamic(run, fileDownload, document, 300); + } catch (Exception e) { + throw new ServiceException("插入图片失败"); + } + } else { + // —— 非图片:插入超链接 —— + XWPFHyperlinkRun link = paragraph.createHyperlinkRun(ossVo.getUrl()); + link.setText(ossVo.getOriginalName()); + link.setColor("0000FF"); + link.setUnderline(UnderlinePatterns.SINGLE); + } + } + } else { + // 如果占位符与其它文本混合,可进行文本替换,提示附件展示请单独使用占位符 + text = text.replace(placeholder, "[附件]"); + run.setText(text, 0); + } + } else { + // 普通文本占位符直接替换 + if (StringUtils.isBlank(value)) { + // 如果填入值为空,清空原有文本 + run.setText("", 0); + continue; + } + text = text.replace(placeholder, value); + run.setText(text, 0); + } + } + } + } + } + } + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/constant/SafetyConstant.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/constant/SafetyConstant.java new file mode 100644 index 00000000..671e2f18 --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/constant/SafetyConstant.java @@ -0,0 +1,34 @@ +package org.dromara.safety.constant; + +import org.dromara.common.core.utils.DateUtils; +import org.dromara.safety.domain.BusSafetyInspection; + +import java.text.SimpleDateFormat; + +/** + * @author lcj + * @date 2025/4/16 23:09 + */ +public interface SafetyConstant { + + String SAFETY_INSPECTION_TYPE = "safety_inspection_type"; + + String SAFETY_INSPECTION_CHECK_TYPE = "safety_inspection_check_type"; + + String SAFETY_INSPECTION_VIOLATION_TYPE = "safety_inspection_violation_type"; + + String SAFETY_INSPECTION_FILE_URL = "docs/safety/inspection/"; + + String SAFETY_INSPECTION_TEMPLATE_PATH = "docs/template/安全生产监督检查通知书模版.docx"; + + static String getSafetyInspectionFileUrl(BusSafetyInspection safetyInspection) { + String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(safetyInspection.getUpdateTime()); + return String.format("%s%s/%s", SAFETY_INSPECTION_FILE_URL, safetyInspection.getId(), timestamp); + } + + static String getSafetyInspectionFileName(BusSafetyInspection safetyInspection) { + String createDate = DateUtils.formatDate(safetyInspection.getCreateTime()); + return String.format("安全生产监督检查通知书(%s).docx", createDate); + } + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/controller/BusSafetyInspectionController.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/controller/BusSafetyInspectionController.java index 77e2bbf1..a3d866bd 100644 --- a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/controller/BusSafetyInspectionController.java +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/controller/BusSafetyInspectionController.java @@ -59,6 +59,17 @@ public class BusSafetyInspectionController extends BaseController { ExcelUtil.exportExcel(list, "安全巡检工单", BusSafetyInspectionVo.class, response); } + /** + * 根据主键导出安全巡检工单 + */ + @SaCheckPermission("safety:safetyInspection:export") + @Log(title = "安全巡检工单", businessType = BusinessType.EXPORT) + @PostMapping("/export/word") + public void exportWordById(@NotNull(message = "主键不能为空") Long id, + HttpServletResponse response) { + busSafetyInspectionService.exportWordById(id, response); + } + /** * 获取安全巡检工单详细信息 * diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/req/safetyinspection/SafetyInspectionQueryReq.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/req/safetyinspection/SafetyInspectionQueryReq.java index 8b33a0d6..c4c60bd2 100644 --- a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/req/safetyinspection/SafetyInspectionQueryReq.java +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/req/safetyinspection/SafetyInspectionQueryReq.java @@ -4,7 +4,6 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; -import java.util.Date; /** * @author lcj @@ -16,11 +15,6 @@ public class SafetyInspectionQueryReq implements Serializable { @Serial private static final long serialVersionUID = 8880866939746311233L; - /** - * 主键ID - */ - private Long id; - /** * 父id(默认为0) */ @@ -41,11 +35,6 @@ public class SafetyInspectionQueryReq implements Serializable { */ private String violationType; - /** - * 巡检结果 - */ - private String inspectionResult; - /** * 整改班组id */ @@ -56,59 +45,14 @@ public class SafetyInspectionQueryReq implements Serializable { */ private Long correctorId; - /** - * 是否回复(1回复 2不回复) - */ - private String isReply; - - /** - * 回复日期 - */ - private String replyDate; - /** * 工单状态(1通知 2整改 3复查) */ private String status; - /** - * 问题隐患 - */ - private String hiddenDanger; - - /** - * 整改措施 - */ - private String measure; - - /** - * 复查情况 - */ - private String review; - /** * 复查状态(1通过 2未通过) */ private String reviewType; - /** - * 检查时间 - */ - private Date checkTime; - - /** - * 整改时间 - */ - private Date rectificationTime; - - /** - * 复查时间 - */ - private Date reviewTime; - - /** - * 备注 - */ - private String remark; - } diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/IBusSafetyInspectionService.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/IBusSafetyInspectionService.java index 72e0adf1..4f6107f6 100644 --- a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/IBusSafetyInspectionService.java +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/IBusSafetyInspectionService.java @@ -3,6 +3,7 @@ package org.dromara.safety.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 jakarta.servlet.http.HttpServletResponse; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.safety.domain.BusSafetyInspection; @@ -96,4 +97,12 @@ public interface IBusSafetyInspectionService extends IService getVoPage(Page safetyInspectionPage); + /** + * 根据id导出word + * + * @param id 主键 + * @param response 响应 + */ + void exportWordById(Long id, HttpServletResponse response); + } diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/impl/BusSafetyInspectionServiceImpl.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/impl/BusSafetyInspectionServiceImpl.java index 732fa510..487a39ea 100644 --- a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/impl/BusSafetyInspectionServiceImpl.java +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/impl/BusSafetyInspectionServiceImpl.java @@ -6,17 +6,25 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.xwpf.usermodel.*; import org.dromara.common.core.constant.HttpStatus; import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.DateUtils; import org.dromara.common.core.utils.ObjectUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.oss.core.OssClient; +import org.dromara.common.oss.exception.OssException; +import org.dromara.common.oss.factory.OssFactory; import org.dromara.project.domain.BusConstructionUser; import org.dromara.project.domain.BusProjectTeam; import org.dromara.project.service.IBusConstructionUserService; import org.dromara.project.service.IBusProjectService; import org.dromara.project.service.IBusProjectTeamService; +import org.dromara.safety.constant.SafetyConstant; import org.dromara.safety.domain.BusSafetyInspection; import org.dromara.safety.domain.req.safetyinspection.SafetyInspectionCreateReq; import org.dromara.safety.domain.req.safetyinspection.SafetyInspectionQueryReq; @@ -24,15 +32,26 @@ import org.dromara.safety.domain.req.safetyinspection.SafetyInspectionUpdateReq; import org.dromara.safety.domain.vo.BusSafetyInspectionVo; import org.dromara.safety.mapper.BusSafetyInspectionMapper; import org.dromara.safety.service.IBusSafetyInspectionService; +import org.dromara.system.domain.vo.SysOssVo; import org.dromara.system.domain.vo.SysUserVo; +import org.dromara.system.service.ISysDictDataService; +import org.dromara.system.service.ISysOssService; import org.dromara.system.service.ISysUserService; +import org.dromara.utils.DocumentUtils; import org.springframework.beans.BeanUtils; +import org.springframework.core.io.ResourceLoader; +import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.Collection; -import java.util.Date; -import java.util.List; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.zip.ZipOutputStream; /** * 安全巡检工单Service业务层处理 @@ -56,6 +75,15 @@ public class BusSafetyInspectionServiceImpl extends ServiceImpl replacementMap = getReplacementMap(safetyInspection); + Path targetDir = Paths.get(SafetyConstant.getSafetyInspectionFileUrl(safetyInspection)); + try (FileInputStream fis = new FileInputStream(SafetyConstant.SAFETY_INSPECTION_TEMPLATE_PATH); + XWPFDocument document = new XWPFDocument(fis)) { + // 替换段落中的文本 + for (XWPFParagraph paragraph : document.getParagraphs()) { + replaceInParagraph(paragraph, replacementMap, document, safetyInspection); + } + // 替换表格中的文本(如果模板中有表格) + for (XWPFTable table : document.getTables()) { + for (XWPFTableRow row : table.getRows()) { + for (XWPFTableCell cell : row.getTableCells()) { + for (XWPFParagraph paragraph : cell.getParagraphs()) { + replaceInParagraph(paragraph, replacementMap, document, safetyInspection); + } + } + } + } + // 创建目标目录 + if (!Files.exists(targetDir)) { + Files.createDirectories(targetDir); + } + // 组合目标文件名 + String fileName = SafetyConstant.getSafetyInspectionFileName(safetyInspection); + // 保存修改后的文件 + try (FileOutputStream fos = new FileOutputStream(targetDir.resolve(fileName).toFile())) { + document.write(fos); + } + } catch (IOException | InvalidFormatException e) { + throw new OssException("生成Word文件失败,错误信息: " + e.getMessage()); + } + // 设置响应头 + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8"); + try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) { + DocumentUtils.zipDirectory(targetDir, targetDir, zos); + zos.flush(); + } catch (Exception e) { + throw new OssException("生成ZIP文件失败,错误信息: " + e.getMessage()); + } + } + + /** + * 根据实体获取Word的文本Map + * + * @param safetyInspection 安全巡检工单对象 + * @return Map + */ + public Map getReplacementMap(BusSafetyInspection safetyInspection) { + Map replacementMap = new HashMap<>(); + String createBy = userService.selectUserById(safetyInspection.getCreateBy()).getNickName(); + replacementMap.put("${createBy}", createBy); + Date createTime = safetyInspection.getCreateTime(); + replacementMap.put("${createTime}", createTime != null ? DateUtils.formatDateTime(createTime) : ""); + String projectName = projectService.getById(safetyInspection.getProjectId()).getProjectName(); + replacementMap.put("${projectName}", projectName); + String checkType = dictDataService.selectDictLabel(SafetyConstant.SAFETY_INSPECTION_CHECK_TYPE, safetyInspection.getCheckType()); + replacementMap.put("${checkType}", checkType); + String violationType = dictDataService.selectDictLabel(SafetyConstant.SAFETY_INSPECTION_VIOLATION_TYPE, safetyInspection.getViolationType()); + replacementMap.put("${violationType}", violationType); + Date checkTime = safetyInspection.getCheckTime(); + replacementMap.put("${checkTime}", checkTime != null ? DateUtils.formatDateTime(checkTime) : ""); + String correctorName = constructionUserService.getById(safetyInspection.getCorrectorId()).getUserName(); + replacementMap.put("${correctorName}", correctorName); + replacementMap.put("${replyDate}", safetyInspection.getReplyDate()); + replacementMap.put("${hiddenDanger}", safetyInspection.getHiddenDanger()); + replacementMap.put("${checkFile}", safetyInspection.getCheckFile()); + String status = dictDataService.selectDictLabel(SafetyConstant.SAFETY_INSPECTION_TYPE, safetyInspection.getStatus()); + replacementMap.put("${status}", status); + String teamName = projectTeamService.getById(safetyInspection.getTeamId()).getTeamName(); + replacementMap.put("${teamName}", teamName); + Date rectificationTime = safetyInspection.getRectificationTime(); + replacementMap.put("${rectificationTime}", rectificationTime != null ? DateUtils.formatDateTime(rectificationTime) : ""); + replacementMap.put("${measure}", safetyInspection.getMeasure()); + replacementMap.put("${rectificationFile}", safetyInspection.getRectificationFile()); + Date reviewTime = safetyInspection.getReviewTime(); + replacementMap.put("${reviewTime}", reviewTime != null ? DateUtils.formatDateTime(reviewTime) : ""); + replacementMap.put("${review}", safetyInspection.getReview()); + return replacementMap; + } + + /** + * 替换段落中所有文本运行的占位符内容,对于 checkFile 和 rectificationFile + * 插入图片或超链接(附件)展示。 + * + * @param paragraph 当前段落 + * @param replacements 占位符与替换内容的映射 + * @param document 当前文档,用于插入图片或创建超链接 + */ + public void replaceInParagraph(XWPFParagraph paragraph, Map replacements, XWPFDocument document, BusSafetyInspection safetyInspection) throws InvalidFormatException, IOException { + // 先拷贝 paragraph 里所有的 run + List runs = new ArrayList<>(paragraph.getRuns()); + // 在拷贝上遍历,修改原 paragraph(增删 run)都不会抛 CME + for (XWPFRun run : runs) { + String text = run.getText(0); + if (text != null) { + // 针对每个占位符进行检查 + for (Map.Entry entry : replacements.entrySet()) { + String placeholder = entry.getKey(); + String value = entry.getValue(); + if (text.contains(placeholder)) { + // 针对 checkFile 和 rectificationFile 进行特殊处理 + if (placeholder.equals("${checkFile}") || placeholder.equals("${rectificationFile}")) { + // 判断该 run 中的文本是否仅包含该占位符(建议模板中独占一行) + if (text.trim().equals(placeholder)) { + // 清空原有文本 + run.setText("", 0); + // 根据附件的后缀决定以图片或超链接展示 + if (StringUtils.isBlank(value)) { + continue; + } + // 获取附件的ossId + List ossIdList = Arrays.stream(value.split(",")).map(Long::parseLong).toList(); + List ossVoList = ossService.listByIds(ossIdList); + String baseFile = SafetyConstant.getSafetyInspectionFileUrl(safetyInspection) + "/file"; + for (SysOssVo ossVo : ossVoList) { + String lowerVal = ossVo.getUrl().toLowerCase(); + OssClient storage = OssFactory.instance(ossVo.getService()); + String fileDownload = storage.fileDownload(ossVo.getUrl(), ossVo.getOriginalName(), baseFile); + if (lowerVal.endsWith(".png") || lowerVal.endsWith(".jpg") || lowerVal.endsWith(".jpeg") || lowerVal.endsWith(".gif")) { + try { + DocumentUtils.insertImageDynamic(run, fileDownload, document, 300); + } catch (Exception e) { + throw new ServiceException("插入图片失败"); + } + } else { + // —— 非图片:插入超链接 —— + XWPFHyperlinkRun link = paragraph.createHyperlinkRun(ossVo.getUrl()); + link.setText(ossVo.getOriginalName()); + link.setColor("0000FF"); + link.setUnderline(UnderlinePatterns.SINGLE); + } + } + } else { + // 如果占位符与其它文本混合,可进行文本替换,提示附件展示请单独使用占位符 + text = text.replace(placeholder, "[附件]"); + run.setText(text, 0); + } + } else { + // 普通文本占位符直接替换 + if (StringUtils.isBlank(value)) { + // 如果填入值为空,清空原有文本 + run.setText("", 0); + continue; + } + text = text.replace(placeholder, value); + run.setText(text, 0); + } + } + } + } + } + } + } diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/impl/BusSafetyLogServiceImpl.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/impl/BusSafetyLogServiceImpl.java index 410fabf9..620821b7 100644 --- a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/impl/BusSafetyLogServiceImpl.java +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/impl/BusSafetyLogServiceImpl.java @@ -181,7 +181,7 @@ public class BusSafetyLogServiceImpl extends ServiceImpl 0 && widthPx > maxWidthPx) { + double ratio = (double) maxWidthPx / widthPx; + widthPx = maxWidthPx; + heightPx = (int) (heightPx * ratio); + } + // 4. 把像素转换成 EMU + int widthEmu = Units.pixelToEMU(widthPx); + int heightEmu = Units.pixelToEMU(heightPx); + // 5. 插入图片 + String lower = imagePath.toLowerCase(); + int format = lower.endsWith(".png") ? XWPFDocument.PICTURE_TYPE_PNG + : lower.endsWith(".gif") ? XWPFDocument.PICTURE_TYPE_GIF + : lower.endsWith(".jpeg") ? XWPFDocument.PICTURE_TYPE_JPEG + : lower.endsWith(".jpg") ? XWPFDocument.PICTURE_TYPE_JPEG + : XWPFDocument.PICTURE_TYPE_PNG; // 默认当 PNG + try (InputStream picIn = new ByteArrayInputStream(imgBytes)) { + run.addPicture(picIn, format, imagePath, widthEmu, heightEmu); + } + } + + + /** + * 递归将 sourceDir 中的所有文件和子目录,按照相对于 rootDir 的路径写入 ZipOutputStream。 + * + * @param rootDir 源目录的根,用来计算相对路径 + * @param sourceDir 当前递归到的目录 + * @param zos ZIP 输出流 + */ + public static void zipDirectory(Path rootDir, Path sourceDir, ZipOutputStream zos) throws IOException { + // 遍历当前目录下的所有文件和文件夹 + try (DirectoryStream stream = Files.newDirectoryStream(sourceDir)) { + for (Path entry : stream) { + if (Files.isDirectory(entry)) { + // 如果是目录,递归 + zipDirectory(rootDir, entry, zos); + } else { + // 如果是文件,创建一个 ZipEntry,路径以 '/' 分隔 + Path relativePath = rootDir.relativize(entry); + String zipEntryName = relativePath.toString().replace(File.separatorChar, '/'); + ZipEntry zipEntry = new ZipEntry(zipEntryName); + zos.putNextEntry(zipEntry); + // 把文件内容写入 ZIP + try (InputStream is = Files.newInputStream(entry)) { + byte[] buffer = new byte[4096]; + int len; + while ((len = is.read(buffer)) != -1) { + zos.write(buffer, 0, len); + } + } + zos.closeEntry(); + } + } + } + } + +} diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/quality/BusQualityConstructionLogMapper.xml b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/quality/BusQualityConstructionLogMapper.xml new file mode 100644 index 00000000..d2a00884 --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/quality/BusQualityConstructionLogMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/quality/BusQualityInspectionMapper.xml b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/quality/BusQualityInspectionMapper.xml new file mode 100644 index 00000000..3858e218 --- /dev/null +++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/quality/BusQualityInspectionMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/RuoYi-Vue-Plus/script/sql/xinnengyuan.sql b/RuoYi-Vue-Plus/script/sql/xinnengyuan.sql index 0daf84ad..4a7dd444 100644 --- a/RuoYi-Vue-Plus/script/sql/xinnengyuan.sql +++ b/RuoYi-Vue-Plus/script/sql/xinnengyuan.sql @@ -690,3 +690,55 @@ CREATE TABLE `bus_questions_category` PRIMARY KEY (`id`) USING BTREE, INDEX `idx_project_id` (`project_id` ASC) USING BTREE ) comment = '题库类别' COLLATE = utf8mb4_unicode_ci; + +DROP TABLE IF EXISTS `bus_quality_inspection`; +CREATE TABLE `bus_quality_inspection` +( + `id` bigint not null auto_increment comment '主键id', + `project_id` bigint null comment '项目id', + `inspection_type` char(1) null comment '巡检类型', + `inspection_headline` varchar(64) null comment '巡检标题', + `inspection_result` varchar(300) null comment '巡检结果', + `inspection_status` char(1) null comment '工单状态(1通知 2整改 3验证)', + `inspection_file` varchar(1024) null comment '巡检附件', + `team_id` bigint null comment '班组id', + `corrector` varchar(128) null comment '整改人(班组长)', + `is_reply` char(1) null comment '是否回复(1回复 2不回复)', + `reply_period_date` date null comment '回复期限日期', + `rectification_result` varchar(300) null comment '整改反馈', + `rectification_time` datetime null comment '整改时间', + `rectification_file` varchar(1024) null comment '整改附件', + `verification_result` varchar(300) null comment '验证结果', + `verification_type` char(1) null comment '验证状态(1通过 2未通过)', + `verification_time` datetime null comment '验证时间', + `remark` varchar(512) null comment '备注', + `create_by` varchar(64) null comment '创建者', + `update_by` varchar(64) null comment '更新者', + `create_time` datetime default CURRENT_TIMESTAMP null comment '创建时间', + `update_time` datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间', + `deleted_at` datetime null comment '删除时间', + `is_delete` tinyint(4) default 0 not null comment '是否删除(0正常 1删除)', + PRIMARY KEY (`id`) USING BTREE, + INDEX `project_id` (`project_id` ASC) USING BTREE, + INDEX `team_id` (`team_id` ASC) USING BTREE +) comment = '质量-检查工单' COLLATE = utf8mb4_unicode_ci; + +DROP TABLE IF EXISTS `bus_quality_construction_log`; +CREATE TABLE `bus_quality_construction_log` +( + `id` bigint not null auto_increment comment '主键id', + `project_id` bigint null comment '项目id', + `happen_date` date null comment '发生日期', + `production_status` varchar(300) null comment '生产情况', + `technology_quality` varchar(300) null comment '技术质量安全工作', + `file` varchar(1024) null comment '附件', + `remark` varchar(512) null comment '备注', + `create_by` varchar(64) null comment '创建者', + `update_by` varchar(64) null comment '更新者', + `create_time` datetime default CURRENT_TIMESTAMP null comment '创建时间', + `update_time` datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间', + `deleted_at` datetime null comment '删除时间', + `is_delete` tinyint(4) default 0 not null comment '是否删除(0正常 1删除)', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_project_id` (`project_id` ASC) USING BTREE +) comment = '质量-施工日志' COLLATE = utf8mb4_unicode_ci;