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("/+$", "");
|
}
|
}
|