吉安感知网项目-后端
linwei
2026-07-08 79c2d5fa54ca680f5fc439b3607d7f0482a2ff32
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
package org.sxkj.resource.endpoint;
 
import com.alibaba.fastjson.JSON;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springblade.core.oss.model.BladeFile;
import org.springblade.core.tenant.annotation.NonDS;
import org.springblade.core.tool.utils.FileUtil;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.sxkj.resource.builder.OssBuilder;
import org.sxkj.resource.dto.OfficeCallbackDTO;
import org.sxkj.resource.entity.Attach;
import org.sxkj.resource.service.IAttachService;
 
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
 
/**
 * ONLYOFFICE 回调端点
 *
 * @author lw
 * @since 2026-07-08
 */
@Slf4j
@NonDS
@RestController
@AllArgsConstructor
@RequestMapping("/office")
@Api(value = "ONLYOFFICE回调端点", tags = "ONLYOFFICE回调接口")
public class OfficeCallbackEndpoint {
 
    /**
     * 对象存储构建类
     */
    private final OssBuilder ossBuilder;
 
    /**
     * 附件表服务
     */
    private final IAttachService attachService;
 
    /**
     * ONLYOFFICE 保存回调接口
     * 对应前端 config.editorConfig.callbackUrl = "http://localhost:8080/office/callback"
     *
     * @param callbackDTO 回调数据对象
     * @return 回调响应结果
     */
    @SneakyThrows
    @PostMapping("/callback")
    @ApiOperation(value = "ONLYOFFICE保存回调", notes = "ONLYOFFICE文档编辑保存回调接口")
    public Map<String, Object> officeCallback(@RequestBody OfficeCallbackDTO callbackDTO) {
        log.info("ONLYOFFICE回调开始,回调数据: {}", JSON.toJSONString(callbackDTO));
        Map<String, Object> result = new HashMap<>(4);
 
        // 步骤1:获取回调状态和文件信息
        String status = callbackDTO.getStatus();
        String fileKey = callbackDTO.getKey();
        String fileDownloadUrl = callbackDTO.getUrl();
 
        try {
            // 步骤2:判断是否是保存操作(1=手动保存,2=强制保存)
            if ("1".equals(status) || "2".equals(status)) {
                // 步骤3:下载ONLYOFFICE返回的新文件
                byte[] fileContent = downloadFile(fileDownloadUrl);
 
                // 步骤4:获取文件名(可根据业务需求从数据库获取原始文件名)
                String fileName = generateFileName(fileKey);
 
                // 步骤5:上传文件到OSS
                BladeFile bladeFile = uploadToOss(fileName, fileContent);
                log.info("ONLYOFFICE文件下载成功,文件名: {}, 文件大小: {}", fileName, fileContent.length);
                // 步骤6:保存附件信息到数据库
                Long attachId = saveAttachInfo(fileName, fileContent.length, bladeFile);
 
                // 步骤7:记录操作日志(可根据业务需求扩展)
                log.info("ONLYOFFICE文件保存成功,文件key: {}, 文件名: {}, 附件ID: {}, 下载URL: {}",
                    fileKey, fileName, attachId, bladeFile.getLink());
 
                // 步骤8:业务扩展(可根据需求添加)
                // - 更新数据库文件地址、版本号、更新时间
                // - 记录操作日志、操作用户
                // - 发送通知等
            }
 
            // 步骤9:成功必须返回 error:0
            result.put("error", 0);
        } catch (Exception e) {
            // 步骤10:异常处理,非0代表失败,编辑器会提示保存失败
            log.error("ONLYOFFICE文件保存异常,文件key: {}, 异常信息: {}", fileKey, e.getMessage(), e);
            result.put("error", 1);
            result.put("message", "文件保存异常:" + e.getMessage());
        }
 
        return result;
    }
 
    /**
     * 从ONLYOFFICE服务器下载文件
     *
     * @param downloadUrl 文件下载URL
     * @return 文件内容字节数组
     * @throws Exception 下载异常
     */
    private byte[] downloadFile(String downloadUrl) throws Exception {
        // 步骤1:创建HTTP客户端
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            // 步骤2:构建GET请求
            HttpGet httpGet = new HttpGet(downloadUrl);
 
            // 步骤3:执行请求并获取响应
            try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
                // 步骤4:获取响应内容流并读取到字节数组
                InputStream inputStream = response.getEntity().getContent();
                return inputStream.readAllBytes();
            }
        }
    }
 
    /**
     * 上传文件到OSS
     *
     * @param fileName    文件名
     * @param fileContent 文件内容
     * @return BladeFile OSS文件信息
     * @throws Exception 上传异常
     */
    private BladeFile uploadToOss(String fileName, byte[] fileContent) throws Exception {
        // 步骤1:创建输入流
        InputStream inputStream = new java.io.ByteArrayInputStream(fileContent);
 
        // 步骤2:使用ossBuilder上传文件到指定存储桶(estack)
        return ossBuilder.template("estack").putFile(fileName, inputStream);
    }
 
    /**
     * 保存附件信息到数据库
     *
     * @param fileName   文件名
     * @param fileSize   文件大小
     * @param bladeFile  OSS文件信息
     * @return 附件ID
     */
    private Long saveAttachInfo(String fileName, int fileSize, BladeFile bladeFile) {
        // 步骤1:获取文件扩展名
        String fileExtension = FileUtil.getFileExtension(fileName);
 
        // 步骤2:构建附件实体
        Attach attach = new Attach();
        attach.setDomainUrl(bladeFile.getDomain());
        attach.setLink(bladeFile.getLink());
        attach.setName(bladeFile.getName());
        attach.setOriginalName(bladeFile.getOriginalName());
        attach.setAttachSize(Long.valueOf(fileSize));
        attach.setExtension(fileExtension);
 
        // 步骤3:保存附件信息
        attachService.save(attach);
 
        // 步骤4:返回附件ID
        return attach.getId();
    }
 
    /**
     * 根据文件key生成文件名
     *
     * @param fileKey 文件唯一标识
     * @return 文件名
     */
    private String generateFileName(String fileKey) {
        // 步骤1:可根据业务需求从数据库查询原始文件名
        // FileEntity file = fileMapper.selectByKey(fileKey);
        // return file.getFileName();
 
        // 步骤2:默认使用fileKey作为文件名(可根据业务修改)
        return fileKey + ".docx";
    }
}