刘光辉
8 小时以前 06f314545ac3514d7ce28939920866ae8188d31c
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
package jnpf.bizcommon.onlyoffice.service.impl;
 
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWTUtil;
import jnpf.bizcommon.onlyoffice.entity.OnlyOfficeProperties;
import jnpf.exception.DataException;
import org.springframework.stereotype.Component;
 
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;
 
/** Generates a tracked-change DOCX on Document Server; never opens a browser edit session. */
@Component
public class OnlyOfficeComparisonRenderer {
    public static final String SCRIPT = "builderJS.OpenFile(Argument.originalUrl);\n"
            + "var file = builderJS.OpenTmpFile(Argument.revisedUrl);\n"
            + "AscCommonWord.CompareDocuments(Api, file);\n"
            + "file.Close();\n"
            + "builderJS.SaveFile(\"docx\", \"Result.docx\");\n"
            + "builderJS.CloseFile();\n";
    private final OnlyOfficeProperties properties;
 
    public OnlyOfficeComparisonRenderer(OnlyOfficeProperties properties) {
        this.properties = properties;
    }
 
    public String generate(String originalUrl, String revisedUrl) {
        Map<String, Object> request = new LinkedHashMap<>();
        request.put("async", false);
        request.put("url", properties.getFileFetchBaseUrl() + "/api/biz/onlyoffice/compare/script.js");
        request.put("argument", Map.of("originalUrl", originalUrl, "revisedUrl", revisedUrl));
        request.put("token", JWTUtil.createToken(request, properties.getJwtSecret().getBytes(StandardCharsets.UTF_8)));
        try (HttpResponse response = HttpRequest.post(serverBase() + "/docbuilder")
                .timeout(90_000).body(JSONUtil.toJsonStr(request)).execute()) {
            if (!response.isOk()) throw new DataException("生成版本差异失败,请稍后重试");
            cn.hutool.json.JSONObject result = JSONUtil.parseObj(response.body());
            if (!Boolean.TRUE.equals(result.getBool("end")) || result.containsKey("error") || result.getJSONObject("urls") == null) {
                throw new DataException("OnlyOffice 未能生成版本差异,请稍后重试");
            }
            return resultPath(result.getJSONObject("urls").getStr("Result.docx"));
        } catch (DataException e) {
            throw e;
        } catch (Exception e) {
            throw new DataException("生成版本差异失败或超时,请稍后重试");
        }
    }
 
    /** Only DS cache paths are accepted; the host is always the configured DS server. */
    static String resultPath(String url) {
        try {
            URI uri = URI.create(url);
            String path = uri.getRawPath();
            if (path == null || !path.startsWith("/cache/files/") || path.contains("..")
                    || path.contains("%") || !path.endsWith("/Result.docx")) {
                throw new IllegalArgumentException();
            }
            return path + (uri.getRawQuery() == null ? "" : "?" + uri.getRawQuery());
        } catch (Exception e) {
            throw new DataException("无效的版本比较结果地址");
        }
    }
 
    public byte[] readResult(String path) {
        String safePath = resultPath(path);
        try (HttpResponse response = HttpRequest.get(serverBase() + safePath).timeout(30_000).execute()) {
            if (!response.isOk()) throw new DataException("比较结果已过期,请重新比较");
            return response.bodyBytes();
        }
    }
 
    private String serverBase() {
        String base = properties.getDsInternalUrl();
        if (base == null || base.isBlank()) base = properties.getDsPublicUrl();
        if (base == null || base.isBlank()) throw new DataException("OnlyOffice 服务地址未配置");
        return base.replaceAll("/+$", "");
    }
}