吉安感知网项目-后端
linwei
2026-07-10 2fd8b654d1eb9f1eb5e1a5537df845752f24726a
feat(resource): 集成OnlyOffice文档编辑功能
2 files modified
141 ■■■■■ changed files
drone-ops/drone-resource/src/main/java/org/sxkj/resource/controller/AttachController.java 86 ●●●●● patch | view | raw | blame | history
drone-ops/drone-resource/src/main/java/org/sxkj/resource/util/OnlyofficeJwt.java 55 ●●●●● patch | view | raw | blame | history
drone-ops/drone-resource/src/main/java/org/sxkj/resource/controller/AttachController.java
@@ -9,7 +9,7 @@
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.mp.support.Condition;
@@ -21,6 +21,8 @@
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.DateTimeUtil;
import org.springblade.core.tool.utils.Func;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.sxkj.common.model.ResponseResult;
@@ -57,12 +59,21 @@
@Slf4j
@NonDS
@RestController
@AllArgsConstructor
@RequiredArgsConstructor
@RequestMapping("/attach")
@Api(value = "附件表接口", tags = "附件")
public class AttachController extends BladeController {
    private final IAttachService attachService;
    private final OnlyofficeJwt onlyofficeJwt;
    /** OnlyOffice应用服务基础URL,供Document Server访问代理下载接口 */
    @Value("${onlyoffice.base-url:}")
    private String onlyofficeBaseUrl;
    /** OnlyOffice回调地址 */
    @Value("${onlyoffice.callback-url:}")
    private String onlyofficeCallbackUrl;
    /** 远程文件URL缓存,key为attachId,value为远程文件URL */
    private static final ConcurrentHashMap<Long, String> REMOTE_URL_CACHE = new ConcurrentHashMap<>();
@@ -435,8 +446,7 @@
        Map<String, Object> editorConfig = new HashMap<>();
        editorConfig.put("customization", customization);
        editorConfig.put("mode", "edit");
        // editorConfig.put("callbackUrl", "http://192.168.1.33:8310/office/callback");
        editorConfig.put("callbackUrl", "http://172.18.10.4:8010/office/callback");
        editorConfig.put("callbackUrl", onlyofficeCallbackUrl);
        editorConfig.put("lang", "zh-CN");
        editorConfig.put("user", userConfig);
@@ -467,7 +477,14 @@
        // 10. 构建代理下载URL,替代直接使用远程URL
        // Document Server通过代理下载接口获取文件,避免无法直接访问远程URL的问题
        String proxyDownloadUrl = buildProxyDownloadUrl(request, attachId);
        String documentUrl = buildProxyDownloadUrl(request, attachId);
        // if (onlyofficeBaseUrl != null && !onlyofficeBaseUrl.isEmpty()) {
        //     // 使用代理下载URL,Document Server通过本服务的代理接口获取文件
        //     documentUrl = onlyofficeBaseUrl + "/attach/onlyoffice-download?attachId=" + attachId;
        // } else {
        //     // 未配置baseUrl时回退到直接使用远程URL
        //     documentUrl = fileUrl != null ? fileUrl : "";
        // }
        // 11. 生成文档唯一标识
        String key = generateRevisionId(fileName);
@@ -475,9 +492,8 @@
        // 12. 构建文档配置,使用代理下载URL替代远程URL
        Map<String, Object> document = new HashMap<>();
        document.put("title", fileName != null ? fileName : "未命名文档");
        document.put("url", proxyDownloadUrl); // 使用代理下载URL
        document.put("url", documentUrl);
        document.put("fileType", fileType);
        document.put("attachId", attach.getId());
        document.put("key", attach.getMd5());
        // 13. 构建最终返回结果
@@ -487,8 +503,8 @@
        result.put("editorConfig", editorConfig);
        // 14. 生成 JWT token
        Map<String, Object> result1 = OnlyofficeJwt.getToken(result);
        log.info("ONLYOFFICE配置生成完成,attachId: {}, 代理下载URL: {}", attachId, proxyDownloadUrl);
        Map<String, Object> result1 = onlyofficeJwt.getToken(result);
        log.info("ONLYOFFICE配置生成完成,attachId: {}, documentUrl: {}", attachId, documentUrl);
        return R.data(result1);
    }
    public static final Integer MAX_KEY_LENGTH = 20;
