This commit is contained in:
zt
2025-04-29 17:45:42 +08:00
parent 2c15d26bcc
commit c982cbaa75
32 changed files with 936 additions and 370 deletions

View File

@ -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<BgtMessageCountVO> count() {
@ -74,10 +78,14 @@ public class AppBgtMessageController extends BaseController {
@ApiOperation("已读")
@PutMapping("/read/{id}")
public AjaxResult<Boolean> 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("已操作")

View File

@ -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); // 异常时清理无效连接
// }
// }
// }
}

View File

@ -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<String, String> leafFileMap = (Map<String, String>) paths[0]; // 改为Map
List<String> targetDirPaths = (List<String>) 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<Void> 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<String, String> leafFileMap = new HashMap<>(); // key: 倒数第二层/倒数第一层/文件名value: 完整相对路径
List<String> targetDirPaths = new ArrayList<>();
collectLeafPathsRecursive(extractDir, extractDir, leafFileMap, targetDirPaths);
return new Object[]{leafFileMap, targetDirPaths};
}
List<AnnexRecord> 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<String, String> leafFileMap, List<String> 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<String> 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<String> targetDirPaths, Long recruitId) {
List<Long> insuranceUserIds = new ArrayList<>(); // 存储需要删除保险附件的用户ID
List<Long> contractUserIds = new ArrayList<>(); // 存储需要删除合同附件的用户ID
List<Long> recruitApplyIds = new ArrayList<>(); // 存储关联的招工申请ID
// 保险 2
List<Long> insurance = new ArrayList<>();
// 劳务合同 1
List<Long> contract = new ArrayList<>();
// 招工申请Id
List<Long> 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<String, String> 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<Annex> annexList = new ArrayList<>();
if (firstLevelFiles != null) {
for (File firstLevelFile : firstLevelFiles) {
if (firstLevelFile.isDirectory()) {
for (Map.Entry<String, String> 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<Annex> 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<Void> 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<AnnexRecord> 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) {

View File

@ -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<FbsMessageCountVO> count() {
@ -54,10 +56,14 @@ public class AppFbsMessageController extends BaseController {
@ApiOperation("分包商消息已读")
@PutMapping("/read/{id}")
public AjaxResult<Boolean> 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<FbsMessage> detail(@PathVariable(value = "id") Long id) {
return AjaxResult.success(iFbsMessageService.getById(id));
}
}

View File

@ -148,6 +148,15 @@
return AjaxResult.success(iWgzUserService.userPersonalBasicInformation(req));
}
/**
* 【我的】总收入
*/
@GetMapping("/grossIncome")
public AjaxResult<BigDecimal> grossIncome() {
return AjaxResult.success(iWgzPayCalculationService.grossIncome());
}
/**
* 【我的】【实名认证】实名认证
*/

View File

@ -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<Boolean> 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("总包方消息已操作")

View File

@ -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

View File

@ -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<BgtMessageMapper, BgtMessage> 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<BgtMessageMapper, Bgt
@Override
@Transactional
public Boolean sendAMessage(BgtMessage bo) {
return save(bo);
boolean save = save(bo);
MessageUtil.saveMessagePush(bo.getRecipientType(), bo.getRecipientId(), bo.getMessageLargeType(), bo.getIsOperation());
return save;
}
@Override
public BgtMessageCountVO countUnread() {
// Object cacheObject = redisCache.getCacheObject(REIDS_KEY + SecurityUtils.getAppUserId());
List<BgtMessage> bgtMessages = baseMapper.selectList(Wrappers.<BgtMessage>lambdaQuery()
.eq(BgtMessage::getRecipientId, SecurityUtils.getAppUserId())
.eq(BgtMessage::getReadStatus, "0"));
@ -189,6 +187,7 @@ public class BgtMessageServiceImpl extends ServicePlusImpl<BgtMessageMapper, Bgt
.eq(BgtMessage::getRecipientId, SecurityUtils.getAppUserId())
.eq(BgtMessage::getIsOperation, OPERATION_NEED));
bgtMessageCountVO.setHandleMessageCount(handle);
redisCache.setCacheObject(REDIS_KEY+SecurityUtils.getAppUserId(), bgtMessageCountVO);
return bgtMessageCountVO;
}
@ -283,6 +282,7 @@ public class BgtMessageServiceImpl extends ServicePlusImpl<BgtMessageMapper, Bgt
wrapper.eq(BgtMessage::getTableName, tableName);
wrapper.set(BgtMessage::getIsOperation, OPERATION_ALREADY);
update(wrapper);
MessageUtil.operationMessagePush(recipientType, recipientId, 1);
}

View File

@ -611,7 +611,7 @@ public class BgtProjectRecruitApplyServiceImpl extends ServicePlusImpl<BgtProjec
.setRecipientId(vo.getUserId())
.setHeadline(map.get(HEADLINE))
.setSubheading(map.get(SUBHEADING))
.setTableId(vo.getRecruitId())
.setTableId(vo.getId())
.setTableName(SqlHelper.table(BgtProjectRecruitApply.class).getTableName())
.setMessageLargeType(LARGE_OTHER)
.setMessageSmallType(SMALL_EXIT);
@ -741,7 +741,7 @@ public class BgtProjectRecruitApplyServiceImpl extends ServicePlusImpl<BgtProjec
throw new RuntimeException("修改招工申请信息失败!");
}
//3-2、修改消息
boolean b = iWgzMessageService.updateById(new WgzMessage().setId(req.getMessageId()).setIsOperation("2"));
boolean b = iWgzMessageService.updateOperationStatus(req.getMessageId());
if (!b) {
throw new RuntimeException("修改消息失败!");
}

View File

@ -9,17 +9,19 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.ruoyi.bgt.bo.BgtProjectTaskProgressQueryBo;
import com.ruoyi.bgt.domain.BgtMessage;
import com.ruoyi.bgt.domain.BgtProjectRecruit;
import com.ruoyi.bgt.domain.BgtProjectTaskProgress;
import com.ruoyi.bgt.domain.dto.BgtProjectTaskProgressQueryDTO;
import com.ruoyi.bgt.domain.vo.BgtProjectTaskProgressDetailVO;
import com.ruoyi.bgt.domain.vo.BgtProjectTaskProgressVO;
import com.ruoyi.bgt.mapper.BgtProjectTaskProgressMapper;
import com.ruoyi.bgt.service.IBgtMessageService;
import com.ruoyi.bgt.service.IBgtProjectRecruitApplyService;
import com.ruoyi.bgt.service.IBgtProjectRecruitService;
import com.ruoyi.bgt.service.IBgtProjectTaskProgressService;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.AuditStatus;
import com.ruoyi.common.enums.RecruitStatus;
import com.ruoyi.common.exception.BaseException;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.SecurityUtils;
@ -55,7 +57,7 @@ public class BgtProjectTaskProgressServiceImpl extends ServicePlusImpl<BgtProjec
@Autowired
private IBgtMessageService bgtMessageService;
@Autowired
private IBgtProjectRecruitApplyService recruitApplyService;
private IBgtProjectRecruitService recruitService;
@Override
public BgtProjectTaskProgress queryById(Long id){
@ -159,6 +161,16 @@ public class BgtProjectTaskProgressServiceImpl extends ServicePlusImpl<BgtProjec
if(lastProgress>=entity.getProgress()){
throw new BaseException("当前进度不能小于等于上一个进度");
}
if(entity.getProgress()==100){
List<BgtProjectRecruit> recruitList = recruitService.list(Wrappers.<BgtProjectRecruit>lambdaQuery()
.eq(BgtProjectRecruit::getTaskId, entity.getTaskId())
.ne(BgtProjectRecruit::getStatus, RecruitStatus.OVERDUE.getCode())
);
if(CollectionUtil.isNotEmpty(recruitList)){
throw new BaseException("招工结束前不能完成任务");
}
}
}

View File

@ -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"; //大类型-结算

View File

@ -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"; //大类型-结算

View File

@ -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;

View File

@ -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"; //大类型-其它

View File

@ -64,4 +64,6 @@ public class CompanyAuthenticateDTO {
@ApiModelProperty("资质与荣誉")
private String url;
@ApiModelProperty("备注")
private String remark;
}

View File

@ -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<String, Integer> 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<String, Integer> 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<String, Integer> 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);
}
}
}

