package com.product.file.service; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.IdUtil; import com.product.common.lang.StringUtils; import com.product.core.cache.util.RedisUtil; import com.product.core.config.Global; import com.product.core.entity.DataTableEntity; import com.product.core.entity.FieldSetEntity; import com.product.core.exception.BaseException; import com.product.core.service.support.AbstractBaseService; import com.product.core.spring.context.SpringMVCContextHolder; import com.product.file.config.CmnConst; import com.product.file.config.FileCode; import com.product.file.util.FileUtils; import com.product.module.sys.entity.SystemUser; import com.product.module.sys.service.idel.IOnlineDocumentEditingService; import com.product.util.BaseUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Service; import redis.clients.jedis.Jedis; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.stream.Collectors; /** * @Author cheng * @Date 2022/4/11 10:08 * @Desc 文档在线编辑业务类 */ @Service @Primary public class OnlineDocumentEditService extends AbstractBaseService implements IOnlineDocumentEditingService { /** * 文档编辑标识key */ private final String DOCUMENT_EDIT_KEY = "document-edit:"; @Autowired FileManagerService fileManagerService; @Autowired FileUtils fileUtils; /** * 文档套红 * * @param ff * @param file */ public void nestRedDocument(FieldSetEntity ff, File file) { try { String document_template = ff.getString("document_template"); String document_template_tail = ff.getString("document_template_tail"); String fileUuid = ff.getString("file_uuid"); FieldSetEntity fieldSetEntity = getBaseDao().getFieldSetEntity(CmnConst.PRODUCT_SYS_ATTACHMENTS, fileUuid, false); //获取文件类型 String fileName = fieldSetEntity.getString(CmnConst.FILE_NAME); //判断是否为doc文件 String fileType = fileName.substring(fileName.lastIndexOf(".") + 1); if ("doc".equals(fileType)) { //将doc文件转换为docx文件 File f = file; file = com.product.file.util.FileUtil.toDocx(file); if (f != null) { f.delete(); } } List fileList = new ArrayList<>(3); if (!StringUtils.isEmpty(document_template)) { File file1 = fileManagerService.getFile(document_template); //判断file1的文件类型 if (file1.getName().substring(file1.getName().lastIndexOf(".") + 1).equals("doc")) { //转换为docx File docx = com.product.file.util.FileUtil.toDocx(file1); FileUtil.del(file1); file1 = docx; } fileList.add(file1); fileList.add(file); } else { fileList.add(file); } if (!StringUtils.isEmpty(document_template_tail)) { fileList.add(fileManagerService.getFile(document_template_tail)); } File mergeFile = MergeDoc.mergeDoc(fileList, Global.getSystemConfig("upload.file.temp.path", "./attachment/temp") + File.separator + "nest_red_document_" + IdUtil.randomUUID() + "_" + ff.getString("file_uuid")); if (mergeFile != null && mergeFile.isFile()) { if ("doc".equals(fileType)) { //将docx文件转换为doc文件 File f = mergeFile; mergeFile = com.product.file.util.FileUtil.toDoc(f); if (f != null) { f.delete(); } } HashMap objectObjectHashMap = new HashMap<>(); objectObjectHashMap.put("mergeFile", mergeFile); uploadFile(ff.getString("file_uuid"), objectObjectHashMap); } else { throw new BaseException(FileCode.NEST_RED_DOCUMENT_FAIL); } fileList.forEach(FileUtil::del); } catch (BaseException e) { throw e; } catch (Exception e) { e.printStackTrace(); throw new BaseException(FileCode.NEST_RED_DOCUMENT_FAIL); } } /** * 在线预览或编辑获取文件 * * @param response * @param uuid * @throws IOException */ public void getFile(HttpServletResponse response, String uuid) throws IOException { String currentUserId = SpringMVCContextHolder.getCurrentUserId(); if (StringUtils.isEmpty(currentUserId)) { return; } String redisKey = this.DOCUMENT_EDIT_KEY + uuid + "-" + currentUserId; //在redis中获取缓存 根据缓存是否存在判断该文件是否标识为正在编辑的状态 Map map = (Map) RedisUtil.get(redisKey); //查询文件记录 FieldSetEntity fse = getBaseDao().getFieldSetEntity(CmnConst.PRODUCT_SYS_ATTACHMENTS, uuid, false); if (FieldSetEntity.isEmpty(fse)) { throw new BaseException(FileCode.GET_FILE_RECORD_FAIL); } if (map != null) { //正在编辑 准备输出已编辑过但未提交的文件 String dir = fse.getString(CmnConst.ATTACHMENT_URL); String fileName = fse.getString(CmnConst.ATTACHMENT_TITLE); //已编辑未提交的文件路径 try { File sourceFile = fileUtils.getFile(fse.getBoolean(CmnConst.UPLOAD_SIGN), dir, fileName + "_" + currentUserId + "_" + map.get("uniqueKey"), fse.getBoolean(CmnConst.ENCRPT_SIGN)); if (sourceFile != null && sourceFile.isFile()) { //输出 IoUtil.write(response.getOutputStream(), true, FileUtil.readBytes(sourceFile)); sourceFile.delete(); return; } } catch (BaseException e) { //捕获异常 } } IoUtil.write(response.getOutputStream(), true, fileManagerService.getFileContent(fse)); } /** * 清空正在编辑文档状态 根据用户 * * @param userId 用户id */ public void clearBeingEditDocumentStatus(String userId) { if (StringUtils.isEmpty(userId)) { return; } //正则匹配redis中的key Set keys = RedisUtil.keys(this.DOCUMENT_EDIT_KEY + "*-" + userId); if (keys != null && keys.size() > 0) { String[] strKeys = new String[keys.size()]; int i = 0; for (byte[] key : keys) { strKeys[i++] = new String(key); } //清空redis RedisUtil.del(strKeys); } } /** * 业务数据保存时底层调用此方法 * 调用此方法后将文件标记为已保存 清除正在编辑的状态标识 * * @param fileUuid 文件uuid * @param userId 操作人id * @return */ @Override public boolean saveDocNotice(String fileUuid, int userId) throws BaseException { if (StringUtils.isEmpty(fileUuid)) { return false; } String[] fileUuids = fileUuid.split(","); //查询文件记录 DataTableEntity dt = getBaseDao().listTable(CmnConst.PRODUCT_SYS_ATTACHMENTS, BaseUtil.buildQuestionMarkFilter("uuid", fileUuids, true)); if (!DataTableEntity.isEmpty(dt)) { // throw new BaseException(FileCode.GET_FILE_RECORD_FAIL); for (int i = 0; i < dt.getRows(); i++) { FieldSetEntity fse = dt.getFieldSetEntity(i); try { String redisKey = this.DOCUMENT_EDIT_KEY + fse.getUUID() + "-" + userId; if (!RedisUtil.exists(redisKey)) { continue; } Map map = (Map) RedisUtil.get(redisKey); if (map != null) { String dir = fse.getString(CmnConst.ATTACHMENT_URL); String fileName = fse.getString(CmnConst.ATTACHMENT_TITLE); if (!fileUtils.fileIsExist(fse.getBoolean(CmnConst.UPLOAD_SIGN), dir, fileName)) { continue; } //系统附件存放的根路径 File sourceFile = fileUtils.getFile(fse.getBoolean(CmnConst.UPLOAD_SIGN), dir, fileName + "_" + userId + "_" + map.get("uniqueKey"), fse.getBoolean(CmnConst.ENCRPT_SIGN)); if (sourceFile != null && sourceFile.isFile()) { File targetFile = fileUtils.getFile(fse.getBoolean(CmnConst.UPLOAD_SIGN), dir, fileName, false); if (targetFile != null && targetFile.isFile()) { //备份源文件 fileUtils.uploadOtherFile(fse.getBoolean(CmnConst.ENCRPT_SIGN), dir, targetFile, fileName + "_back_" + userId + "_" + map.get("uniqueKey")); targetFile.delete(); } //将修改后的文件覆盖到源文件 fileUtils.replaceFile(dir, fileName, fse.getString(CmnConst.FILE_NAME), sourceFile, fse.getBoolean(CmnConst.ENCRPT_SIGN), fse.getBoolean(CmnConst.UPLOAD_SIGN), fse.getBoolean(CmnConst.VIEW_ONLINE_SIGN)); if (fse.getBoolean(CmnConst.ENCRPT_SIGN)) { fileUtils.deleteFilesServerOnFile(dir, fileName + "_" + userId + "_" + map.get("uniqueKey")); } sourceFile.delete(); } } RedisUtil.del(redisKey); } catch (Exception e) { e.printStackTrace(); SpringMVCContextHolder.getSystemLogger().error(e); // return false; } } } return true; } /** * weboffice 保存文件(手动、自动) * * @param uuid 文件的uuid * @param fileMap 新提交的文件 * @throws BaseException */ public void uploadFile(String uuid, Map fileMap) throws BaseException { //查询文件记录 FieldSetEntity fse = getBaseDao().getFieldSetEntity(CmnConst.PRODUCT_SYS_ATTACHMENTS, uuid, false); if (FieldSetEntity.isEmpty(fse)) { throw new BaseException(FileCode.GET_FILE_RECORD_FAIL); } String userId = SpringMVCContextHolder.getCurrentUserId(); if (StringUtils.isEmpty(userId)) { return; } //获取源文件路径 String dir = fse.getString(CmnConst.ATTACHMENT_URL); String fileName = fse.getString(CmnConst.ATTACHMENT_TITLE); if (!fileUtils.fileIsExist(fse.getBoolean(CmnConst.UPLOAD_SIGN), dir, fileName)) { throw new BaseException(FileCode.INVALID_FILE_PATH); } String redisKey = this.DOCUMENT_EDIT_KEY + uuid + "-" + userId; Map map = (Map) RedisUtil.get(redisKey); //系统附件存放的根路径 File file = fileMap.values().toArray(new File[]{})[0]; if (fse.getBoolean(CmnConst.ENCRPT_SIGN)) { String filePath = Global.getSystemConfig("temp.dir", "") + "/onlineEdit_" + redisKey; //加密文件 com.product.file.util.FileUtil.copyFile(file, filePath, 1); file.delete(); file = new File(filePath); } FileUtils.uploadOtherFile(fse.getBoolean(CmnConst.ENCRPT_SIGN), dir, file, fileName + "_" + userId + "_" + map.get("uniqueKey")); file.delete(); } public void deleteFile(String dir, String fileName) { SystemUser currentUser = SpringMVCContextHolder.getCurrentUser(); if (currentUser != null && !StringUtils.isEmpty(currentUser.getToken_info())) { // TokenValidateInterceptor } } /** * 获取过期时间 * * @return 过期时间 单位 秒 */ private int getExpirationTime() { int refreshTokenExpiration = Global.getPropertyToInteger("refresh.token.expiration", "8") * 60 * 60; int tokenExpiration = Global.getPropertyToInteger("token.expiration", (8 * 60) + "") * 60; return refreshTokenExpiration > tokenExpiration ? refreshTokenExpiration : tokenExpiration; } /** * 标识文档正在编辑 * * @param fileUuid * @return */ public void signDocumentEdit(String fileUuid) throws BaseException { String userId = SpringMVCContextHolder.getCurrentUserId(); if (StringUtils.isEmpty(userId)) { throw new BaseException(FileCode.GET_FILE_RECORD_FAIL); } String key = this.DOCUMENT_EDIT_KEY + fileUuid + "-" + userId; //模糊匹配key Set keyBytes = RedisUtil.keys((this.DOCUMENT_EDIT_KEY + fileUuid + "-*").getBytes(StandardCharsets.UTF_8)); if (!CollectionUtil.isEmpty(keyBytes)) { Set keys = keyBytes.stream().map(item -> new String(item, StandardCharsets.UTF_8)).collect(Collectors.toSet()); boolean remove = keys.remove(this.DOCUMENT_EDIT_KEY + fileUuid + "-" + userId); if (keys != null && keys.size() > 0) { //正在被编辑 String existsKey = keys.toArray(new String[]{})[0]; //这里会抛出正在编辑的异常 throwBeingEditDocument(existsKey.substring(existsKey.lastIndexOf("-") + 1)); } else if (remove) { RedisUtil.setOutTime(key, getExpirationTime()); return; } } Map params = new HashMap<>(); params.put("userId", userId); params.put("fileUuid", fileUuid); params.put("uniqueKey", IdUtil.randomUUID()); RedisUtil.set(key, params); RedisUtil.setOutTime(key, getExpirationTime()); } /** * 正在编辑文档错误抛出 * * @param userId * @throws BaseException */ private void throwBeingEditDocument(Object userId) throws BaseException { FieldSetEntity userInfo = BaseUtil.getSingleInfoByCache("用户缓存", new String[]{String.valueOf(userId)}); String user_name; if (userInfo != null && !StringUtils.isEmpty(userInfo.getString("user_name"))) { user_name = userInfo.getString("user_name"); } else { user_name = "未知用户"; } throw new BaseException(FileCode.DOCUMENT_BEING_EDITED.getValue(), FileCode.DOCUMENT_BEING_EDITED.getText().replace("{{userName}}", user_name)); } }