@@ -502,6 +518,32 @@
        String key = formatKey.replace("[^0-9-.a-zA-Z_=]", "_");
        return key.substring(0, Math.min(key.length(), MAX_KEY_LENGTH));  // the resulting key length is 20 or less
    }
    /**
     * 构建当前服务器的代理下载URL
     *
     * @param request  HTTP请求对象,用于获取服务器地址
     * @param attachId 附件ID
     * @return 代理下载URL地址
     */
    private String buildProxyDownloadUrl(HttpServletRequest request, Long attachId) {
        // 1. 获取请求的协议、主机名和端口
        String scheme = request.getScheme();
        String serverName = request.getServerName();
        int serverPort = request.getServerPort();
        // 2. 构建服务器基础URL(省略默认端口)
        StringBuilder baseUrl = new StringBuilder();
        baseUrl.append(scheme).append("://").append(serverName);
        if (("http".equals(scheme) && serverPort != 80) || ("https".equals(scheme) && serverPort != 443)) {
            baseUrl.append(":").append(serverPort);
        }
        // baseUrl.append("http:192.168.1.33:80/blade-resource");
        // 3. 拼接代理下载URL
        return baseUrl.toString() + "/attach/onlyoffice-download?attachId=" + attachId;
    }
    /**
@@ -586,32 +628,6 @@
                connection.disconnect();
            }
        }
    }
    /**
     * 构建当前服务器的代理下载URL
     *
     * @param request  HTTP请求对象,用于获取服务器地址
     * @param attachId 附件ID
     * @return 代理下载URL地址
     */
    private String buildProxyDownloadUrl(HttpServletRequest request, Long attachId) {
        // 1. 获取请求的协议、主机名和端口
        String scheme = request.getScheme();
        String serverName = request.getServerName();
        int serverPort = request.getServerPort();
        // 2. 构建服务器基础URL(省略默认端口)
        StringBuilder baseUrl = new StringBuilder();
        baseUrl.append(scheme).append("://").append(serverName);
        if (("http".equals(scheme) && serverPort != 80) || ("https".equals(scheme) && serverPort != 443)) {
            baseUrl.append(":").append(serverPort);
        }
        // baseUrl.append("http:192.168.1.33:80/blade-resource");
        // 3. 拼接代理下载URL
        return baseUrl.toString() + "/attach/onlyoffice-download?attachId=" + attachId;
    }
}
drone-ops/drone-resource/src/main/java/org/sxkj/resource/util/OnlyofficeJwt.java
@@ -3,40 +3,57 @@
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
 * OnlyOffice JWT令牌生成工具类
 * <p>
 * 用于生成OnlyOffice Document Server所需的JWT令牌,
 * 密钥通过Nacos配置中心统一管理。
 * </p>
 *
 * @author sxkj
 */
@Slf4j
@Component
public class OnlyofficeJwt {
    /**
     * 把jsonObject返回给前端使用
     * @return  ztzf-wv5d0-kx5xo-dx2w2-xpx5q
     * JWT令牌签名密钥,从Nacos配置读取
     */
    public static Map<String, Object> getToken(Map<String, Object> config){
         // String tokenSecret = "ztzf-aisky-kx5xo-dx2w2-xpx5q";
         String tokenSecret = "ztzf-wv5d0-kx5xo-dx2w2-xpx5q";
         String token = "";
    @Value("${onlyoffice.token-secret:ztzf-aisky-kx5xo-dx2w2-xpx5q}")
    private String tokenSecret;
    /**
     * 生成JWT令牌并将令牌添加到配置Map中
     * <p>
     * 使用HMAC256算法对OnlyOffice配置进行签名,
     * 生成令牌后将其放入config的token字段。
     * </p>
     *
     * @param config OnlyOffice编辑器配置Map,包含document、editorConfig等信息
     * @return 添加了token字段的配置Map
     */
    public Map<String, Object> getToken(Map<String, Object> config) {
        String token = "";
        try {
            // Signer signer = HMACSigner.newSHA256Signer(tokenSecret);
            // JWT jwt = new JWT();
            // for (String key : payloadClaims.keySet()) {
            //     jwt.addClaim(key, payloadClaims.get(key));
            // }
            // token = JWT.getEncoder().encode(jwt, signer);
            // 1. 使用配置的密钥创建HMAC256签名算法
            Algorithm algorithm = Algorithm.HMAC256(tokenSecret);
            // Calendar calendar = Calendar.getInstance();
            // Instant issuedAt = calendar.toInstant();
            // calendar.add(12, 5);
            // Instant expiresAt = calendar.toInstant();
            // token = JWT.create().withIssuedAt(issuedAt).withExpiresAt(expiresAt).withPayload(payloadClaims).sign(algorithm);
             token = JWT.create()
            // 2. 基于config负载生成JWT令牌
            token = JWT.create()
                .withPayload(config)
                .sign(algorithm);
        } catch (Exception e) {
            log.error("生成OnlyOffice JWT令牌失败: {}", e.getMessage(), e);
            token = "";
        }
        // 3. 将生成的令牌放入配置中
        config.put("token", token);
        return config;
    }