View File

@ -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<String, SseEmitter> 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); // 异常时清理无效连接
}
}
}
}

View File

@ -46,4 +46,6 @@ public class FbsProjectDetailVO {
@ApiModelProperty("标段列表")
private List<FbsProjectSectionListVO> sectionList;
@ApiModelProperty("备注")
private String remark;
}

View File

@ -22,6 +22,11 @@ public class FbsProjectSectionListVO {
@ApiModelProperty("项目地址")
private String projectAddress;
@ApiModelProperty("标段描述")
private String sectionDescribe;
@ApiModelProperty("分包列表")
private List<FbsProjectSubcontractingListVO> subList;

View File

@ -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<FbsMessageMapper, FbsMessage> implements IFbsMessageService {
@Autowired
private RedisCache redisCache;
@Override
public FbsMessage queryById(Long id){
return getById(id);
@ -122,6 +129,8 @@ public class FbsMessageServiceImpl extends ServicePlusImpl<FbsMessageMapper, Fbs
.eq(FbsMessage::getRecipientId, SecurityUtils.getAppUserId())
.eq(FbsMessage::getIsOperation, OPERATION_NEED));
fbsMessageCountVO.setHandleMessageCount(handle);
redisCache.setCacheObject(FbsMessageConstant.REDIS_KEY+SecurityUtils.getAppUserId(), fbsMessageCountVO);
return fbsMessageCountVO;
}
@ -208,7 +217,9 @@ public class FbsMessageServiceImpl extends ServicePlusImpl<FbsMessageMapper, Fbs
@Override
public Boolean sendAMessage(FbsMessage bo) {
return save(bo);
boolean save = save(bo);
MessageUtil.saveMessagePush(bo.getRecipientType(), bo.getRecipientId(), bo.getMessageLargeType(), bo.getIsOperation());
return save;
}
@Override
@ -222,6 +233,8 @@ public class FbsMessageServiceImpl extends ServicePlusImpl<FbsMessageMapper, Fbs
wrapper.eq(FbsMessage::getTableName, tableName);
wrapper.set(FbsMessage::getIsOperation, OPERATION_ALREADY);
update(wrapper);
MessageUtil.operationMessagePush(recipientType, recipientId, 1);
}
@Override
@ -233,5 +246,6 @@ public class FbsMessageServiceImpl extends ServicePlusImpl<FbsMessageMapper, Fbs
wrapper.eq(FbsMessage::getTableName, tableName);
wrapper.set(FbsMessage::getIsOperation, OPERATION_ALREADY);
update(wrapper);
MessageUtil.operationMessagePush(recipientType, recipientId, tableIds.size());
}
}

