diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bgt/AppBgtMessageController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bgt/AppBgtMessageController.java index 376551e..cf9bd0a 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bgt/AppBgtMessageController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bgt/AppBgtMessageController.java @@ -15,7 +15,9 @@ import com.ruoyi.bgt.service.IBgtProjectRecruitService; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.enums.RecruitApplyStatus; +import com.ruoyi.common.util.MessageUtil; import com.ruoyi.wgz.bo.req.WgzAppPersonalBasicInformationReq; import com.ruoyi.wgz.bo.res.WgzAppPersonalBasicInformationRes; import com.ruoyi.wgz.service.IWgzAttendanceService; @@ -53,6 +55,8 @@ public class AppBgtMessageController extends BaseController { private final IBgtProjectRecruitService iBgtProjectRecruitService; + private final RedisCache redisCache; + @ApiOperation("未读消息统计") @GetMapping("/countUnread") public AjaxResult count() { @@ -74,10 +78,14 @@ public class AppBgtMessageController extends BaseController { @ApiOperation("已读") @PutMapping("/read/{id}") public AjaxResult read(@PathVariable(value = "id") Long id) { - BgtMessage bgtMessage = new BgtMessage(); - bgtMessage.setId(id); - bgtMessage.setReadStatus("1"); - return AjaxResult.success(iBgtMessageService.updateById(bgtMessage)); + BgtMessage bgtMessage = iBgtMessageService.getById(id); + boolean b = true; + if("0".equals(bgtMessage.getReadStatus())){ + MessageUtil.readMessagePush(bgtMessage.getRecipientType(), bgtMessage.getRecipientId(), bgtMessage.getMessageLargeType(), bgtMessage.getReadStatus()); + bgtMessage.setReadStatus("1"); + b = iBgtMessageService.updateById(bgtMessage); + } + return AjaxResult.success(b); } @ApiOperation("已操作") diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/SseController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/SseController.java new file mode 100644 index 0000000..69eafbf --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/SseController.java @@ -0,0 +1,47 @@ +package com.ruoyi.web.controller.common; + +import com.ruoyi.common.util.SseUtil; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +@RestController +@RequestMapping("/sse") +public class SseController { + + + /** + * 客户端订阅SSE连接(适配2.4.7) + */ + @GetMapping(path = "/subscribe", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + public SseEmitter subscribe(String userId,String prefix) { + return SseUtil.subscribe(userId,prefix); + } + +// /** +// * 触发消息数量推送(适配2.4.7) +// */ +// @GetMapping("/push") +// public void pushMessageCount(String userId) { +// SseEmitter emitter = emitterMap.get(userId); +// if (emitter != null) { +// BgtMessageCountVO bgtMessageCountVO = new BgtMessageCountVO(); +// bgtMessageCountVO.setTaskMessageCount(1); +// bgtMessageCountVO.setSettlementMessageCount(0); +// try { +// // 发送自定义事件(客户端通过事件名监听) +// emitter.send(SseEmitter.event() +// .id(String.valueOf(System.currentTimeMillis())) +// .name("messageCount") +// .data(JSONUtil.toJsonStr(bgtMessageCountVO)) +// ); +// } catch (IOException e) { +// emitter.completeWithError(e); +// emitterMap.remove(userId); // 异常时清理无效连接 +// } +// } +// } +} + diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/UploadZipController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/UploadZipController.java index f13453d..2556cf9 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/UploadZipController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/UploadZipController.java @@ -11,7 +11,6 @@ import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.entity.BgtUser; import com.ruoyi.common.domain.Annex; -import com.ruoyi.common.domain.AnnexRecord; import com.ruoyi.common.exception.BaseException; import com.ruoyi.common.service.IAnnexRecordService; import com.ruoyi.common.service.IAnnexService; @@ -34,15 +33,16 @@ import java.nio.charset.Charset; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; /** - * 登录验证 - * - * @author ruoyi + * 压缩文件上传控制器(整合路径收集与验证逻辑) */ @Api(value = "网页模板上传", tags = {"网页模板上传"}) @RequiredArgsConstructor(onConstructor_ = @Autowired) @@ -57,9 +57,9 @@ public class UploadZipController { private final IBgtUserService bgtUserService; private final IBgtProjectRecruitApplyService recruitApplyService; - private static final String TEMP_DIR = "ruoyi/uploadPath/temporaryZip"; //临时解压 - private static final String SAVE_DIR = "ruoyi/uploadPath/recruit"; //保存目录 - private static final String RECORD_DIR = "ruoyi/uploadPath/record"; //记录目录 + private static final String TEMP_DIR = "ruoyi/uploadPath/temporaryZip"; // 临时解压目录 + private static final String SAVE_DIR = "ruoyi/uploadPath/recruit"; // 最终保存目录 + private static final String RECORD_DIR = "ruoyi/uploadPath/record"; // 记录目录 @ApiOperation("上传压缩文件") @PostMapping("/upload-zip") @@ -71,14 +71,14 @@ public class UploadZipController { } String originalFilename = file.getOriginalFilename(); - if (originalFilename == null || !originalFilename.toLowerCase().endsWith(".zip")) { throw new BaseException("上传的文件不是有效的 ZIP 文件!"); } - String[] split = originalFilename.split("_"); - if (split.length != 2 || !split[0].equals(recruitId.toString())) { - throw new BaseException("文件名与所选择招工不匹配"); - } +// String[] split = originalFilename.split("_"); +// if (split.length != 2 || !split[0].equals(recruitId.toString())) { +// throw new BaseException("文件名与所选择招工不匹配"); +// } + BgtProjectRecruit recruit = recruitService.queryById(recruitId); if (recruit == null) { throw new BaseException("招工信息不存在!"); @@ -90,79 +90,151 @@ public class UploadZipController { } String username = bgtUser.getUsername(); - String firstLevelFolderName = ""; + String firstLevelFolderName = recruitId + "_" + DigestUtil.md5Hex(recruit.getRecruitName()); try { - // 保存上传的压缩文件 - String s = DigestUtil.md5Hex(recruit.getRecruitName()); - firstLevelFolderName = recruitId + "_" + s; + // 保存并解压ZIP文件 File zipFile = new File(TEMP_DIR, firstLevelFolderName + ".zip"); ensureDirectoryExists(zipFile.getParentFile()); try (OutputStream os = new FileOutputStream(zipFile)) { os.write(file.getBytes()); } - // 解压压缩文件 File extractDir = new File(TEMP_DIR, firstLevelFolderName); ensureDirectoryExists(extractDir); extractZipFile(zipFile, extractDir); - // 处理解压后的文件夹 - processExtractedFolder(extractDir, recruitId); + // 收集目标路径(仅BaoXian/HeTong目录下的最底层文件) + // 收集路径(返回Map和目录列表) + Object[] paths = collectLeafPaths(extractDir); + Map leafFileMap = (Map) paths[0]; // 改为Map + List targetDirPaths = (List) paths[1]; - // 将解压后的文件移动到 SAVE_DIR - moveFilesToSaveDir(extractDir, recruitId, username); + // 处理SAVE_DIR中已有文件的重命名(关键新增逻辑) + processExistingFilesInSaveDir(targetDirPaths, recruitId); - // 异步执行 RECORD_DIR 操作和删除临时文件操作 - asyncProcessRecordAndDeleteTemp(extractDir, zipFile, recruitId, username, userId); + // 修改后(新调用方式) + processExtractedFolder(targetDirPaths, recruitId); // 传递收集的targetDirPaths + // 移动文件到SAVE_DIR(使用收集的路径) + moveFilesToSaveDir(extractDir, recruitId, username, leafFileMap); + + // 异步清理临时文件(仅保留清理逻辑) + asyncDeleteTempFiles(extractDir, zipFile); return AjaxResult.success("文件上传并处理成功"); } catch (Exception e) { - // 删除临时文件和文件夹 - File extractDir = new File(TEMP_DIR, firstLevelFolderName); - deleteFolder(extractDir); - File zipFile = new File(TEMP_DIR, firstLevelFolderName + ".zip"); - deleteFolder(zipFile); + deleteFolder(new File(TEMP_DIR, firstLevelFolderName)); + deleteFolder(new File(TEMP_DIR, firstLevelFolderName + ".zip")); e.printStackTrace(); return AjaxResult.error("文件处理过程中出现错误"); } } - @Async - public CompletableFuture asyncProcessRecordAndDeleteTemp(File extractDir, File zipFile, Long recruitId, String username, Long userId) { - return CompletableFuture.runAsync(() -> { - try { - // 移动到 RECORD_DIR - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); - String timeStamp = LocalDateTime.now().format(formatter); - BgtProjectRecruit recruit = recruitService.queryById(recruitId); - String s = DigestUtil.md5Hex(recruit.getRecruitName()); - String firstLevelFolderName = recruitId + "_" + s; - File recordDestDir = new File(RECORD_DIR, firstLevelFolderName); - ensureDirectoryExists(recordDestDir); + /** + * 收集最底层文件路径(仅BaoXian/HeTong目录下的文件) + * @param extractDir 解压根目录 + * @return [文件路径列表, 目录路径列表] + */ + private Object[] collectLeafPaths(File extractDir) { + Map leafFileMap = new HashMap<>(); // key: 倒数第二层/倒数第一层/文件名,value: 完整相对路径 + List targetDirPaths = new ArrayList<>(); + collectLeafPathsRecursive(extractDir, extractDir, leafFileMap, targetDirPaths); + return new Object[]{leafFileMap, targetDirPaths}; + } - List annexRecordList = new ArrayList<>(); - moveFilesToRecordDirRecursively(extractDir, recordDestDir, timeStamp, annexRecordList, recruitId, username, userId); - if (CollectionUtil.isNotEmpty(annexRecordList)) { - annexRecordService.saveBatch(annexRecordList); - } + /** + * 递归收集路径(核心逻辑:验证倒数第一层目录名) + */ + private void collectLeafPathsRecursive(File baseDir, File currentDir, + Map leafFileMap, List targetDirPaths) { + File[] files = currentDir.listFiles(); + if (files == null || files.length == 0) return; - // 删除临时文件和文件夹 - deleteFolder(extractDir); - if (!zipFile.delete()) { - System.err.println("无法删除压缩文件: " + zipFile.getAbsolutePath()); - } - } catch (IOException e) { - e.printStackTrace(); + boolean hasSubDir = false; + for (File file : files) { + if (file.isDirectory()) { + hasSubDir = true; + collectLeafPathsRecursive(baseDir, file, leafFileMap, targetDirPaths); } - }); + } + + if (!hasSubDir) { + for (File file : files) { + if (file.isFile()) { + String relativePath = getRelativePath(file, baseDir); // 完整相对路径(如:张三_123/BaoXian/文件.pdf) + String[] pathParts = relativePath.split(Pattern.quote(File.separator)); + + if (pathParts.length >= 3) { + String secondLastDir = pathParts[pathParts.length - 3]; // 倒数第二层(如:张三_123) + String lastDir = pathParts[pathParts.length - 2]; // 倒数第一层(如:BaoXian) + String fileName = pathParts[pathParts.length - 1]; // 文件名(如:文件.pdf) + + if ("BaoXian".equals(lastDir) || "HeTong".equals(lastDir)) { + // 构造key:倒数第二层/倒数第一层/文件名(如:张三_123/BaoXian/文件.pdf) + String key = secondLastDir + File.separator + lastDir + File.separator + fileName; + leafFileMap.put(key, relativePath); // 存入Map + + // 收集目录路径(倒数第二层/倒数第一层/) + String dirPath = secondLastDir + File.separator + lastDir + File.separator; + if (!targetDirPaths.contains(dirPath)) { + targetDirPaths.add(dirPath); + } + } + } + } + } + } + } + + /** + * 处理SAVE_DIR中已有文件的重命名(添加时间戳) + */ + private void processExistingFilesInSaveDir(List targetDirPaths, Long recruitId) throws IOException { + BgtProjectRecruit recruit = recruitService.queryById(recruitId); + String firstLevelFolder = recruitId + "_" + DigestUtil.md5Hex(recruit.getRecruitName()); + File saveBaseDir = new File(SAVE_DIR, firstLevelFolder); + + for (String dirPath : targetDirPaths) { + File targetDir = new File(saveBaseDir, dirPath); + if (targetDir.exists() && targetDir.isDirectory()) { + File[] existingFiles = targetDir.listFiles(); + if (existingFiles != null) { + for (File existingFile : existingFiles) { + if (existingFile.isFile() && !isTimestampedFile(existingFile.getName())) { + String newFileName = addTimestampToFileName(existingFile.getName()); + File newFile = new File(targetDir, newFileName); + if (!existingFile.renameTo(newFile)) { + throw new IOException("重命名失败: " + existingFile.getAbsolutePath()); + } + } + } + } + } + } + } + + /** + * 判断文件名是否已包含时间戳(格式:xxx_yyyyMMddHHmmss.xxx) + */ + private boolean isTimestampedFile(String fileName) { + return fileName.matches("^.*_\\d{14}\\..*$"); + } + + /** + * 为文件名添加时间戳(格式:原文件名_yyyyMMddHHmmss.扩展名) + */ + private String addTimestampToFileName(String originalName) { + String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); + int lastDotIndex = originalName.lastIndexOf('.'); + if (lastDotIndex == -1) { + return originalName + "_" + timestamp; + } + return originalName.substring(0, lastDotIndex) + "_" + timestamp + originalName.substring(lastDotIndex); } private void ensureDirectoryExists(File directory) throws IOException { - if (!directory.exists()) { - if (!directory.mkdirs()) { - throw new IOException("无法创建目录: " + directory.getAbsolutePath()); - } + if (!directory.exists() && !directory.mkdirs()) { + throw new IOException("无法创建目录: " + directory.getAbsolutePath()); } } @@ -170,20 +242,12 @@ public class UploadZipController { try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile), Charset.forName("ISO-8859-15"))) { ZipEntry zipEntry; while ((zipEntry = zis.getNextEntry()) != null) { + File destFile = newFile(extractDir, zipEntry); if (zipEntry.isDirectory()) { - // 是目录,不重命名,直接创建目录 - File newDir = new File(extractDir, zipEntry.getName()); - ensureDirectoryExists(newDir); + ensureDirectoryExists(destFile); } else { - // 是文件,进行重命名 - String originalFileName = zipEntry.getName(); - String newFileName = renameFile(originalFileName); - File newFile = new File(extractDir, newFileName); - // 为文件创建父目录 - ensureDirectoryExists(newFile.getParentFile()); - - // 写入文件内容 - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(newFile))) { + ensureDirectoryExists(destFile.getParentFile()); + try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile))) { byte[] bytesIn = new byte[4096]; int read; while ((read = zis.read(bytesIn)) != -1) { @@ -193,277 +257,181 @@ public class UploadZipController { } } } - - } private String renameFile(String originalFileName) { int lastIndexOfSlash = originalFileName.lastIndexOf('/'); int lastIndexOfDot = originalFileName.lastIndexOf('.'); - - // 获取文件所在的目录路径 String directoryPath = lastIndexOfSlash != -1 ? originalFileName.substring(0, lastIndexOfSlash + 1) : ""; - // 获取文件扩展名 String fileExtension = lastIndexOfDot != -1 ? originalFileName.substring(lastIndexOfDot) : ""; - // 获取文件名(不包含扩展名) String fileNameWithoutExtension = lastIndexOfSlash != -1 && lastIndexOfDot != -1 ? - originalFileName.substring(lastIndexOfSlash + 1, lastIndexOfDot) : - originalFileName; - - // 将 MD5 值转换为一个整数 - String s = DigestUtil.md5Hex(fileNameWithoutExtension); - - - return directoryPath + s + fileExtension; + originalFileName.substring(lastIndexOfSlash + 1, lastIndexOfDot) : originalFileName; + return directoryPath + DigestUtil.md5Hex(fileNameWithoutExtension) + fileExtension; } private File newFile(File destinationDir, ZipEntry zipEntry) throws IOException { File destFile = new File(destinationDir, zipEntry.getName()); - String destDirPath = destinationDir.getCanonicalPath(); String destFilePath = destFile.getCanonicalPath(); - if (!destFilePath.startsWith(destDirPath + File.separator)) { throw new IOException("Entry is outside of the target dir: " + zipEntry.getName()); } - return destFile; } - private void processExtractedFolder(File extractDir, Long recruitId) { - File[] firstLevelFiles = extractDir.listFiles(); + private void processExtractedFolder(List targetDirPaths, Long recruitId) { + List insuranceUserIds = new ArrayList<>(); // 存储需要删除保险附件的用户ID + List contractUserIds = new ArrayList<>(); // 存储需要删除合同附件的用户ID + List recruitApplyIds = new ArrayList<>(); // 存储关联的招工申请ID - // 保险 2 - List insurance = new ArrayList<>(); - // 劳务合同 1 - List contract = new ArrayList<>(); - // 招工申请Id - List recruitApplyIds = new ArrayList<>(); - - - if (firstLevelFiles != null) { - for (File firstLevelFile : firstLevelFiles) { - String firstLevelFolderName = firstLevelFile.getName(); - System.out.println("第一层文件夹名称: " + firstLevelFolderName); - String[] split = firstLevelFolderName.split("_"); - String card = split[1]; - WgzUser wgzUser = wgzUserService.findByIdentityCard(card); - if (wgzUser == null) { - throw new BaseException("文件格式错误"); - } - BgtProjectRecruitApply oneByUserIdAndRecruitId = recruitApplyService.getOneByUserIdAndRecruitId(wgzUser.getUserId(), recruitId); - if (oneByUserIdAndRecruitId == null) { - throw new BaseException("状态不对"); - } - recruitApplyIds.add(oneByUserIdAndRecruitId.getId()); - if (firstLevelFile.isDirectory()) { - File[] secondLevelFiles = firstLevelFile.listFiles(); - if (secondLevelFiles != null) { - for (File secondLevelFile : secondLevelFiles) { - String secondLevelFolderName = secondLevelFile.getName(); - System.out.println("第二层文件夹名称: " + secondLevelFolderName); - if (secondLevelFile.isDirectory()) { - File[] thirdLevelFiles = secondLevelFile.listFiles(); - if (thirdLevelFiles != null && thirdLevelFiles.length > 0) { - // 删除数据库里的附件 - if ("BaoXian".equals(secondLevelFolderName)) { - insurance.add(wgzUser.getUserId()); - } - if ("HeTong".equals(secondLevelFolderName)) { - contract.add(wgzUser.getUserId()); - } - } - } - } - } - } + // 遍历目标目录路径,解析关键信息 + for (String dirPath : targetDirPaths) { + // dirPath格式示例:"张三_110101199001011234/BaoXian/" 或 "李四_120101199002024567/HeTong/" + String[] pathParts = dirPath.split(Pattern.quote(File.separator)); + if (pathParts.length < 2) { + throw new BaseException("目录路径格式异常: " + dirPath); } + + // 解析倒数第二层目录(名字_身份证号)和类型目录(BaoXian/HeTong) + String secondLastDir = pathParts[0]; // 格式:名字_身份证号 + String lastDir = pathParts[1]; // 格式:BaoXian 或 HeTong(末尾可能有斜杠,需处理) + lastDir = lastDir.replaceAll("/$", ""); // 移除末尾斜杠 + + // 验证类型目录是否合法 + if (!"BaoXian".equals(lastDir) && !"HeTong".equals(lastDir)) { + continue; // 非目标类型目录,跳过 + } + + // 从"名字_身份证号"中提取身份证号 + String[] nameIdCard = secondLastDir.split("_"); + if (nameIdCard.length != 2) { + throw new BaseException("倒数第二层目录格式错误(需为'名字_身份证号'): " + secondLastDir); + } + String idCard = nameIdCard[1]; + + // 根据身份证号查询用户 + WgzUser wgzUser = wgzUserService.findByIdentityCard(idCard); + if (wgzUser == null) { + throw new BaseException("身份证号对应的用户不存在: " + idCard); + } + Long userId = wgzUser.getUserId(); + + // 查询用户对应的招工申请记录 + BgtProjectRecruitApply apply = recruitApplyService.getOneByUserIdAndRecruitId(userId, recruitId); + if (apply == null) { + throw new BaseException("用户未参与当前招工或申请记录不存在: 用户ID=" + userId + ", 招工ID=" + recruitId); + } + Long applyId = apply.getId(); + + // 根据类型分类收集数据 + if ("BaoXian".equals(lastDir)) { + insuranceUserIds.add(userId); + } else { + contractUserIds.add(userId); + } + recruitApplyIds.add(applyId); } - if (CollectionUtil.isNotEmpty(insurance)) { - annexService.deleteByUserIdAndRecruitIdAndType(insurance, recruitId, "2", recruitApplyIds); + + // 执行附件删除(与原逻辑一致,仅调整入参) + if (CollectionUtil.isNotEmpty(insuranceUserIds)) { + annexService.deleteByUserIdAndRecruitIdAndType(insuranceUserIds, recruitId, "2", recruitApplyIds); } - if (CollectionUtil.isNotEmpty(contract)) { - annexService.deleteByUserIdAndRecruitIdAndType(contract, recruitId, "1", recruitApplyIds); + if (CollectionUtil.isNotEmpty(contractUserIds)) { + annexService.deleteByUserIdAndRecruitIdAndType(contractUserIds, recruitId, "1", recruitApplyIds); } } - private void moveFilesToSaveDir(File sourceDir, Long recruitId, String username) throws IOException { - // 移动到 SAVE_DIR + private void moveFilesToSaveDir(File sourceDir, Long recruitId, String username, Map leafFileMap) throws IOException { File saveDestDir = new File(SAVE_DIR); ensureDirectoryExists(saveDestDir); - BgtProjectRecruit recruit = recruitService.queryById(recruitId); - String s = DigestUtil.md5Hex(recruit.getRecruitName()); - String firstLevelFolderName = recruit.getId() + "_" + s; + String firstLevelFolderName = recruit.getId() + "_" + DigestUtil.md5Hex(recruit.getRecruitName()); File firstLevelDestDir = new File(saveDestDir, firstLevelFolderName); ensureDirectoryExists(firstLevelDestDir); - File[] firstLevelFiles = sourceDir.listFiles(); List annexList = new ArrayList<>(); - if (firstLevelFiles != null) { - for (File firstLevelFile : firstLevelFiles) { - if (firstLevelFile.isDirectory()) { + for (Map.Entry entry : leafFileMap.entrySet()) { + String key = entry.getKey(); // 格式:倒数第二层/倒数第一层/文件名(如:张三_123/BaoXian/文件.pdf) + String relativePath = entry.getValue(); // 完整相对路径(如:张三_123/BaoXian/文件.pdf) - String firstLevelName = firstLevelFile.getName(); //解压目录 - File secondLevelDestDir = new File(firstLevelDestDir, firstLevelName); //保存目录 - - File[] secondLevelFiles = firstLevelFile.listFiles(); - if (secondLevelFiles != null) { - for (File secondLevelfile : secondLevelFiles) { - if (secondLevelfile.isDirectory()) { - String secondLevelName = secondLevelfile.getName(); //解压目录 - File thirdLevelDestDir = new File(secondLevelDestDir, secondLevelName); //保存目录 - - File[] thirdLevelFiles = secondLevelfile.listFiles(); - if (thirdLevelFiles != null && thirdLevelFiles.length > 0) { - deleteFolder(thirdLevelDestDir); - } - - } - } - } - moveFilesRecursively(firstLevelFile, firstLevelDestDir, annexList, recruitId, username); + // 原文件路径:解压目录 + 相对路径 + File sourceFile = new File(sourceDir,relativePath); + if (!sourceFile.exists() || !sourceFile.isFile()) { + continue; } - } + + // 拆分原文件路径为目录部分和文件名部分 + String originalFileName = sourceFile.getName(); // 原文件名(含扩展名) + String parentDirPath = key.substring(0, key.length() - originalFileName.length()); // 目录路径(如 "张三_123/BaoXian/") + + // 分离文件名主体和扩展名(例如 "原文件" 和 ".pdf") + int lastDotIndex = originalFileName.lastIndexOf('.'); + String fileNameWithoutExt = (lastDotIndex == -1) ? originalFileName : originalFileName.substring(0, lastDotIndex); + String fileExtension = (lastDotIndex == -1) ? "" : originalFileName.substring(lastDotIndex); + + // 生成MD5文件名(对文件名主体进行哈希,保留扩展名) + String md5FileName = DigestUtil.md5Hex(fileNameWithoutExt); // MD5哈希值 + String newFileName = md5FileName + fileExtension; // 新文件名(如 "d41d8cd98f00b204e9800998ecf8427e.pdf") + + // 构建目标文件路径(原目录结构 + MD5文件名) + String newFilePath = parentDirPath + newFileName; + File destFile = new File(firstLevelDestDir, newFilePath); + ensureDirectoryExists(destFile.getParentFile()); + + // 复制文件到目标目录 + try (InputStream in = new FileInputStream(sourceFile); + OutputStream out = new FileOutputStream(destFile)) { + byte[] buffer = new byte[4096]; + int length; + while ((length = in.read(buffer)) > 0) { + out.write(buffer, 0, length); + } + } + + // 记录附件信息(更新文件名为MD5后的值) + String newRelativePath = SAVE_DIR + File.separator + newFilePath; + newRelativePath = newRelativePath.replace("\\", "/").replace("ruoyi/uploadPath", "/profile"); + + String[] pathParts = relativePath.split(Pattern.quote(File.separator)); + String parentName = pathParts[pathParts.length - 3]; // 倒数第二层目录名(用户标识) + String dirName = pathParts[pathParts.length - 2]; // 倒数第一层目录名(BaoXian/HeTong) + + String[] userParts = parentName.split("_"); + String card = userParts[1]; + WgzUser wgzUser = wgzUserService.findByIdentityCard(card); + BgtProjectRecruitApply apply = recruitApplyService.getOneByUserIdAndRecruitId(wgzUser.getUserId(), recruitId); + + Annex annex = new Annex(); + annex.setAnnexName(newFileName); + annex.setAnnexUrl(newRelativePath); + annex.setAnnexType("BaoXian".equals(dirName) ? "2" : "1"); + annex.setUserType(Constants.WGZ); + annex.setUserId(wgzUser.getUserId()); + annex.setRecruitId(recruitId); + annex.setRecruitApplyId(apply.getId()); + annex.setCreateBy(username); + annex.setUpdateBy(username); + annexList.add(annex); } + if (CollectionUtil.isNotEmpty(annexList)) { annexService.saveBatch(annexList); } } - private void moveFilesRecursively(File source, File destination, List annexList, Long recruitId, String username) throws IOException { - if (source.isDirectory()) { - File newDir = new File(destination, source.getName()); - ensureDirectoryExists(newDir); - File[] files = source.listFiles(); - if (files != null) { - for (File file : files) { - moveFilesRecursively(file, newDir, annexList, recruitId, username); + @Async + public CompletableFuture asyncDeleteTempFiles(File extractDir, File zipFile) { + return CompletableFuture.runAsync(() -> { + try { + // 仅清理临时解压目录和ZIP文件 + deleteFolder(extractDir); + if (!zipFile.delete()) { + System.err.println("无法删除压缩文件: " + zipFile.getAbsolutePath()); } + } catch (Exception e) { + e.printStackTrace(); } - } else { - File destFile = new File(destination, source.getName()); - ensureDirectoryExists(destFile.getParentFile()); - try (InputStream in = new FileInputStream(source); - OutputStream out = new FileOutputStream(destFile)) { - byte[] buffer = new byte[4096]; - int length; - while ((length = in.read(buffer)) > 0) { - out.write(buffer, 0, length); - } - } - String relativePath = SAVE_DIR + File.separator + getRelativePath(source, new File(TEMP_DIR)); - relativePath = relativePath.replace("\\", "/").replace("ruoyi/uploadPath", "/profile"); - System.out.println("文件在项目里的相对目录: " + relativePath); - // 存到数据库 - String parentName = destination.getParentFile().getName(); - System.out.println("上上一级文件名: " + parentName); - String[] split = parentName.split("_"); - String card = split[1]; - WgzUser wgzUser = wgzUserService.findByIdentityCard(card); - - BgtProjectRecruitApply oneByUserIdAndRecruitId = recruitApplyService.getOneByUserIdAndRecruitId(wgzUser.getUserId(), recruitId); - - String name = destination.getName(); - System.out.println("上一级文件名: " + name); - String type = ""; - if ("BaoXian".equals(name)) { - type = "2"; - } - if ("HeTong".equals(name)) { - type = "1"; - } - Annex annex = new Annex(); - annex.setAnnexName(destFile.getName()); - annex.setAnnexUrl(relativePath); - annex.setAnnexType(type); - annex.setUserType(Constants.WGZ); - annex.setUserId(wgzUser.getUserId()); - annex.setRecruitId(recruitId); - annex.setRecruitApplyId(oneByUserIdAndRecruitId.getId()); - annex.setCreateBy(username); - annex.setUpdateBy(username); - annexList.add(annex); - } - } - - private void moveFilesToRecordDirRecursively(File source, File destination, String timeStamp, List annexRecordList, Long recruitId, String username, Long userId) throws IOException { - if (source.isDirectory()) { - String folderName = source.getName(); - String[] parts = folderName.split("_"); - if (parts.length > 0 && parts[0].matches("\\d+") && parts.length > 1 && parts[0].equals(recruitId.toString())) { - // 如果parts第一部分是数字并且长度大于1,跳过这一级目录的创建,直接处理下一级目录 - File[] files = source.listFiles(); - if (files != null) { - for (File file : files) { - moveFilesToRecordDirRecursively(file, destination, timeStamp, annexRecordList, recruitId, username, userId); - } - } - } else { - File newDir = new File(destination, folderName); - ensureDirectoryExists(newDir); - File[] files = source.listFiles(); - if (files != null) { - for (File file : files) { - moveFilesToRecordDirRecursively(file, newDir, timeStamp, annexRecordList, recruitId, username, userId); - } - } - } - } else { - // 获取文件名和扩展名 - String fileName = source.getName(); - int dotIndex = fileName.lastIndexOf('.'); - String nameWithoutExtension = dotIndex == -1 ? fileName : fileName.substring(0, dotIndex); - String extension = dotIndex == -1 ? "" : fileName.substring(dotIndex); - - // 在文件名后面添加时间戳 - String newFileName = nameWithoutExtension + "_" + timeStamp + extension; - File destFile = new File(destination, newFileName); - - ensureDirectoryExists(destFile.getParentFile()); - try (InputStream in = new FileInputStream(source); - OutputStream out = new FileOutputStream(destFile)) { - byte[] buffer = new byte[4096]; - int length; - while ((length = in.read(buffer)) > 0) { - out.write(buffer, 0, length); - } - } - - // 记录文件信息到数据库 - String relativePath = RECORD_DIR + File.separator + getRelativePath(destFile, new File(RECORD_DIR)); - relativePath = relativePath.replace("\\", "/").replace("ruoyi/uploadPath", "/profile"); - - String parentName = destination.getParentFile().getName(); - String[] split = parentName.split("_"); - String card = split[1]; - WgzUser wgzUser = wgzUserService.findByIdentityCard(card); - - BgtProjectRecruitApply oneByUserIdAndRecruitId = recruitApplyService.getOneByUserIdAndRecruitId(wgzUser.getUserId(), recruitId); - - String name = destination.getName(); - String type = ""; - if ("BaoXian".equals(name)) { - type = "2"; - } - if ("HeTong".equals(name)) { - type = "1"; - } - - AnnexRecord annexRecord = new AnnexRecord(); - annexRecord.setAnnexName(newFileName); - annexRecord.setAnnexUrl(relativePath); - annexRecord.setAnnexType(type); - annexRecord.setUserType(Constants.WGZ); - annexRecord.setUserId(wgzUser.getUserId()); - annexRecord.setRecruitId(recruitId); - annexRecord.setRecruitApplyId(oneByUserIdAndRecruitId.getId()); - annexRecord.setCreateBy(username); - annexRecord.setUpdateBy(username); - annexRecord.setCreateUserId(userId); - annexRecord.setCreateUserType(Constants.BGT); - annexRecordList.add(annexRecord); - } + }); } private String getRelativePath(File file, File baseDir) { diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/fbs/AppFbsMessageController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/fbs/AppFbsMessageController.java index d6a026b..2d6382f 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/fbs/AppFbsMessageController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/fbs/AppFbsMessageController.java @@ -3,6 +3,8 @@ package com.ruoyi.web.controller.fbs; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.util.MessageUtil; import com.ruoyi.fbs.domain.FbsMessage; import com.ruoyi.fbs.domain.dto.FbsMessageDetailDTO; import com.ruoyi.fbs.domain.dto.FbsMessageMyListDTO; @@ -29,7 +31,7 @@ import org.springframework.web.bind.annotation.*; public class AppFbsMessageController extends BaseController { private final IFbsMessageService iFbsMessageService; - + private final RedisCache redisCache; @ApiOperation("分包商未读消息统计") @GetMapping("/countUnread") public AjaxResult count() { @@ -54,10 +56,14 @@ public class AppFbsMessageController extends BaseController { @ApiOperation("分包商消息已读") @PutMapping("/read/{id}") public AjaxResult read(@PathVariable(value = "id") Long id) { - FbsMessage fbsMessage = new FbsMessage(); - fbsMessage.setId(id); - fbsMessage.setReadStatus("1"); - return AjaxResult.success(iFbsMessageService.updateById(fbsMessage)); + FbsMessage fbsMessage = iFbsMessageService.getById(id); + boolean b = true; + if("0".equals(fbsMessage.getReadStatus())){ + MessageUtil.readMessagePush(fbsMessage.getRecipientType(), fbsMessage.getRecipientId(), fbsMessage.getMessageLargeType(), fbsMessage.getReadStatus()); + fbsMessage.setReadStatus("1"); + b = iFbsMessageService.updateById(fbsMessage); + } + return AjaxResult.success(b); } @ApiOperation("分包商消息已操作") @@ -69,4 +75,10 @@ public class AppFbsMessageController extends BaseController { return AjaxResult.success(iFbsMessageService.updateById(fbsMessage)); } + @ApiOperation("消息详情") + @GetMapping("/detail/{id}") + public AjaxResult detail(@PathVariable(value = "id") Long id) { + return AjaxResult.success(iFbsMessageService.getById(id)); + } + } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wgz/WgzAppController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wgz/WgzAppController.java index c47da3e..f84471b 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wgz/WgzAppController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wgz/WgzAppController.java @@ -148,6 +148,15 @@ return AjaxResult.success(iWgzUserService.userPersonalBasicInformation(req)); } + /** + * 【我的】总收入 + */ + @GetMapping("/grossIncome") + public AjaxResult grossIncome() { + return AjaxResult.success(iWgzPayCalculationService.grossIncome()); + } + + /** * 【我的】【实名认证】实名认证 */ diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/zbf/AppZbfMessageController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/zbf/AppZbfMessageController.java index 713789e..95e5ff8 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/zbf/AppZbfMessageController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/zbf/AppZbfMessageController.java @@ -3,6 +3,7 @@ package com.ruoyi.web.controller.zbf; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.util.MessageUtil; import com.ruoyi.zbf.domain.ZbfMessage; import com.ruoyi.zbf.domain.dto.ZbfMessageDetailDTO; import com.ruoyi.zbf.domain.dto.ZbfMessageMyListDTO; @@ -29,7 +30,6 @@ import org.springframework.web.bind.annotation.*; public class AppZbfMessageController extends BaseController { private final IZbfMessageService iZbfMessageService; - /** * 查询消息列表 */ @@ -55,10 +55,14 @@ public class AppZbfMessageController extends BaseController { @ApiOperation("总包方消息已读") @PutMapping("/read/{id}") public AjaxResult read(@PathVariable(value = "id") Long id) { - ZbfMessage zbfMessage = new ZbfMessage(); - zbfMessage.setId(id); - zbfMessage.setReadStatus("1"); - return AjaxResult.success(iZbfMessageService.updateById(zbfMessage)); + ZbfMessage zbfMessage = iZbfMessageService.getById(id); + boolean b= true; + if("0".equals(zbfMessage.getReadStatus())){ + MessageUtil.readMessagePush(zbfMessage.getRecipientType(), zbfMessage.getRecipientId(), zbfMessage.getMessageLargeType(), zbfMessage.getReadStatus()); + zbfMessage.setReadStatus("1"); + iZbfMessageService.updateById(zbfMessage); + } + return AjaxResult.success(b); } @ApiOperation("总包方消息已操作") diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java index 83a30a8..98ee3e9 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -86,7 +86,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter * hasRole | 如果有参数,参数表示角色,则其角色可以访问 * permitAll | 用户可以任意访问 * rememberMe | 允许通过remember-me登录的用户访问 - * authenticated | 用户登录后可访问 + * authenticated | 用户登录后可访问 "/sse/subscribe", */ @Override protected void configure(HttpSecurity httpSecurity) throws Exception diff --git a/ruoyi-system/src/main/java/com/ruoyi/bgt/service/impl/BgtMessageServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/bgt/service/impl/BgtMessageServiceImpl.java index b2a742e..dcbd0e5 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/bgt/service/impl/BgtMessageServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/bgt/service/impl/BgtMessageServiceImpl.java @@ -17,12 +17,12 @@ import com.ruoyi.bgt.domain.vo.BgtMessageDetailVO; import com.ruoyi.bgt.domain.vo.BgtMessageVO; import com.ruoyi.bgt.mapper.BgtMessageMapper; import com.ruoyi.bgt.service.IBgtMessageService; -import com.ruoyi.bgt.service.IBgtProjectRecruitApplyService; -import com.ruoyi.bgt.service.IBgtProjectRecruitService; import com.ruoyi.bgt.service.IBgtWageApplicationService; import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl; import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.enums.BgtMessageType; +import com.ruoyi.common.util.MessageUtil; import com.ruoyi.common.utils.PageUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.fbs.domain.FbsProjectTask; @@ -32,7 +32,10 @@ import com.ruoyi.fbs.service.IFbsProjectTaskService; import com.ruoyi.wgz.domain.WgzDailyClock; import com.ruoyi.wgz.domain.WgzLeave; import com.ruoyi.wgz.domain.WgzReissueacard; -import com.ruoyi.wgz.service.*; +import com.ruoyi.wgz.service.IWgzDailyClockService; +import com.ruoyi.wgz.service.IWgzLeaveService; +import com.ruoyi.wgz.service.IWgzReissueacardService; +import com.ruoyi.wgz.service.IWgzUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -59,15 +62,7 @@ import static com.ruoyi.common.constants.WgzAndBgtMessageConstant.OPERATION_NEED public class BgtMessageServiceImpl extends ServicePlusImpl implements IBgtMessageService { @Autowired - @Lazy - private IBgtProjectRecruitApplyService recruitApplyService; - - @Autowired - private IBgtProjectRecruitService recruitService; - - @Autowired - @Lazy - private IWgzPayCalculationService payCalculationService; + private RedisCache redisCache; @Autowired @Lazy @@ -170,11 +165,14 @@ public class BgtMessageServiceImpl extends ServicePlusImpl bgtMessages = baseMapper.selectList(Wrappers.lambdaQuery() .eq(BgtMessage::getRecipientId, SecurityUtils.getAppUserId()) .eq(BgtMessage::getReadStatus, "0")); @@ -189,6 +187,7 @@ public class BgtMessageServiceImpl extends ServicePlusImpl=entity.getProgress()){ throw new BaseException("当前进度不能小于等于上一个进度"); } + if(entity.getProgress()==100){ + List recruitList = recruitService.list(Wrappers.lambdaQuery() + .eq(BgtProjectRecruit::getTaskId, entity.getTaskId()) + .ne(BgtProjectRecruit::getStatus, RecruitStatus.OVERDUE.getCode()) + ); + if(CollectionUtil.isNotEmpty(recruitList)){ + throw new BaseException("招工结束前不能完成任务"); + } + + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/common/constants/BgtMessageConstant.java b/ruoyi-system/src/main/java/com/ruoyi/common/constants/BgtMessageConstant.java index a4006ef..6fc7a0d 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/common/constants/BgtMessageConstant.java +++ b/ruoyi-system/src/main/java/com/ruoyi/common/constants/BgtMessageConstant.java @@ -7,6 +7,7 @@ import java.util.Map; public class BgtMessageConstant { // 公共常量 + public static final String REDIS_KEY = "messageCount:bgt:"; public static final String BGT_LARGE_TASK = "1"; //大类型-任务 public static final String BGT_LARGE_SETTLEMENT = "2"; //大类型-结算 diff --git a/ruoyi-system/src/main/java/com/ruoyi/common/constants/FbsMessageConstant.java b/ruoyi-system/src/main/java/com/ruoyi/common/constants/FbsMessageConstant.java index 40b9035..afb5e2d 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/common/constants/FbsMessageConstant.java +++ b/ruoyi-system/src/main/java/com/ruoyi/common/constants/FbsMessageConstant.java @@ -10,6 +10,7 @@ import static com.ruoyi.common.constants.BgtMessageConstant.SUBHEADING; public class FbsMessageConstant { // 公共常量 + public static final String REDIS_KEY = "messageCount:fbs:"; public static final String FBS_LARGE_TASK = "1"; //大类型-项目 public static final String FBS_LARGE_SETTLEMENT = "2"; //大类型-结算 diff --git a/ruoyi-system/src/main/java/com/ruoyi/common/constants/WgzAndBgtMessageConstant.java b/ruoyi-system/src/main/java/com/ruoyi/common/constants/WgzAndBgtMessageConstant.java index 8c9dbdb..58ab1fc 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/common/constants/WgzAndBgtMessageConstant.java +++ b/ruoyi-system/src/main/java/com/ruoyi/common/constants/WgzAndBgtMessageConstant.java @@ -3,12 +3,14 @@ package com.ruoyi.common.constants; import java.util.Map; public class WgzAndBgtMessageConstant { + + public static final String REDIS_KEY = "messageCount:wgz:"; // 公共常量 public static final String USERTYPE_SYSTEM = "0"; //系统 public static final String USERTYPE_WGZ = "1"; //务工者 public static final String USERTYPE_BGT = "2"; //包工头 public static final String USERTYPE_FBS = "3"; //分包商 - public static final String USERTYPE_ZBF = "4"; //分包商 + public static final String USERTYPE_ZBF = "4"; //总包方 //系统ID public static final Long SYSTEM_ID = 0L; diff --git a/ruoyi-system/src/main/java/com/ruoyi/common/constants/ZbfMessageConstant.java b/ruoyi-system/src/main/java/com/ruoyi/common/constants/ZbfMessageConstant.java index 13f2a8f..e7cee96 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/common/constants/ZbfMessageConstant.java +++ b/ruoyi-system/src/main/java/com/ruoyi/common/constants/ZbfMessageConstant.java @@ -11,6 +11,8 @@ import static com.ruoyi.common.constants.BgtMessageConstant.SUBHEADING; public class ZbfMessageConstant { // 公共常量 + public static final String REDIS_KEY = "messageCount:zbf:"; + public static final String ZBF_LARGE_TASK = "1"; //大类型-任务 public static final String ZBF_LARGE_SETTLEMENT = "2"; //大类型-结算 public static final String ZBF_LARGE_OTHER = "3"; //大类型-其它 diff --git a/ruoyi-system/src/main/java/com/ruoyi/common/domain/dto/CompanyAuthenticateDTO.java b/ruoyi-system/src/main/java/com/ruoyi/common/domain/dto/CompanyAuthenticateDTO.java index 4004a90..d2d2977 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/common/domain/dto/CompanyAuthenticateDTO.java +++ b/ruoyi-system/src/main/java/com/ruoyi/common/domain/dto/CompanyAuthenticateDTO.java @@ -64,4 +64,6 @@ public class CompanyAuthenticateDTO { @ApiModelProperty("资质与荣誉") private String url; + @ApiModelProperty("备注") + private String remark; } diff --git a/ruoyi-system/src/main/java/com/ruoyi/common/util/MessageUtil.java b/ruoyi-system/src/main/java/com/ruoyi/common/util/MessageUtil.java new file mode 100644 index 0000000..be577f2 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/common/util/MessageUtil.java @@ -0,0 +1,297 @@ +package com.ruoyi.common.util; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.ruoyi.bgt.domain.vo.BgtMessageCountVO; +import com.ruoyi.common.constants.BgtMessageConstant; +import com.ruoyi.common.constants.FbsMessageConstant; +import com.ruoyi.common.constants.WgzAndBgtMessageConstant; +import com.ruoyi.common.constants.ZbfMessageConstant; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.fbs.domain.vo.FbsMessageCountVO; +import com.ruoyi.wgz.bo.res.WgzAppMessageTypeStatisticsRes; +import com.ruoyi.zbf.domain.vo.ZbfMessageCountVO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +import static com.ruoyi.common.constants.BgtMessageConstant.*; +import static com.ruoyi.common.constants.FbsMessageConstant.*; +import static com.ruoyi.common.constants.WgzAndBgtMessageConstant.OPERATION_NEED; +import static com.ruoyi.common.constants.ZbfMessageConstant.*; + +public class MessageUtil { + + //获取日志 + public static final Logger log = LoggerFactory.getLogger(MessageUtil.class); + + /** + * 保存消息是像客户端发送消息 + * + * @param recipientType 用户类型 + * @param recipientId 用户ID + * @param messageLargeType 消息大类型 + * @param isOperation 操作类型 + */ + public static void saveMessagePush(String recipientType, Long recipientId, String messageLargeType, String isOperation) { + try { + String messageKey = ""; + String data = ""; + String redisKey = ""; + Object cacheObject; + RedisCache redisCache = SpringUtils.getBean(RedisCache.class); + switch (recipientType) { + case WgzAndBgtMessageConstant.USERTYPE_WGZ: + redisKey = WgzAndBgtMessageConstant.REDIS_KEY + recipientId; + messageKey = SseUtil.WGZ_PREFIX + recipientId; + cacheObject = redisCache.getCacheObject(redisKey); + if (cacheObject != null) { + WgzAppMessageTypeStatisticsRes res = (WgzAppMessageTypeStatisticsRes) cacheObject; + Map mp = res.getMp(); + if ("0".equals(messageLargeType)) { + mp.put("0", mp.get("0") + 1); + } else if ("1".equals(messageLargeType)) { + mp.put("1", mp.get("1") + 1); + } else if ("2".equals(messageLargeType)) { + mp.put("2", mp.get("2") + 1); + } + if (OPERATION_NEED.equals(isOperation)) { + mp.put("3", mp.get("3") + 1); + } + redisCache.setCacheObject(redisKey, res); + data = JSONUtil.toJsonStr(res); + } + break; + case WgzAndBgtMessageConstant.USERTYPE_BGT: + redisKey = BgtMessageConstant.REDIS_KEY + recipientId; + messageKey = SseUtil.BGT_PREFIX + recipientId; + cacheObject = redisCache.getCacheObject(redisKey); + if (cacheObject != null) { + BgtMessageCountVO bgtMessageCountVO = (BgtMessageCountVO) cacheObject; + if (BGT_LARGE_TASK.equals(messageLargeType)) { + bgtMessageCountVO.setTaskMessageCount(bgtMessageCountVO.getTaskMessageCount() + 1); + } else if (BGT_LARGE_SETTLEMENT.equals(messageLargeType)) { + bgtMessageCountVO.setSettlementMessageCount(bgtMessageCountVO.getSettlementMessageCount() + 1); + } else if (BGT_LARGE_OTHER.equals(messageLargeType)) { + bgtMessageCountVO.setOtherMessageCount(bgtMessageCountVO.getOtherMessageCount() + 1); + } + if (OPERATION_NEED.equals(isOperation)) { + bgtMessageCountVO.setHandleMessageCount(bgtMessageCountVO.getHandleMessageCount() + 1); + } + redisCache.setCacheObject(redisKey, bgtMessageCountVO); + data = JSONUtil.toJsonStr(bgtMessageCountVO); + } + break; + case WgzAndBgtMessageConstant.USERTYPE_FBS: + redisKey = FbsMessageConstant.REDIS_KEY + recipientId; + messageKey = SseUtil.FBS_PREFIX + recipientId; + cacheObject = redisCache.getCacheObject(redisKey); + if (cacheObject != null) { + FbsMessageCountVO fbsMessageCountVO = (FbsMessageCountVO) cacheObject; + if (FBS_LARGE_TASK.equals(messageLargeType)) { + fbsMessageCountVO.setTaskMessageCount(fbsMessageCountVO.getTaskMessageCount() + 1); + } else if (FBS_LARGE_SETTLEMENT.equals(messageLargeType)) { + fbsMessageCountVO.setSettlementMessageCount(fbsMessageCountVO.getSettlementMessageCount() + 1); + } else if (FBS_LARGE_OTHER.equals(messageLargeType)) { + fbsMessageCountVO.setOtherMessageCount(fbsMessageCountVO.getOtherMessageCount() + 1); + } + if (OPERATION_NEED.equals(isOperation)) { + fbsMessageCountVO.setHandleMessageCount(fbsMessageCountVO.getHandleMessageCount() + 1); + } + redisCache.setCacheObject(redisKey, fbsMessageCountVO); + data = JSONUtil.toJsonStr(fbsMessageCountVO); + } + break; + case WgzAndBgtMessageConstant.USERTYPE_ZBF: + redisKey = ZbfMessageConstant.REDIS_KEY + recipientId; + messageKey = SseUtil.ZBF_PREFIX + recipientId; + cacheObject = redisCache.getCacheObject(redisKey); + if (cacheObject != null) { + ZbfMessageCountVO zbfMessageCountVO = (ZbfMessageCountVO) cacheObject; + if (ZBF_LARGE_TASK.equals(messageLargeType)) { + zbfMessageCountVO.setTaskMessageCount(zbfMessageCountVO.getTaskMessageCount() + 1); + } else if (ZBF_LARGE_SETTLEMENT.equals(messageLargeType)) { + zbfMessageCountVO.setSettlementMessageCount(zbfMessageCountVO.getSettlementMessageCount() + 1); + } else if (ZBF_LARGE_OTHER.equals(messageLargeType)) { + zbfMessageCountVO.setOtherMessageCount(zbfMessageCountVO.getOtherMessageCount() + 1); + } + if (OPERATION_NEED.equals(isOperation)) { + zbfMessageCountVO.setHandleMessageCount(zbfMessageCountVO.getHandleMessageCount() + 1); + } + redisCache.setCacheObject(redisKey, zbfMessageCountVO); + data = JSONUtil.toJsonStr(zbfMessageCountVO); + } + break; + default: + break; + } + if(StrUtil.isNotBlank(data)){ + SseUtil.pushMessage(messageKey, data); + } + } catch (Exception e) { + log.error("sse推送异常", e); + } + } + + + public static void readMessagePush(String recipientType, Long recipientId, String messageLargeType, String readStatus) { + try { + String messageKey = ""; + String data = ""; + String redisKey; + Object cacheObject; + RedisCache redisCache = SpringUtils.getBean(RedisCache.class); + switch (recipientType) { + case WgzAndBgtMessageConstant.USERTYPE_WGZ: + redisKey = WgzAndBgtMessageConstant.REDIS_KEY + recipientId; + messageKey = SseUtil.WGZ_PREFIX + recipientId; + + cacheObject = redisCache.getCacheObject(redisKey); + if (cacheObject != null && "0".equals(readStatus)) { + WgzAppMessageTypeStatisticsRes res = (WgzAppMessageTypeStatisticsRes) cacheObject; + Map mp = res.getMp(); + if ("0".equals(messageLargeType)) { + mp.put("0", mp.get("0") - 1); + } else if ("1".equals(messageLargeType)) { + mp.put("1", mp.get("1") - 1); + } else if ("2".equals(messageLargeType)) { + mp.put("2", mp.get("2") - 1); + } + redisCache.setCacheObject(redisKey, res); + data = JSONUtil.toJsonStr(res); + } + break; + case WgzAndBgtMessageConstant.USERTYPE_BGT: + redisKey = BgtMessageConstant.REDIS_KEY + recipientId; + messageKey = SseUtil.BGT_PREFIX + recipientId; + + cacheObject = redisCache.getCacheObject(redisKey); + if (cacheObject != null && "0".equals(readStatus)) { + BgtMessageCountVO bgtMessageCountVO = (BgtMessageCountVO) cacheObject; + if (BGT_LARGE_TASK.equals(messageLargeType)) { + bgtMessageCountVO.setTaskMessageCount(bgtMessageCountVO.getTaskMessageCount() - 1); + } else if (BGT_LARGE_SETTLEMENT.equals(messageLargeType)) { + bgtMessageCountVO.setSettlementMessageCount(bgtMessageCountVO.getSettlementMessageCount() - 1); + } else if (BGT_LARGE_OTHER.equals(messageLargeType)) { + bgtMessageCountVO.setOtherMessageCount(bgtMessageCountVO.getOtherMessageCount() - 1); + } + redisCache.setCacheObject(redisKey, bgtMessageCountVO); + data = JSONUtil.toJsonStr(bgtMessageCountVO); + } + + break; + case WgzAndBgtMessageConstant.USERTYPE_FBS: + redisKey = FbsMessageConstant.REDIS_KEY + recipientId; + messageKey = SseUtil.FBS_PREFIX + recipientId; + + cacheObject = redisCache.getCacheObject(redisKey); + if (cacheObject != null && "0".equals(readStatus)) { + FbsMessageCountVO fbsMessageCountVO = (FbsMessageCountVO) cacheObject; + if (FBS_LARGE_TASK.equals(messageLargeType)) { + fbsMessageCountVO.setTaskMessageCount(fbsMessageCountVO.getTaskMessageCount() - 1); + } else if (FBS_LARGE_SETTLEMENT.equals(messageLargeType)) { + fbsMessageCountVO.setSettlementMessageCount(fbsMessageCountVO.getSettlementMessageCount() - 1); + } else if (FBS_LARGE_OTHER.equals(messageLargeType)) { + fbsMessageCountVO.setOtherMessageCount(fbsMessageCountVO.getOtherMessageCount() - 1); + } + redisCache.setCacheObject(redisKey, fbsMessageCountVO); + data = JSONUtil.toJsonStr(fbsMessageCountVO); + } + break; + case WgzAndBgtMessageConstant.USERTYPE_ZBF: + redisKey = ZbfMessageConstant.REDIS_KEY + recipientId; + messageKey = SseUtil.ZBF_PREFIX + recipientId; + + cacheObject = redisCache.getCacheObject(redisKey); + if (cacheObject != null && "0".equals(readStatus)) { + ZbfMessageCountVO zbfMessageCountVO = (ZbfMessageCountVO) cacheObject; + if (ZBF_LARGE_TASK.equals(messageLargeType)) { + zbfMessageCountVO.setTaskMessageCount(zbfMessageCountVO.getTaskMessageCount() - 1); + } else if (ZBF_LARGE_SETTLEMENT.equals(messageLargeType)) { + zbfMessageCountVO.setSettlementMessageCount(zbfMessageCountVO.getSettlementMessageCount() - 1); + } else if (ZBF_LARGE_OTHER.equals(messageLargeType)) { + zbfMessageCountVO.setOtherMessageCount(zbfMessageCountVO.getOtherMessageCount() - 1); + } + redisCache.setCacheObject(redisKey, zbfMessageCountVO); + data = JSONUtil.toJsonStr(zbfMessageCountVO); + } + break; + default: + break; + } + if(StrUtil.isNotBlank(data)){ + SseUtil.pushMessage(messageKey, data); + } + } catch (Exception e) { + log.error("sse推送异常", e); + } + } + + public static void operationMessagePush(String recipientType, Long recipientId, Integer num) { + try { + String messageKey = ""; + String data = ""; + String redisKey; + Object cacheObject; + RedisCache redisCache = SpringUtils.getBean(RedisCache.class); + switch (recipientType) { + case WgzAndBgtMessageConstant.USERTYPE_WGZ: + redisKey = WgzAndBgtMessageConstant.REDIS_KEY + recipientId; + messageKey = SseUtil.WGZ_PREFIX + recipientId; + cacheObject = redisCache.getCacheObject(redisKey); + if (cacheObject != null) { + WgzAppMessageTypeStatisticsRes res = (WgzAppMessageTypeStatisticsRes) cacheObject; + Map mp = res.getMp(); + mp.put("3", mp.get("3") - num); + redisCache.setCacheObject(redisKey, res); + data = JSONUtil.toJsonStr(res); + } + break; + case WgzAndBgtMessageConstant.USERTYPE_BGT: + redisKey = BgtMessageConstant.REDIS_KEY + recipientId; + messageKey = SseUtil.BGT_PREFIX + recipientId; + cacheObject = redisCache.getCacheObject(redisKey); + if (cacheObject != null) { + BgtMessageCountVO bgtMessageCountVO = (BgtMessageCountVO) cacheObject; + bgtMessageCountVO.setHandleMessageCount(bgtMessageCountVO.getHandleMessageCount() - num); + redisCache.setCacheObject(redisKey, bgtMessageCountVO); + data = JSONUtil.toJsonStr(bgtMessageCountVO); + } + + break; + case WgzAndBgtMessageConstant.USERTYPE_FBS: + redisKey = FbsMessageConstant.REDIS_KEY + recipientId; + messageKey = SseUtil.FBS_PREFIX + recipientId; + cacheObject = redisCache.getCacheObject(redisKey); + if (cacheObject != null) { + FbsMessageCountVO fbsMessageCountVO = (FbsMessageCountVO) cacheObject; + fbsMessageCountVO.setHandleMessageCount(fbsMessageCountVO.getHandleMessageCount() - num); + redisCache.setCacheObject(redisKey, fbsMessageCountVO); + data = JSONUtil.toJsonStr(fbsMessageCountVO); + } + + break; + case WgzAndBgtMessageConstant.USERTYPE_ZBF: + redisKey = ZbfMessageConstant.REDIS_KEY + recipientId; + messageKey = SseUtil.ZBF_PREFIX + recipientId; + cacheObject = redisCache.getCacheObject(redisKey); + if (cacheObject != null) { + ZbfMessageCountVO zbfMessageCountVO = (ZbfMessageCountVO) cacheObject; + zbfMessageCountVO.setHandleMessageCount(zbfMessageCountVO.getHandleMessageCount() - num); + redisCache.setCacheObject(redisKey, zbfMessageCountVO); + data = JSONUtil.toJsonStr(zbfMessageCountVO); + } + break; + default: + break; + } + if(StrUtil.isNotBlank(data)){ + SseUtil.pushMessage(messageKey, data); + } + } catch (Exception e) { + log.error("sse推送异常", e); + } + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/common/util/SseUtil.java b/ruoyi-system/src/main/java/com/ruoyi/common/util/SseUtil.java new file mode 100644 index 0000000..bda1c30 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/common/util/SseUtil.java @@ -0,0 +1,75 @@ +package com.ruoyi.common.util; + +import cn.hutool.json.JSONUtil; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.spring.SpringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class SseUtil { + //日志 + private static final Logger log = LoggerFactory.getLogger(SseUtil.class); + + private final static Map emitterMap = new ConcurrentHashMap<>(); + + public static final String WGZ_PREFIX = "wgz-"; + public static final String BGT_PREFIX = "bgt-"; + public static final String FBS_PREFIX = "fbs-"; + public static final String ZBF_PREFIX = "zbf-"; + public static SseEmitter subscribe(String userId,String prefix) { + // Spring Boot 2.4.7中SseEmitter构造函数支持超时时间(单位:毫秒) + SseEmitter emitter = new SseEmitter(1 * 60 * 1000L); // 30分钟超时 + + // 存储emitter并设置回调(2.4.7中回调机制与主流版本一致) + String key = prefix + "-" +userId; + emitterMap.put(key, emitter); + + // 利用闭包特性捕获userId和prefix + emitter.onCompletion(() -> emitterMap.remove(key)); + emitter.onTimeout(() -> { + emitter.complete(); + emitterMap.remove(key); + log.info("用户{}连接已超时", key); + }); + emitter.onError(e -> emitterMap.remove(key)); + + String redisKey = "messageCount:"+prefix+":"+userId; + + Object cacheObj = SpringUtils.getBean(RedisCache.class).getCacheObject(redisKey); +// String name = cacheObj == null ? "connect" + userId : "messageCount"; + String data = cacheObj == null ? "连接已建立,用户ID:" + userId : JSONUtil.toJsonStr(cacheObj); + // 发送初始连接确认 + try { + emitter.send(SseEmitter.event() +// .id("init") +// .name(name) + .data(data) + ); + } catch (IOException e) { + emitter.completeWithError(e); + } + return emitter; + } + + public static void pushMessage(String key,String data){ + SseEmitter emitter = emitterMap.get(key); + if (emitter != null) { + try { + // 发送自定义事件(客户端通过事件名监听) + emitter.send(SseEmitter.event() +// .id(String.valueOf(System.currentTimeMillis())) +// .name("messageCount") + .data(data) + ); + } catch (IOException e) { + emitter.completeWithError(e); + emitterMap.remove(key); // 异常时清理无效连接 + } + } + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/fbs/domain/vo/FbsProjectDetailVO.java b/ruoyi-system/src/main/java/com/ruoyi/fbs/domain/vo/FbsProjectDetailVO.java index c5de529..d9077c0 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/fbs/domain/vo/FbsProjectDetailVO.java +++ b/ruoyi-system/src/main/java/com/ruoyi/fbs/domain/vo/FbsProjectDetailVO.java @@ -46,4 +46,6 @@ public class FbsProjectDetailVO { @ApiModelProperty("标段列表") private List sectionList; + @ApiModelProperty("备注") + private String remark; } diff --git a/ruoyi-system/src/main/java/com/ruoyi/fbs/domain/vo/FbsProjectSectionListVO.java b/ruoyi-system/src/main/java/com/ruoyi/fbs/domain/vo/FbsProjectSectionListVO.java index 40547e9..6f710dc 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/fbs/domain/vo/FbsProjectSectionListVO.java +++ b/ruoyi-system/src/main/java/com/ruoyi/fbs/domain/vo/FbsProjectSectionListVO.java @@ -22,6 +22,11 @@ public class FbsProjectSectionListVO { @ApiModelProperty("项目地址") private String projectAddress; + + @ApiModelProperty("标段描述") + private String sectionDescribe; + + @ApiModelProperty("分包列表") private List subList; diff --git a/ruoyi-system/src/main/java/com/ruoyi/fbs/service/impl/FbsMessageServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/fbs/service/impl/FbsMessageServiceImpl.java index da90b63..688870d 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/fbs/service/impl/FbsMessageServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/fbs/service/impl/FbsMessageServiceImpl.java @@ -6,9 +6,12 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.common.constants.FbsMessageConstant; import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl; import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.enums.BgtMessageType; +import com.ruoyi.common.util.MessageUtil; import com.ruoyi.common.utils.PageUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.fbs.bo.FbsMessageQueryBo; @@ -20,6 +23,7 @@ import com.ruoyi.fbs.domain.vo.FbsMessageDetailVO; import com.ruoyi.fbs.domain.vo.FbsMessageVO; import com.ruoyi.fbs.mapper.FbsMessageMapper; import com.ruoyi.fbs.service.IFbsMessageService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.time.LocalTime; @@ -41,6 +45,9 @@ import static com.ruoyi.common.constants.WgzAndBgtMessageConstant.OPERATION_NEED @Service public class FbsMessageServiceImpl extends ServicePlusImpl implements IFbsMessageService { + @Autowired + private RedisCache redisCache; + @Override public FbsMessage queryById(Long id){ return getById(id); @@ -122,6 +129,8 @@ public class FbsMessageServiceImpl extends ServicePlusImpl { * 修改已读未读状态 */ Boolean userReadUnread(@Validated WgzAppReadUnreadReq req); + + /** + * 修改操作状态 + */ + Boolean updateOperationStatus(Long id); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/wgz/service/IWgzPayCalculationService.java b/ruoyi-system/src/main/java/com/ruoyi/wgz/service/IWgzPayCalculationService.java index 7ff6320..2b8ebbe 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/wgz/service/IWgzPayCalculationService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/wgz/service/IWgzPayCalculationService.java @@ -153,4 +153,9 @@ public interface IWgzPayCalculationService extends IServicePlus getPassListByTaskIds(List taskIds); + + /** + * 总收入 + */ + BigDecimal grossIncome(); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/wgz/service/impl/WgzMessageServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/wgz/service/impl/WgzMessageServiceImpl.java index 8c48e66..4cab277 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/wgz/service/impl/WgzMessageServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/wgz/service/impl/WgzMessageServiceImpl.java @@ -8,10 +8,13 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.bgt.domain.BgtProjectRecruit; import com.ruoyi.bgt.service.IBgtProjectRecruitApplyService; import com.ruoyi.bgt.service.IBgtProjectRecruitService; +import com.ruoyi.common.constants.WgzAndBgtMessageConstant; import com.ruoyi.common.core.domain.entity.SysDictData; import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl; import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.service.IAnnexService; +import com.ruoyi.common.util.MessageUtil; import com.ruoyi.common.utils.PageUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.system.service.ISysDictTypeService; @@ -69,6 +72,8 @@ public class WgzMessageServiceImpl extends ServicePlusImpl 0; + WgzMessage wgzMessage = baseMapper.selectById(req.getMessageId()); + boolean b = true; + if("0".equals(wgzMessage.getReadStatus())){ + MessageUtil.readMessagePush(wgzMessage.getRecipientType(), wgzMessage.getRecipientId(), wgzMessage.getMessageLargeType(), wgzMessage.getReadStatus()); + wgzMessage.setReadStatus("1"); + b = baseMapper.updateById(wgzMessage) > 0; + } + + return b; } + @Override + public Boolean updateOperationStatus(Long id) { + WgzMessage wgzMessage = baseMapper.selectById(id); + MessageUtil.operationMessagePush(wgzMessage.getRecipientType(), wgzMessage.getRecipientId(), 1); + wgzMessage.setIsOperation("2"); + return baseMapper.updateById(wgzMessage) > 0; + + } /** * 判断招工是否已招满或已过期 diff --git a/ruoyi-system/src/main/java/com/ruoyi/wgz/service/impl/WgzPayCalculationServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/wgz/service/impl/WgzPayCalculationServiceImpl.java index 7b83a0b..5d68ed4 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/wgz/service/impl/WgzPayCalculationServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/wgz/service/impl/WgzPayCalculationServiceImpl.java @@ -701,4 +701,19 @@ public class WgzPayCalculationServiceImpl extends ServicePlusImpl wrapper = new LambdaQueryWrapper<>(); + wrapper.in(WgzPayCalculation::getUserId, SecurityUtils.getAppUserId()) + .eq(WgzPayCalculation::getAuditorType, AuditStatus.PASS.getCode()); + List list = list(wrapper); + BigDecimal total = BigDecimal.ZERO; + for (WgzPayCalculation wgzPayCalculation : list) { + //金额*天数=实际工资 + total = total.add(wgzPayCalculation.getRecruitAmount().multiply(new BigDecimal(wgzPayCalculation.getNum()))); + } + + return total; + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/zbf/domain/dto/ZbfSubSwitchListDTO.java b/ruoyi-system/src/main/java/com/ruoyi/zbf/domain/dto/ZbfSubSwitchListDTO.java index 02d56cd..d7ebd34 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/zbf/domain/dto/ZbfSubSwitchListDTO.java +++ b/ruoyi-system/src/main/java/com/ruoyi/zbf/domain/dto/ZbfSubSwitchListDTO.java @@ -16,4 +16,7 @@ public class ZbfSubSwitchListDTO { @ApiModelProperty("总包方用户") private Long zbfUserId; + + @ApiModelProperty("项目ID") + private Long projectId; } diff --git a/ruoyi-system/src/main/java/com/ruoyi/zbf/domain/vo/ZbfProjectSectionListVO.java b/ruoyi-system/src/main/java/com/ruoyi/zbf/domain/vo/ZbfProjectSectionListVO.java index 8eb6263..b89ece2 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/zbf/domain/vo/ZbfProjectSectionListVO.java +++ b/ruoyi-system/src/main/java/com/ruoyi/zbf/domain/vo/ZbfProjectSectionListVO.java @@ -19,6 +19,9 @@ public class ZbfProjectSectionListVO { @ApiModelProperty("标段名称") private String sectionName; + @ApiModelProperty("标段描述") + private String sectionDescribe; + @ApiModelProperty("项目地址") private String projectAddress; diff --git a/ruoyi-system/src/main/java/com/ruoyi/zbf/service/impl/ZbfMessageServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/zbf/service/impl/ZbfMessageServiceImpl.java index 1be8bca..1cd5f01 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/zbf/service/impl/ZbfMessageServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/zbf/service/impl/ZbfMessageServiceImpl.java @@ -6,9 +6,12 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.common.constants.ZbfMessageConstant; import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl; import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.enums.BgtMessageType; +import com.ruoyi.common.util.MessageUtil; import com.ruoyi.common.utils.PageUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.zbf.bo.ZbfMessageQueryBo; @@ -20,6 +23,7 @@ import com.ruoyi.zbf.domain.vo.ZbfMessageDetailVO; import com.ruoyi.zbf.domain.vo.ZbfMessageVO; import com.ruoyi.zbf.mapper.ZbfMessageMapper; import com.ruoyi.zbf.service.IZbfMessageService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.time.LocalTime; @@ -40,6 +44,8 @@ import static com.ruoyi.common.constants.WgzAndBgtMessageConstant.OPERATION_NEED @Service public class ZbfMessageServiceImpl extends ServicePlusImpl implements IZbfMessageService { + @Autowired + private RedisCache redisCache; @Override public ZbfMessage queryById(Long id){ return getById(id); @@ -107,7 +113,9 @@ public class ZbfMessageServiceImpl extends ServicePlusImpl fbsProgressList(FbsProgressListDTO dto) { Page fbsProgressListVOPage = new Page<>(); //查询所有创建的任务 - List taskList = fbsProjectTaskService.list(Wrappers.lambdaQuery() + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() .eq(FbsProjectTask::getCreateId, SecurityUtils.getAppUserId()) .eq(FbsProjectTask::getProjectId, dto.getProjectId()) - ); - List taskIds = taskList.stream().map(FbsProjectTask::getId).collect(Collectors.toList()); + .orderByDesc(FbsProjectTask::getId); + Page fbsProjectTaskPage = fbsProjectTaskService.getBaseMapper().selectPage(PageUtils.buildPage(), wrapper); + List taskIds = fbsProjectTaskPage.getRecords().stream().map(FbsProjectTask::getId).collect(Collectors.toList()); if (CollectionUtil.isNotEmpty(taskIds)) { - TableDataInfo progressByTaskIds = bgtProjectTaskProgressService.getProgressByTaskIds(dto.getPageSize(), dto.getPageNum(), taskIds); - fbsProgressListVOPage.setTotal(progressByTaskIds.getTotal()); - fbsProgressListVOPage.setRecords(BeanUtil.copyToList(progressByTaskIds.getRows(), FbsProgressListVO.class)); + List progressByTaskIds = bgtProjectTaskProgressService.getProgressByTaskIds(taskIds); + Map map = progressByTaskIds.stream().collect(Collectors.toMap(BgtProjectTaskProgress::getTaskId, BgtProjectTaskProgress::getProgress)); + ArrayList fbsProgressListVOS = new ArrayList<>(); + for (FbsProjectTask fbsProjectTask : fbsProjectTaskPage.getRecords()) { + FbsProgressListVO fbsProgressListVO = new FbsProgressListVO(); + fbsProgressListVO.setId(fbsProjectTask.getId()); + fbsProgressListVO.setTaskName(fbsProjectTask.getTaskName()); + fbsProgressListVO.setProgress(map.get(fbsProjectTask.getId())==null?0:map.get(fbsProjectTask.getId())); + fbsProgressListVOS.add(fbsProgressListVO); + } + fbsProgressListVOPage.setTotal(fbsProjectTaskPage.getTotal()); + fbsProgressListVOPage.setRecords(fbsProgressListVOS); } return PageUtils.buildDataInfo(fbsProgressListVOPage); } @@ -586,8 +590,9 @@ public class ZbfProjectServiceImpl extends ServicePlusImpl payList = bgtWageApplicationService.getPassListByTaskIds(subIds); + if (CollectionUtil.isNotEmpty(taskList)) { + List taskIds = taskList.stream().map(FbsProjectTask::getId).collect(Collectors.toList()); + List payList = bgtWageApplicationService.getPassListByTaskIds(taskIds); BigDecimal payAmount = payList.stream().map(BgtWageApplication::getApplicantAmount).reduce(BigDecimal.ZERO, BigDecimal::add); vo.setPayAmount(payAmount); } @@ -867,7 +872,8 @@ public class ZbfProjectServiceImpl extends ServicePlusImpl collect = list.stream().map(FbsProjectTask::getUserId).collect(Collectors.toSet()); + zbfPersonCountVO.setBgtCount(collect.size()); List taskIds = list.stream().map(FbsProjectTask::getId).collect(Collectors.toList()); zbfPersonCountVO.setWgzCount(0); if (CollectionUtil.isNotEmpty(taskIds)) { @@ -924,14 +930,24 @@ public class ZbfProjectServiceImpl extends ServicePlusImpl zbfProgressList(ZbfProgressListDTO dto) { Page voPage = new Page<>(); //查询所有创建的任务 - List taskList = fbsProjectTaskService.list(Wrappers.lambdaQuery() + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() .eq(FbsProjectTask::getProjectId, dto.getProjectId()) - ); - List taskIds = taskList.stream().map(FbsProjectTask::getId).collect(Collectors.toList()); + .orderByDesc(FbsProjectTask::getId); + Page fbsProjectTaskPage = fbsProjectTaskService.getBaseMapper().selectPage(PageUtils.buildPage(), wrapper); + List taskIds = fbsProjectTaskPage.getRecords().stream().map(FbsProjectTask::getId).collect(Collectors.toList()); if (CollectionUtil.isNotEmpty(taskIds)) { - TableDataInfo progressByTaskIds = bgtProjectTaskProgressService.getProgressByTaskIds(dto.getPageSize(), dto.getPageNum(), taskIds); - voPage.setTotal(progressByTaskIds.getTotal()); - voPage.setRecords(BeanUtil.copyToList(progressByTaskIds.getRows(), ZbfProgressListVO.class)); + List progressByTaskIds = bgtProjectTaskProgressService.getProgressByTaskIds(taskIds); + Map map = progressByTaskIds.stream().collect(Collectors.toMap(BgtProjectTaskProgress::getTaskId, BgtProjectTaskProgress::getProgress)); + ArrayList zbfProgressListVOS = new ArrayList<>(); + for (FbsProjectTask fbsProjectTask : fbsProjectTaskPage.getRecords()) { + ZbfProgressListVO zbfProgressListVO = new ZbfProgressListVO(); + zbfProgressListVO.setId(fbsProjectTask.getId()); + zbfProgressListVO.setTaskName(fbsProjectTask.getTaskName()); + zbfProgressListVO.setProgress(map.get(fbsProjectTask.getId())==null?0:map.get(fbsProjectTask.getId())); + zbfProgressListVOS.add(zbfProgressListVO); + } + voPage.setTotal(fbsProjectTaskPage.getTotal()); + voPage.setRecords(zbfProgressListVOS); } return PageUtils.buildDataInfo(voPage); } @@ -980,6 +996,7 @@ public class ZbfProjectServiceImpl extends ServicePlusImpl wrapper = new LambdaQueryWrapper<>(); wrapper.eq(ZbfProject::getUserId, SecurityUtils.getAppUserId()); wrapper.like(StrUtil.isNotBlank(dto.getProjectName()), ZbfProject::getProjectName, dto.getProjectName()); + wrapper.orderByDesc(ZbfProject::getId); Page result = page(PageUtils.buildPage(), wrapper); return PageUtils.buildDataInfo(result); } diff --git a/ruoyi-system/src/main/resources/mapper/bgt/BgtProjectTaskProgressMapper.xml b/ruoyi-system/src/main/resources/mapper/bgt/BgtProjectTaskProgressMapper.xml index d8f95ff..faa4b60 100644 --- a/ruoyi-system/src/main/resources/mapper/bgt/BgtProjectTaskProgressMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/bgt/BgtProjectTaskProgressMapper.xml @@ -56,17 +56,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" diff --git a/ruoyi-system/src/main/resources/mapper/zbf/ZbfProjectSectionMapper.xml b/ruoyi-system/src/main/resources/mapper/zbf/ZbfProjectSectionMapper.xml index 7153127..9a7a1b5 100644 --- a/ruoyi-system/src/main/resources/mapper/zbf/ZbfProjectSectionMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/zbf/ZbfProjectSectionMapper.xml @@ -35,7 +35,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" SELECT zps.id,zps.project_id,zps.section_name FROM zbf_project_section zps left JOIN zbf_project zp ON zps.project_id = zp.id - WHERE zp.user_id = #{dto.zbfUserId} + WHERE zp.id = #{dto.projectId} and zp.user_id = #{dto.zbfUserId} diff --git a/ruoyi/uploadPath/appResource/html/upload.html b/ruoyi/uploadPath/appResource/html/upload.html index 2cb2eb6..543f33a 100644 --- a/ruoyi/uploadPath/appResource/html/upload.html +++ b/ruoyi/uploadPath/appResource/html/upload.html @@ -197,6 +197,23 @@ > 确认上传 +
+

操作流程:

+
    +
  1. 选择主题
  2. +
  3. 勾选需要上传文件的人员
  4. +
  5. 点击"下载模板"按钮
  6. +
  7. 解压下载的模板文件
  8. +
  9. 将图片或PDF文件放入相应人员的文件夹中
  10. +
  11. 压缩文件夹并上传
  12. +
+

注意事项:

+
    +
  • 请确保压缩文件为.zip格式
  • +
  • 压缩层级不要过深,建议直接压缩人员文件夹
  • +
  • 每个人员的文件请放入对应的文件夹中
  • +
+
@@ -260,8 +277,8 @@ mounted() { const protocol = window.location.protocol; const host = window.location.host; - this.baseUrl = `${protocol}//${host}`; // 动态获取基础 URL - // 获取 URL 中的 userId 参数 + // this.baseUrl = `${protocol}//${host}/lhyg`; // 动态获取基础 URL + // 获取 URL 中的 userId 参数 const urlParams = new URLSearchParams(window.location.search); this.userId = urlParams.get("userId"); console.log("userId", this.userId);