diff --git a/xinnengyuan/ruoyi-admin/src/main/resources/template/CCCET-JL-CX-25设计计划表.docx b/xinnengyuan/ruoyi-admin/src/main/resources/template/CCCET-JL-CX-25设计计划表.docx new file mode 100644 index 00000000..1b400946 Binary files /dev/null and b/xinnengyuan/ruoyi-admin/src/main/resources/template/CCCET-JL-CX-25设计计划表.docx differ diff --git a/xinnengyuan/ruoyi-admin/src/main/resources/template/设计更改通知单.docx b/xinnengyuan/ruoyi-admin/src/main/resources/template/设计更改通知单.docx new file mode 100644 index 00000000..48604063 Binary files /dev/null and b/xinnengyuan/ruoyi-admin/src/main/resources/template/设计更改通知单.docx differ diff --git a/xinnengyuan/ruoyi-admin/src/main/resources/template/设计项目负责人任命通知单.docx b/xinnengyuan/ruoyi-admin/src/main/resources/template/设计项目负责人任命通知单.docx new file mode 100644 index 00000000..25e16e4f Binary files /dev/null and b/xinnengyuan/ruoyi-admin/src/main/resources/template/设计项目负责人任命通知单.docx differ diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/AsyncUtil.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/AsyncUtil.java index 3dc5eb45..0937b97c 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/AsyncUtil.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/AsyncUtil.java @@ -1,12 +1,20 @@ package org.dromara.common.utils; +import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.utils.MessageUtils; import org.dromara.common.sse.dto.SseMessageDto; import org.dromara.common.sse.utils.SseMessageUtils; +import org.dromara.contractor.domain.SubConstructionUser; +import org.dromara.mobileAttendanceMachine.DeviceMessageSender; +import org.dromara.mobileAttendanceMachine.KqjEntity; +import org.dromara.project.domain.BusAttendanceMachine; +import org.dromara.project.service.IBusAttendanceMachineService; import org.dromara.sms4j.api.SmsBlend; import org.dromara.sms4j.api.entity.SmsResponse; import org.dromara.sms4j.core.factory.SmsFactory; +import org.dromara.system.domain.vo.SysOssVo; +import org.dromara.system.service.ISysOssService; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; @@ -17,6 +25,13 @@ import java.util.List; @Slf4j public class AsyncUtil { + @Resource + private DeviceMessageSender deviceMessageSender; + @Resource + private ISysOssService ossService; + @Resource + private IBusAttendanceMachineService attendanceMachineService; + //发送短信 @Async public void sendSms(List mobileList, String config) { @@ -39,4 +54,27 @@ public class AsyncUtil { } + //下发考勤人员 + @Async + public void sendPersonnel(Long teamId, SubConstructionUser constructionUser) { + SysOssVo byId = ossService.getById(Long.valueOf(constructionUser.getFacePic())); + List list = attendanceMachineService.lambdaQuery().apply("FIND_IN_SET({0}, teams)", teamId).list(); + for (BusAttendanceMachine machine : list) { + deviceMessageSender.sendPersonnelInformation(machine.getSn(), constructionUser.getSysUserId().toString(), constructionUser.getUserName(), byId.getUrl()); + } + } + + //删除考勤人员 + @Async + public void deletePersonnel(SubConstructionUser constructionUser) { + List list = attendanceMachineService.lambdaQuery().apply("FIND_IN_SET({0}, teams)", constructionUser.getTeamId()).list(); + for (BusAttendanceMachine machine : list) { + try { + deviceMessageSender.deleteUser(machine.getSn(), constructionUser.getSysUserId().toString()); + } catch (Exception e) { + log.error("删除考勤人员异常", e); + } + } + } + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/service/impl/SubConstructionUserServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/service/impl/SubConstructionUserServiceImpl.java index f7e489a2..8e76bc75 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/service/impl/SubConstructionUserServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/service/impl/SubConstructionUserServiceImpl.java @@ -26,6 +26,7 @@ import org.dromara.common.oss.core.OssClient; import org.dromara.common.oss.exception.OssException; import org.dromara.common.oss.factory.OssFactory; import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.utils.AsyncUtil; import org.dromara.common.utils.IdCardEncryptorUtil; import org.dromara.common.utils.baiduUtil.BaiDuFace; import org.dromara.common.utils.baiduUtil.BaiDuOCR; @@ -46,6 +47,7 @@ import org.dromara.contractor.mapper.SubConstructionUserMapper; import org.dromara.contractor.service.ISubConstructionUserFileService; import org.dromara.contractor.service.ISubConstructionUserService; import org.dromara.contractor.service.ISubContractorService; +import org.dromara.mobileAttendanceMachine.DeviceMessageSender; import org.dromara.project.domain.*; import org.dromara.project.domain.enums.BusAttendanceClockStatusEnum; import org.dromara.project.domain.enums.BusAttendanceCommuterEnum; @@ -151,6 +153,11 @@ public class SubConstructionUserServiceImpl extends ServiceImpl 0; } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/controller/DesConstructionSchedulePlanController.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/controller/DesConstructionSchedulePlanController.java index 9388df81..9b2356cb 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/controller/DesConstructionSchedulePlanController.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/controller/DesConstructionSchedulePlanController.java @@ -2,10 +2,13 @@ package org.dromara.design.controller; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; + +import org.apache.poi.xwpf.usermodel.*; import org.dromara.common.core.domain.R; import org.dromara.common.excel.utils.ExcelUtil; import org.dromara.common.idempotent.annotation.RepeatSubmit; @@ -13,18 +16,37 @@ import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.web.core.BaseController; import org.dromara.design.domain.DesConstructionSchedulePlan; +import org.dromara.design.domain.DesUser; import org.dromara.design.domain.dto.constructionscheduleplan.*; import org.dromara.design.domain.vo.DesConstructionSchedulePlanVo; +import org.dromara.design.exportUtil.plan.AttachmentPersonnel; import org.dromara.design.service.IDesConstructionSchedulePlanService; +import org.dromara.design.service.IDesUserService; +import org.dromara.system.domain.vo.SysDictDataVo; +import org.dromara.system.service.ISysDictDataService; +import org.dromara.system.service.ISysDictTypeService; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import java.io.IOException; -import java.util.List; +import java.io.*; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; /** * 设计计划 * @@ -38,7 +60,10 @@ public class DesConstructionSchedulePlanController extends BaseController { @Resource private IDesConstructionSchedulePlanService desConstructionSchedulePlanService; - + @Resource + private IDesUserService desUserService; + @Resource + private ISysDictTypeService dictTypeService; /** * 查询设计计划列表 */ @@ -150,4 +175,168 @@ public class DesConstructionSchedulePlanController extends BaseController { desConstructionSchedulePlanService.exportSchedule(response,projectId); } + private static final String TEMPLATE_RESOURCE_PATH = "template/CCCET-JL-CX-25设计计划表.docx"; + // -------------------------- 2. 核心接口:修复单元格清空逻辑与数据填充 -------------------------- + @PostMapping("/downloadWord") + public void fillCccetTemplate(Long projectId, HttpServletResponse response) { + // 1. 基础参数校验(避免空数据) + + // 2. 读取resource中的模板+填充数据 + try ( + // 关键:通过ClassPathResource读取resource/template下的模板 + InputStream templateIs = new ClassPathResource(TEMPLATE_RESOURCE_PATH).getInputStream(); + XWPFDocument doc = new XWPFDocument(templateIs); // 加载模板 + OutputStream out = response.getOutputStream() // 响应流 + ) { + // -------------------------- + // 步骤1:填充第1页-主信息表(索引0,固定6列) + // -------------------------- +// XWPFTable mainTable = doc.getTables().get(0); +// // 工程名称(第1行第0列) +// if (mainTable.getRows().size() > 1 && mainTable.getRow(1).getCell(0) != null) { +// mainTable.getRow(1).getCell(0).setText(request.getProjectName() == null ? "" : request.getProjectName()); +// } +// // 工程号(第1行第1列) +// if (mainTable.getRows().size() > 1 && mainTable.getRow(1).getCell(1) != null) { +// mainTable.getRow(1).getCell(1).setText(request.getProjectNo()); +// } +// // 编制日期(第2行第3列) +// if (mainTable.getRows().size() > 2 && mainTable.getRow(2).getCell(3) != null) { +// mainTable.getRow(2).getCell(3).setText(request.getCompileDate() == null ? "" : request.getCompileDate()); +// } +// // 编制人(第3行第0列) +// if (mainTable.getRows().size() > 3 && mainTable.getRow(3).getCell(0) != null) { +// mainTable.getRow(3).getCell(0).setText(request.getCompiler() == null ? "" : request.getCompiler()); +// } +// // 批准人(第3行第2列) +// if (mainTable.getRows().size() > 3 && mainTable.getRow(3).getCell(2) != null) { +// mainTable.getRow(3).getCell(2).setText(request.getApprover() == null ? "" : request.getApprover()); +// } +// // 设计阶段(第3行第4列) +// if (mainTable.getRows().size() > 3 && mainTable.getRow(3).getCell(4) != null) { +// mainTable.getRow(3).getCell(4).setText(request.getDesignStage() == null ? "" : request.getDesignStage()); +// } +// // 设计规模(第4行第0列) +// if (mainTable.getRows().size() > 4 && mainTable.getRow(4).getCell(0) != null) { +// mainTable.getRow(4).getCell(0).setText(request.getDesignScale() == null ? "" : request.getDesignScale()); +// } + + // -------------------------- + // 步骤2:填充第3页-附件1人员配置表(索引2,固定11列) + // -------------------------- + XWPFTable staffTable = doc.getTables().get(2); + // 删除模板中附件1的空数据行(保留第0行表头) + while (staffTable.getRows().size() > 2) { + staffTable.removeRow(1); + } + + List list = getPersonnelDataByProjectId(projectId); + for (AttachmentPersonnel staff : list) { + XWPFTableRow newRow = staffTable.createRow(); + // 补全11列(避免POI默认列数不足导致null) + while (newRow.getTableCells().size() < 11) { + newRow.createCell(); + } + // 按附件1列顺序填充 + newRow.getCell(0).setText(staff.getProfessional() == null ? "" : staff.getProfessional()); + newRow.getCell(1).setText(staff.getLeaderName() == null ? "" : staff.getLeaderName()); + newRow.getCell(2).setText(staff.getLeaderTitle() == null ? "" : staff.getLeaderTitle()); + newRow.getCell(3).setText(staff.getDesignerName() == null ? "" : staff.getDesignerName()); + newRow.getCell(4).setText(staff.getDesignerTitle() == null ? "" : staff.getDesignerTitle()); + newRow.getCell(5).setText(staff.getReviewerName() == null ? "" : staff.getReviewerName()); + newRow.getCell(6).setText(staff.getReviewerTitle() == null ? "" : staff.getReviewerTitle()); + newRow.getCell(7).setText(staff.getCheckerName() == null ? "" : staff.getCheckerName()); + newRow.getCell(8).setText(staff.getCheckerTitle() == null ? "" : staff.getCheckerTitle()); + newRow.getCell(9).setText(staff.getApproverName() == null ? "" : staff.getApproverName()); + newRow.getCell(10).setText(staff.getApproverTitle() == null ? "" : staff.getApproverTitle()); + } + + // -------------------------- + // 步骤3:设置响应头(触发前端下载) + // -------------------------- + response.setContentType("application/octet-stream"); + // 文件名:填充后_工程号_CCCET-JL-CX-25设计计划表.docx + String fileName = "CCCET-JL-CX-25设计计划表.docx"; + response.setHeader("Content-Disposition", + "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8)); + response.setHeader("Access-Control-Expose-Headers", "Content-Disposition"); // 允许前端获取文件名 + + // -------------------------- + // 步骤4:写出文件到前端 + // -------------------------- + doc.write(out); + out.flush(); + + } catch (Exception e) { + // 异常封装(前端可捕获具体错误) + throw new RuntimeException("CCCET-JL-CX-25模板填充失败:" + e.getMessage()); + } + } + + + + /** + * 根据projectId获取数据(仅针对CCCET-JL-CX-25设计计划表.docx附件1) + */ + private List getPersonnelDataByProjectId(Long projectId) { + // 模拟数据库查询(实际项目替换为真实Service调用) + List userList = desUserService.list(Wrappers.lambdaQuery() + .eq(DesUser::getProjectId, projectId) + ); + if (userList.isEmpty()) { + return Collections.emptyList(); + } + + // 专业字典映射(编码→名称) + List majorDict = dictTypeService.selectDictDataByType("des_user_major"); + Map majorMap = majorDict.stream() + .collect(Collectors.toMap(SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel)); + + // 按角色分组(1-专业负责人,2-设计人,3-校审人,4-审定人,5-审核人) + DesUser leader = userList.stream().filter(u -> "1".equals(u.getUserType())).findFirst().orElse(null); + Map> designerMap = userList.stream() + .filter(u -> "2".equals(u.getUserType())) + .collect(Collectors.groupingBy(DesUser::getUserMajor)); + Map> reviewerMap = userList.stream() + .filter(u -> "3".equals(u.getUserType())) + .collect(Collectors.groupingBy(DesUser::getUserMajor)); + Map> checkerMap = userList.stream() + .filter(u -> "5".equals(u.getUserType())) + .collect(Collectors.groupingBy(DesUser::getUserMajor)); + Map> approverMap = userList.stream() + .filter(u -> "4".equals(u.getUserType())) + .collect(Collectors.groupingBy(DesUser::getUserMajor)); + + // 构建附件1数据(一个专业一行,避免重复) + List dataList = new ArrayList<>(); + for (Map.Entry> entry : designerMap.entrySet()) { + String majorCode = entry.getKey(); + String majorName = majorMap.getOrDefault(majorCode, majorCode); + List designers = entry.getValue(); + + // 获取对应专业的其他角色 + DesUser reviewer = reviewerMap.getOrDefault(majorCode, Collections.emptyList()).stream().findFirst().orElse(null); + DesUser checker = checkerMap.getOrDefault(majorCode, Collections.emptyList()).stream().findFirst().orElse(null); + DesUser approver = approverMap.getOrDefault(majorCode, Collections.emptyList()).stream().findFirst().orElse(null); + // 封装数据(多个设计人用顿号分隔) + AttachmentPersonnel data = new AttachmentPersonnel(); + data.setProfessional(majorName); + data.setLeaderName(leader != null ? leader.getUserName() : ""); + //data.setLeadeTitle(leader != null ? leader.getUserTitle() : ""); + data.setDesignerName(designers.stream().map(DesUser::getUserName).collect(Collectors.joining("、"))); + //data.setDesignerTitle(designers.stream().map(DesUser::getUserTitle).collect(Collectors.joining("、"))); + data.setReviewerName(reviewer != null ? reviewer.getUserName() : ""); + //data.setReviewerTitle(reviewer != null ? reviewer.getUserTitle() : ""); + data.setCheckerName(checker != null ? checker.getUserName() : ""); + //data.setCheckerTitle(checker != null ? checker.getUserTitle() : ""); + data.setApproverName(approver != null ? approver.getUserName() : ""); + //data.setApproverTitle(approver != null ? approver.getUserTitle() : ""); + + dataList.add(data); + } + + return dataList; + } + + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/controller/DesDesignChangeController.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/controller/DesDesignChangeController.java index cdb4d941..a3106579 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/controller/DesDesignChangeController.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/controller/DesDesignChangeController.java @@ -2,6 +2,7 @@ package org.dromara.design.controller; import cn.dev33.satoken.annotation.SaCheckPermission; import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.deepoove.poi.XWPFTemplate; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.constraints.NotEmpty; @@ -16,24 +17,36 @@ 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.design.domain.DesUser; import org.dromara.design.domain.DesVolumeCatalog; import org.dromara.design.domain.DesVolumeFile; import org.dromara.design.domain.dto.designchange.DesDesignChangeCreateReq; import org.dromara.design.domain.dto.designchange.DesDesignChangeQueryReq; import org.dromara.design.domain.dto.designchange.DesDesignChangeUpdateReq; +import org.dromara.design.domain.dto.designchange.DesDesignExtendDetailDto; import org.dromara.design.domain.dto.volumecatalog.DesVolumeCatalogQueryReq; import org.dromara.design.domain.vo.designchange.DesDesignChangeVo; import org.dromara.design.domain.vo.volumecatalog.DesVolumeCatalogVo; import org.dromara.design.service.IDesDesignChangeService; import org.dromara.design.service.IDesVolumeCatalogService; import org.dromara.design.service.IDesVolumeFileService; +import org.dromara.project.domain.BusProject; import org.dromara.system.domain.vo.SysOssVo; +import org.dromara.system.service.ISysDictDataService; import org.dromara.system.service.ISysOssService; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.MediaType; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URLEncoder; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; /** @@ -56,6 +69,9 @@ public class DesDesignChangeController extends BaseController { @Resource private IDesVolumeFileService desVolumeFileService; + @Resource + private ISysDictDataService dictDataService; + /** * 查询设计变更管理列表 */ @@ -146,4 +162,92 @@ public class DesDesignChangeController extends BaseController { return R.ok(list); } + private static final String TEMPLATE_PATH = "template/设计更改通知单.docx"; + + @PostMapping("/downloadWord") + public void generateDesignLeaderDoc(Long id, HttpServletResponse response) { + OutputStream outputStream = null; + try { + // 1. 调用Service生成目标模板的Word字节流 + byte[] docBytes = generateDocBytes(id); + + // 2. 配置响应头:确保前端正确下载(避免中文乱码、指定文件类型) + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); // 二进制流类型 + // 下载文件名:格式为“[项目名]-设计负责人任命通知单.docx”(此处用projectId拼接,真实场景可从数据中获取项目名) + String downloadFileName = URLEncoder.encode( + "设计更改通知单.docx", + "UTF-8" + ); + response.setHeader("Content-Disposition", "attachment;filename=" + downloadFileName); + response.setContentLength(docBytes.length); // 设置响应体长度(优化下载体验) + + // 3. 将Word字节流写入响应 + outputStream = response.getOutputStream(); + outputStream.write(docBytes); + outputStream.flush(); + + } catch (Exception e) { + e.printStackTrace(); + // 异常处理:返回500错误状态码 + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } finally { + // 关闭流,避免资源泄漏 + if (outputStream != null) { + try { + outputStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + public byte[] generateDocBytes(Long id) throws Exception { + // -------------------------- 步骤1:按projectId查询项目数据(模拟真实业务) -------------------------- + // 实际场景替换为数据库查询(如调用DAO获取项目名称、负责人等) + DesDesignChangeVo vo = desDesignChangeService.queryById(id); + DesDesignExtendDetailDto extendDetail = vo.getExtendDetail()==null?new DesDesignExtendDetailDto():vo.getExtendDetail(); + + Map placeholderData = new HashMap<>(); + placeholderData.put("projectName", vo.getProjectName()); + placeholderData.put("designPhase",extendDetail.getDesignPhase()); + placeholderData.put("subName",extendDetail.getSubName()); + String s = dictDataService.selectDictLabel("des_user_major", vo.getSpecialty()); + placeholderData.put("specialty",s); + placeholderData.put("volumeNo",vo.getVolumeNo()); + + String changeReason = vo.getChangeReason(); + List reasons = Arrays.asList("设计漏项", "设计改进", "设计差错", "接口差错", + "业主要求", "施工承包商要求", "外部资料与最终情况不符", "材料代用或其他"); + + String reason = reasons.stream() + .map(item -> changeReason.contains(String.valueOf(reasons.indexOf(item) + 1)) ? " ☑" + item : " □" + item) + .collect(Collectors.joining()); + + placeholderData.put("changeReason", reason); + placeholderData.put("designDisposal1", "1".equals(extendDetail.getDesignDisposal())?"☑" : "□"); + placeholderData.put("designDisposal2", "2".equals(extendDetail.getDesignDisposal())?"☑" : "□"); + placeholderData.put("designDisposal3", "3".equals(extendDetail.getDesignDisposal())?"☑" : "□"); + placeholderData.put("changeContent",vo.getChangeContent()); + placeholderData.put("changeCategory1", "1".equals(extendDetail.getChangeCategory())?"☑" : "□"); + placeholderData.put("changeCategory2", "2".equals(extendDetail.getChangeCategory())?"☑" : "□"); + placeholderData.put("involvingProfessions", extendDetail.getInvolvingProfessions()); + // -------------------------- 步骤2:用poi-tl加载目标模板并替换占位符 -------------------------- + // 读取resources下的“设计项目负责人任命通知单.docx”模板 + ClassPathResource templateResource = new ClassPathResource(TEMPLATE_PATH); + try (InputStream templateIs = templateResource.getInputStream(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + + // 1. 加载模板 2. 注入替换数据 3. 渲染生成新文档 + XWPFTemplate template = XWPFTemplate.compile(templateIs) + .render(placeholderData); // 自动匹配{{变量名}}占位符 + + // -------------------------- 步骤3:将生成的文档写入字节流 -------------------------- + template.write(outputStream); + template.close(); // 关闭模板资源 + return outputStream.toByteArray(); + } + } + + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/controller/DesUserController.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/controller/DesUserController.java index 6971b4ab..1e5358f4 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/controller/DesUserController.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/controller/DesUserController.java @@ -1,16 +1,18 @@ package org.dromara.design.controller; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; +import java.net.URLEncoder; import java.rmi.ServerException; import java.time.LocalDate; import java.time.ZoneId; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.List; +import java.util.*; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.deepoove.poi.XWPFTemplate; import lombok.RequiredArgsConstructor; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.constraints.*; @@ -22,8 +24,15 @@ import org.apache.poi.ss.usermodel.Row; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; //import org.dromara.design.domain.DesUserExcelData; +import org.dromara.design.domain.DesUser; import org.dromara.design.domain.DesUserExcelData; import org.dromara.design.domain.dto.desUser.DesUserBatchDto; +import org.dromara.project.domain.BusProject; +import org.dromara.project.service.IBusProjectService; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; import org.dromara.common.idempotent.annotation.RepeatSubmit; @@ -54,6 +63,7 @@ import org.springframework.web.multipart.MultipartFile; public class DesUserController extends BaseController { private final IDesUserService desUserService; + private final IBusProjectService projectService; /** * 查询设计人员列表 @@ -142,5 +152,75 @@ public class DesUserController extends BaseController { return toAjax(desUserService.batchAddOrUpdate(dto)); } + private static final String TEMPLATE_PATH = "template/设计项目负责人任命通知单.docx"; + @PostMapping("/downloadWord") + public void generateDesignLeaderDoc( Long projectId, + HttpServletResponse response + ) { + OutputStream outputStream = null; + try { + // 1. 调用Service生成目标模板的Word字节流 + byte[] docBytes = generateDocBytes(projectId); + + // 2. 配置响应头:确保前端正确下载(避免中文乱码、指定文件类型) + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); // 二进制流类型 + // 下载文件名:格式为“[项目名]-设计负责人任命通知单.docx”(此处用projectId拼接,真实场景可从数据中获取项目名) + String downloadFileName = URLEncoder.encode( + "设计负责人任命通知单.docx", + "UTF-8" + ); + response.setHeader("Content-Disposition", "attachment;filename=" + downloadFileName); + response.setContentLength(docBytes.length); // 设置响应体长度(优化下载体验) + + // 3. 将Word字节流写入响应 + outputStream = response.getOutputStream(); + outputStream.write(docBytes); + outputStream.flush(); + + } catch (Exception e) { + e.printStackTrace(); + // 异常处理:返回500错误状态码 + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } finally { + // 关闭流,避免资源泄漏 + if (outputStream != null) { + try { + outputStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + public byte[] generateDocBytes(Long projectId) throws Exception { + // -------------------------- 步骤1:按projectId查询项目数据(模拟真实业务) -------------------------- + // 实际场景替换为数据库查询(如调用DAO获取项目名称、负责人等) + BusProject byId = projectService.getById(projectId); + DesUser desUser = desUserService.lambdaQuery().eq(DesUser::getProjectId, projectId) + .eq(DesUser::getUserType, "1") + .last("limit 1").one(); + + + Map placeholderData = new HashMap<>(); + placeholderData.put("projectName", byId==null?"" :byId.getProjectName()); + placeholderData.put("leaderName",desUser==null?"": desUser.getUserName()); + + // -------------------------- 步骤2:用poi-tl加载目标模板并替换占位符 -------------------------- + // 读取resources下的“设计项目负责人任命通知单.docx”模板 + ClassPathResource templateResource = new ClassPathResource(TEMPLATE_PATH); + try (InputStream templateIs = templateResource.getInputStream(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + + // 1. 加载模板 2. 注入替换数据 3. 渲染生成新文档 + XWPFTemplate template = XWPFTemplate.compile(templateIs) + .render(placeholderData); // 自动匹配{{变量名}}占位符 + + // -------------------------- 步骤3:将生成的文档写入字节流 -------------------------- + template.write(outputStream); + template.close(); // 关闭模板资源 + return outputStream.toByteArray(); + } + } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/exportUtil/plan/AttachmentPersonnel.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/exportUtil/plan/AttachmentPersonnel.java new file mode 100644 index 00000000..6e1b0aec --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/exportUtil/plan/AttachmentPersonnel.java @@ -0,0 +1,25 @@ +package org.dromara.design.exportUtil.plan; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AttachmentPersonnel { + String professional; // 专业及人员分工 + String leaderName; // 专业负责人姓名 + String leaderTitle; // 专业负责人职称 + String designerName; // 设计人姓名 + String designerTitle; // 设计人职称 + String reviewerName; // 校审人姓名 + String reviewerTitle; // 校审人职称 + String checkerName; // 审核人姓名 + String checkerTitle; // 审核人职称 + String approverName; // 审定人姓名 + String approverTitle; // 审定人职称 +} + + + diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/BusAttendanceMachineController.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/BusAttendanceMachineController.java index 1a842e40..01fc1f6c 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/BusAttendanceMachineController.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/BusAttendanceMachineController.java @@ -91,4 +91,16 @@ public class BusAttendanceMachineController extends BaseController { @PathVariable Long[] ids) { return toAjax(busAttendanceMachineService.deleteWithValidByIds(List.of(ids), true)); } + + + /** + * 批量重新下发 + */ + @SaCheckPermission("project:attendanceMachine:reissue") + @RepeatSubmit() + @PostMapping("/reissue/{ids}") + public void reissue(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids){ + busAttendanceMachineService.reissue(List.of(ids)); + } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusAttendanceAppController.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusAttendanceAppController.java index ae06fd1a..12e89db6 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusAttendanceAppController.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusAttendanceAppController.java @@ -128,8 +128,8 @@ public class BusAttendanceAppController extends BaseController { * 获取用户打卡异常记录 */ @GetMapping("/getAbnormalAttendance/{projectId}") - public TableDataInfo getAbnormalAttendance(@NotNull @PathVariable("projectId") Long projectId, PageQuery pageQuery){ - return attendanceService.getAbnormalAttendance(projectId,pageQuery); + public TableDataInfo getAbnormalAttendance(@NotNull @PathVariable("projectId") Long projectId,String handle, PageQuery pageQuery){ + return attendanceService.getAbnormalAttendance(projectId,handle,pageQuery); } /** diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusAttendanceMachineService.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusAttendanceMachineService.java index 78eec5ce..7403c90a 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusAttendanceMachineService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusAttendanceMachineService.java @@ -1,12 +1,15 @@ package org.dromara.project.service; import com.baomidou.mybatisplus.extension.service.IService; +import jakarta.validation.constraints.NotEmpty; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.project.domain.BusAttendanceMachine; import org.dromara.project.domain.dto.attendancemachine.BusAttendanceMachineQueryReq; import org.dromara.project.domain.dto.attendancemachine.BusAttendanceMachineUpdateReq; import org.dromara.project.domain.vo.BusAttendanceMachineVo; +import org.springframework.scheduling.annotation.Async; +import org.springframework.web.bind.annotation.PathVariable; import java.util.Collection; import java.util.List; @@ -75,4 +78,7 @@ public interface IBusAttendanceMachineService extends IService ids, Boolean isValid); + + + void reissue(Collection ids); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusAttendanceService.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusAttendanceService.java index 2d0ad545..0e390caa 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusAttendanceService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusAttendanceService.java @@ -131,7 +131,7 @@ public interface IBusAttendanceService extends IService{ /** * 获取用户打卡异常记录 */ - TableDataInfo getAbnormalAttendance(@NotNull @PathVariable("projectId") Long projectId, PageQuery pageQuery); + TableDataInfo getAbnormalAttendance(Long projectId,String handle, PageQuery pageQuery); /** * 统计指定日期的打卡人员 diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceMachineServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceMachineServiceImpl.java index 7fb23233..9a4ebc9c 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceMachineServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceMachineServiceImpl.java @@ -1,6 +1,7 @@ package org.dromara.project.service.impl; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -28,13 +29,13 @@ import org.dromara.project.service.IBusAttendanceMachineService; import org.dromara.project.service.IBusProjectService; import org.dromara.project.service.IBusProjectTeamMemberService; import org.dromara.project.service.IBusProjectTeamService; +import org.dromara.system.domain.vo.SysOssVo; +import org.dromara.system.service.ISysOssService; import org.springframework.beans.BeanUtils; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; /** @@ -63,6 +64,10 @@ public class BusAttendanceMachineServiceImpl extends ServiceImpl 0; } + + @Async + @Override + public void reissue(Collection ids) { + List busAttendanceMachines = baseMapper.selectByIds(ids); + + for (BusAttendanceMachine machine : busAttendanceMachines) { + String sn = machine.getSn(); + try { + deviceMessageSender.deleteAllUsers(sn); + } catch (Exception e) { + log.error("删除考勤人员异常", e); + } + + String teams = machine.getTeams(); + if (StrUtil.isBlank(teams)) { + continue; + } + List oldTeamIds = Arrays.stream(teams.split(",")) + .map(Long::parseLong) + .distinct() + .toList(); + + List userIds = projectTeamMemberService.lambdaQuery() + .select(BusProjectTeamMember::getMemberId) + .in(BusProjectTeamMember::getTeamId, oldTeamIds) + .list() + .stream().map(BusProjectTeamMember::getMemberId) + .toList(); + if (CollUtil.isEmpty(userIds)) { + continue; + } + + List users = constructionUserService.lambdaQuery() + .in(SubConstructionUser::getSysUserId, userIds) + .list(); + + Map faceMap = new HashMap<>(); + List list = users.stream().map(SubConstructionUser::getFacePic).filter(StrUtil::isNotBlank).toList(); + if (CollUtil.isNotEmpty(list)) { + //转成Long的集合 + List list1 = list.stream().map(Long::parseLong).toList(); + List sysOssVos = ossService.listByIds(list1); + faceMap = sysOssVos.stream().collect(Collectors.toMap(SysOssVo::getOssId, SysOssVo::getUrl)); + } + + for (SubConstructionUser user : users) { + String facePic = StrUtil.isBlank(user.getFacePic()) ? "" : faceMap.get(Long.valueOf(user.getFacePic())); + deviceMessageSender.sendPersonnelInformation(sn, user.getSysUserId().toString() + , user.getUserName(), facePic); + } + } + } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceServiceImpl.java index 95ac6927..cdedb017 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceServiceImpl.java @@ -792,7 +792,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl getAbnormalAttendance(Long projectId, PageQuery pageQuery) { + public TableDataInfo getAbnormalAttendance(Long projectId,String handle, PageQuery pageQuery) { List abnormalList = Arrays.asList(BusAttendanceClockStatusEnum.LATE.getValue(), BusAttendanceClockStatusEnum.LEAVEEARLY.getValue(), @@ -809,6 +809,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl busAttendanceVoIPage = baseMapper.selectVoPage(pageQuery.build(), Wrappers.lambdaQuery(BusAttendance.class) .eq(BusAttendance::getUserId, userId) + .eq(StrUtil.isNotBlank(handle), BusAttendance::getHandle, handle) .eq(b, BusAttendance::getProjectId, projectId) .in(BusAttendance::getClockStatus, abnormalList) .ge(BusAttendance::getClockDate, LocalDate.now().minusDays(29)) diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusProjectTeamMemberServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusProjectTeamMemberServiceImpl.java index 7c36cab1..8f54b9c3 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusProjectTeamMemberServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusProjectTeamMemberServiceImpl.java @@ -17,6 +17,7 @@ import org.dromara.common.enums.AppUserTypeEnum; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.utils.AsyncUtil; import org.dromara.common.utils.IdCardEncryptorUtil; import org.dromara.contractor.domain.SubConstructionUser; import org.dromara.contractor.service.ISubConstructionUserService; @@ -90,6 +91,8 @@ public class BusProjectTeamMemberServiceImpl extends ServiceImpl