View File

@ -29,6 +29,7 @@ import com.ruoyi.common.enums.RecruitApplyStatus;
import com.ruoyi.common.enums.TaskApplyStatus;
import com.ruoyi.common.exception.BaseException;
import com.ruoyi.common.util.DataUtil;
import com.ruoyi.common.util.ValidUtil;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.SecurityUtils;
@ -168,6 +169,9 @@ public class FbsProjectTaskServiceImpl extends ServicePlusImpl<FbsProjectTaskMap
if( fbsUser.getCompanyId()==null){
throw new BaseException("尚未企业认证");
}
if (!ValidUtil.isValidChineseMobile(entity.getTaskContactPhone())) {
throw new BaseException("手机号格式不正确");
}
}
@Override

View File

@ -1,15 +1,14 @@
package com.ruoyi.wgz.service;
import com.ruoyi.wgz.bo.req.WgzAppConfirmRegistrationReq;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.wgz.bo.WgzMessageQueryBo;
import com.ruoyi.wgz.bo.req.WgzAppGetMessageListReq;
import com.ruoyi.wgz.bo.req.WgzAppReadUnreadReq;
import com.ruoyi.wgz.bo.res.WgzAppGetMessageListRes;
import com.ruoyi.wgz.bo.res.WgzAppMessageTypeStatisticsRes;
import com.ruoyi.wgz.bo.res.WgzAppRegistrationInformationRes;
import com.ruoyi.wgz.domain.WgzMessage;
import com.ruoyi.wgz.bo.WgzMessageQueryBo;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import org.springframework.validation.annotation.Validated;
import java.time.LocalDate;
@ -98,4 +97,9 @@ public interface IWgzMessageService extends IServicePlus<WgzMessage> {
* 修改已读未读状态
*/
Boolean userReadUnread(@Validated WgzAppReadUnreadReq req);
/**
* 修改操作状态
*/
Boolean updateOperationStatus(Long id);
}

View File

@ -153,4 +153,9 @@ public interface IWgzPayCalculationService extends IServicePlus<WgzPayCalculatio
* 获取任务下已审核通过的数据
*/
List<WgzPayCalculation> getPassListByTaskIds(List<Long> taskIds);
/**
* 总收入
*/
BigDecimal grossIncome();
}

