| New file |
| | |
| | | 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"; |
| | | } |
| | | } |