许鹏程
2024-09-26 4e2dbadb64676b7913ad8bd6f1394b9983e4365c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
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<File> 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<String, File> 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<String, Object> map = (Map<String, Object>) 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<byte[]> 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(keys.toArray(new String[keys.size()]));
        }
    }
 
    /**
     * 业务数据保存时底层调用此方法
     * 调用此方法后将文件标记为已保存 清除正在编辑的状态标识
     *
     * @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<String, Object> map = (Map<String, Object>) 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<String, File> 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<String, Object> map = (Map<String, Object>) 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<byte[]> keyBytes = RedisUtil.keys((this.DOCUMENT_EDIT_KEY + fileUuid + "-*").getBytes(StandardCharsets.UTF_8));
        if (!CollectionUtil.isEmpty(keyBytes)) {
            Set<String> 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<String, Object> 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));
    }
 
}