View File

@ -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<WgzMessageMapper, Wgz
@Autowired
private IBgtProjectRecruitService iBgtProjectRecruitService;
@Autowired
private RedisCache redisCache;
@Override
public WgzMessage queryById(Long id){
@ -143,7 +148,9 @@ public class WgzMessageServiceImpl extends ServicePlusImpl<WgzMessageMapper, Wgz
@Override
public Boolean sendAMessage(WgzMessage bo) {
return save(bo);
boolean save = save(bo);
MessageUtil.saveMessagePush(bo.getRecipientType(), bo.getRecipientId(), bo.getMessageLargeType(), bo.getIsOperation());
return save;
}
@Override
@ -192,7 +199,9 @@ public class WgzMessageServiceImpl extends ServicePlusImpl<WgzMessageMapper, Wgz
);
mp.put("3",daiBanCount);
}
return res.setMp(mp);
res.setMp(mp);
redisCache.setCacheObject(WgzAndBgtMessageConstant.REDIS_KEY+SecurityUtils.getAppUserId(), res);
return res;
}
@Override
@ -256,10 +265,25 @@ public class WgzMessageServiceImpl extends ServicePlusImpl<WgzMessageMapper, Wgz
@Override
public Boolean userReadUnread(WgzAppReadUnreadReq req) {
WgzMessage wgzMessage = new WgzMessage().setId(req.getMessageId()).setReadStatus("1");
return baseMapper.updateById(wgzMessage) > 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;
}
/**
* 判断招工是否已招满或已过期

View File

@ -701,4 +701,19 @@ public class WgzPayCalculationServiceImpl extends ServicePlusImpl<WgzPayCalculat
.eq(WgzPayCalculation::getAuditorType, AuditStatus.PASS.getCode());
return list(wrapper);
}
@Override
public BigDecimal grossIncome() {
LambdaQueryWrapper<WgzPayCalculation> wrapper = new LambdaQueryWrapper<>();
wrapper.in(WgzPayCalculation::getUserId, SecurityUtils.getAppUserId())
.eq(WgzPayCalculation::getAuditorType, AuditStatus.PASS.getCode());
List<WgzPayCalculation> list = list(wrapper);
BigDecimal total = BigDecimal.ZERO;
for (WgzPayCalculation wgzPayCalculation : list) {
//金额*天数=实际工资
total = total.add(wgzPayCalculation.getRecruitAmount().multiply(new BigDecimal(wgzPayCalculation.getNum())));
}
return total;
}
}

View File

@ -16,4 +16,7 @@ public class ZbfSubSwitchListDTO {
@ApiModelProperty("总包方用户")
private Long zbfUserId;
@ApiModelProperty("项目ID")
private Long projectId;
}

View File

@ -19,6 +19,9 @@ public class ZbfProjectSectionListVO {
@ApiModelProperty("标段名称")
private String sectionName;
@ApiModelProperty("标段描述")
private String sectionDescribe;
@ApiModelProperty("项目地址")
private String projectAddress;

View File

@ -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<ZbfMessageMapper, ZbfMessage> implements IZbfMessageService {
@Autowired
private RedisCache redisCache;
@Override
public ZbfMessage queryById(Long id){
return getById(id);
@ -107,7 +113,9 @@ public class ZbfMessageServiceImpl extends ServicePlusImpl<ZbfMessageMapper, Zbf
@Override
public Boolean sendAMessage(ZbfMessage bo) {
return save(bo);
boolean save = save(bo);
MessageUtil.saveMessagePush(bo.getRecipientType(), bo.getRecipientId(), bo.getMessageLargeType(), bo.getIsOperation());
return save;
}
@Override
@ -121,6 +129,7 @@ public class ZbfMessageServiceImpl extends ServicePlusImpl<ZbfMessageMapper, Zbf
wrapper.eq(ZbfMessage::getTableName, tableName);
wrapper.set(ZbfMessage::getIsOperation, OPERATION_ALREADY);
update(wrapper);
MessageUtil.operationMessagePush(recipientType, recipientId, 1);
}
@Override
@ -132,6 +141,7 @@ public class ZbfMessageServiceImpl extends ServicePlusImpl<ZbfMessageMapper, Zbf
wrapper.eq(ZbfMessage::getTableName, tableName);
wrapper.set(ZbfMessage::getIsOperation, OPERATION_ALREADY);
update(wrapper);
MessageUtil.operationMessagePush(recipientType, recipientId, tableIds.size());
}
@Override
@ -150,6 +160,7 @@ public class ZbfMessageServiceImpl extends ServicePlusImpl<ZbfMessageMapper, Zbf
.eq(ZbfMessage::getRecipientId, SecurityUtils.getAppUserId())
.eq(ZbfMessage::getIsOperation, OPERATION_NEED));
zbfMessageCountVO.setHandleMessageCount(handle);
redisCache.setCacheObject(ZbfMessageConstant.REDIS_KEY+SecurityUtils.getAppUserId(), zbfMessageCountVO);
return zbfMessageCountVO;
}

View File

@ -204,9 +204,6 @@ public class ZbfProjectServiceImpl extends ServicePlusImpl<ZbfProjectMapper, Zbf
}
subListVO.add(fbsProjectSubcontractingListVO);
}
if (CollectionUtil.isEmpty(subListVO)) {
continue;
}
FbsProjectSectionListVO fbsProjectSectionListVO = new FbsProjectSectionListVO();
BeanUtil.copyProperties(zbfProjectSection, fbsProjectSectionListVO);
fbsProjectSectionListVO.setSubList(subListVO);
@ -363,9 +360,6 @@ public class ZbfProjectServiceImpl extends ServicePlusImpl<ZbfProjectMapper, Zbf
}
subListVO.add(fbsProjectSubcontractingListVO);
}
if (CollectionUtil.isEmpty(subListVO)) {
continue;
}
FbsProjectSectionListVO fbsProjectSectionListVO = new FbsProjectSectionListVO();
BeanUtil.copyProperties(zbfProjectSection, fbsProjectSectionListVO);
fbsProjectSectionListVO.setSubList(subListVO);
@ -543,15 +537,25 @@ public class ZbfProjectServiceImpl extends ServicePlusImpl<ZbfProjectMapper, Zbf
public TableDataInfo<FbsProgressListVO> fbsProgressList(FbsProgressListDTO dto) {
Page<FbsProgressListVO> fbsProgressListVOPage = new Page<>();
//查询所有创建的任务
List<FbsProjectTask> taskList = fbsProjectTaskService.list(Wrappers.<FbsProjectTask>lambdaQuery()
LambdaQueryWrapper<FbsProjectTask> wrapper = Wrappers.<FbsProjectTask>lambdaQuery()
.eq(FbsProjectTask::getCreateId, SecurityUtils.getAppUserId())
.eq(FbsProjectTask::getProjectId, dto.getProjectId())
);
List<Long> taskIds = taskList.stream().map(FbsProjectTask::getId).collect(Collectors.toList());
.orderByDesc(FbsProjectTask::getId);
Page<FbsProjectTask> fbsProjectTaskPage = fbsProjectTaskService.getBaseMapper().selectPage(PageUtils.buildPage(), wrapper);
List<Long> taskIds = fbsProjectTaskPage.getRecords().stream().map(FbsProjectTask::getId).collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(taskIds)) {
TableDataInfo<BgtProjectTaskProgress> progressByTaskIds = bgtProjectTaskProgressService.getProgressByTaskIds(dto.getPageSize(), dto.getPageNum(), taskIds);
fbsProgressListVOPage.setTotal(progressByTaskIds.getTotal());
fbsProgressListVOPage.setRecords(BeanUtil.copyToList(progressByTaskIds.getRows(), FbsProgressListVO.class));
List<BgtProjectTaskProgress> progressByTaskIds = bgtProjectTaskProgressService.getProgressByTaskIds(taskIds);
Map<Long, Integer> map = progressByTaskIds.stream().collect(Collectors.toMap(BgtProjectTaskProgress::getTaskId, BgtProjectTaskProgress::getProgress));
ArrayList<FbsProgressListVO> 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<ZbfProjectMapper, Zbf
//已支付金额
vo.setPayAmount(BigDecimal.ZERO);
if (CollectionUtil.isNotEmpty(subIds)) {
List<BgtWageApplication> payList = bgtWageApplicationService.getPassListByTaskIds(subIds);
if (CollectionUtil.isNotEmpty(taskList)) {
List<Long> taskIds = taskList.stream().map(FbsProjectTask::getId).collect(Collectors.toList());
List<BgtWageApplication> 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<ZbfProjectMapper, Zbf
.eq(FbsProjectTask::getProjectId, id)
.isNotNull(FbsProjectTask::getUserId)
);
zbfPersonCountVO.setBgtCount(list.size());
Set<Long> collect = list.stream().map(FbsProjectTask::getUserId).collect(Collectors.toSet());
zbfPersonCountVO.setBgtCount(collect.size());
List<Long> 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<ZbfProjectMapper, Zbf
public TableDataInfo<ZbfProgressListVO> zbfProgressList(ZbfProgressListDTO dto) {
Page<ZbfProgressListVO> voPage = new Page<>();
//查询所有创建的任务
List<FbsProjectTask> taskList = fbsProjectTaskService.list(Wrappers.<FbsProjectTask>lambdaQuery()
LambdaQueryWrapper<FbsProjectTask> wrapper = Wrappers.<FbsProjectTask>lambdaQuery()
.eq(FbsProjectTask::getProjectId, dto.getProjectId())
);
List<Long> taskIds = taskList.stream().map(FbsProjectTask::getId).collect(Collectors.toList());
.orderByDesc(FbsProjectTask::getId);
Page<FbsProjectTask> fbsProjectTaskPage = fbsProjectTaskService.getBaseMapper().selectPage(PageUtils.buildPage(), wrapper);
List<Long> taskIds = fbsProjectTaskPage.getRecords().stream().map(FbsProjectTask::getId).collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(taskIds)) {
TableDataInfo<BgtProjectTaskProgress> progressByTaskIds = bgtProjectTaskProgressService.getProgressByTaskIds(dto.getPageSize(), dto.getPageNum(), taskIds);
voPage.setTotal(progressByTaskIds.getTotal());
voPage.setRecords(BeanUtil.copyToList(progressByTaskIds.getRows(), ZbfProgressListVO.class));
List<BgtProjectTaskProgress> progressByTaskIds = bgtProjectTaskProgressService.getProgressByTaskIds(taskIds);
Map<Long, Integer> map = progressByTaskIds.stream().collect(Collectors.toMap(BgtProjectTaskProgress::getTaskId, BgtProjectTaskProgress::getProgress));
ArrayList<ZbfProgressListVO> 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<ZbfProjectMapper, Zbf
LambdaQueryWrapper<ZbfProject> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ZbfProject::getUserId, SecurityUtils.getAppUserId());
wrapper.like(StrUtil.isNotBlank(dto.getProjectName()), ZbfProject::getProjectName, dto.getProjectName());
wrapper.orderByDesc(ZbfProject::getId);
Page<ZbfProject> result = page(PageUtils.buildPage(), wrapper);
return PageUtils.buildDataInfo(result);
}

View File

@ -56,17 +56,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="getProgressByTaskIds" resultType="com.ruoyi.bgt.domain.BgtProjectTaskProgress">
SELECT t.*
FROM bgt_project_task_progress t
JOIN (
SELECT task_id, MAX(progress) AS maxProgress
SELECT *
FROM (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY task_id ORDER BY progress DESC) as rn
FROM bgt_project_task_progress
WHERE audit_status = '2' and task_id IN
WHERE audit_status = '2'
AND task_id IN
<foreach item="taskId" collection="taskIds" open="(" separator="," close=")">
#{taskId}
</foreach>
GROUP BY task_id
) sub ON t.task_id = sub.task_id AND t.progress = sub.maxProgress
) AS sub
WHERE sub.rn = 1 order by create_time desc
</select>
</mapper>

View File

@ -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}
</select>
</mapper>

View File

@ -197,6 +197,23 @@
>
确认上传
</el-button>
<div style="background-color: #f5f7fa; border-radius: 4px">
<h4 style="margin: 0 0 10px 0; color: #409eff">操作流程:</h4>
<ol style="margin: 0; padding-left: 20px">
<li>选择主题</li>
<li>勾选需要上传文件的人员</li>
<li>点击"下载模板"按钮</li>
<li>解压下载的模板文件</li>
<li>将图片或PDF文件放入相应人员的文件夹中</li>
<li>压缩文件夹并上传</li>
</ol>
<h4 style="margin: 15px 0 10px 0; color: #f56c6c">注意事项:</h4>
<ul style="margin: 0; padding-left: 20px">
<li>请确保压缩文件为.zip格式</li>
<li>压缩层级不要过深,建议直接压缩人员文件夹</li>
<li>每个人员的文件请放入对应的文件夹中</li>
</ul>
</div>
</div>
</div>
<!-- 查看详情弹窗 -->
@ -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);