Compare commits

..

2 Commits

Author SHA1 Message Date
zzs
95f5b75a1a feat:download file from kingdee 2025-02-21 17:42:43 +08:00
zzs
c828d3cdbc 🎉 2025-02-21 16:17:33 +08:00
7 changed files with 351 additions and 52 deletions

24
pom.xml
View File

@ -53,9 +53,33 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.32</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.53</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.13</version> <!-- 请根据需要使用最新版本 -->
</dependency>
</dependencies>
<build>
<finalName>collabora-api</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>

View File

@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.*;
import vin.vio.collaboraonline.model.WopiFile;
import vin.vio.collaboraonline.service.FileService;
import java.io.File;
import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.Map;
@ -48,9 +49,9 @@ public class WopiController {
// 获取文件内容
@GetMapping("/files/{fileId}/contents")
public ResponseEntity<Resource> getFile(@PathVariable String fileId) {
public ResponseEntity<File> getFile(@PathVariable String fileId) {
org.springframework.core.io.Resource fileResource = fileService.loadFileAsResource(fileId);
File fileResource = fileService.loadFileAsResource(fileId);
if (fileResource == null) {
return ResponseEntity.notFound().build();
}

View File

@ -0,0 +1,16 @@
package vin.vio.collaboraonline.exception;
/**
* @Description: TODO
* @Date: 2025/2/21 16:24
* @Created: by ZZSLL
*/
public class AllKingdeeException extends Exception
{
public AllKingdeeException(String message)
{
super(message);
}
}

View File

@ -0,0 +1,187 @@
package vin.vio.collaboraonline.kingdee;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import vin.vio.collaboraonline.exception.AllKingdeeException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @Description: TODO
* @Date: 2025/2/21 16:25
* @Created: by ZZSLL
*/
@Service
public class AuthService {
@Value("${ensign.kingdee.is-public}")
private Boolean isPublic;
@Value("${ensign.kingdee.test-public-end-point}")
private String testPublicEndPoint;
@Value("${ensign.kingdee.test-inner-end-point}")
private String testInnerEndPoint;
@Value("${ensign.kingdee.prod-public-end-point}")
private String prodPublicEndPoint;
@Value("${ensign.kingdee.prod-inner-end-point}")
private String prodInnerEndPoint;
@Value("${ensign.kingdee.env}")
private String env;
@Value("${ensign.kingdee.appToken.appId}")
private String appId;
@Value("${ensign.kingdee.appToken.appSecuret}")
private String appSecuret;
@Value("${ensign.kingdee.appToken.tenantid}")
private String tenantid;
@Value("${ensign.kingdee.appToken.accountId}")
private String accountId;
@Value("${ensign.kingdee.appToken.language}")
private String language;
@Value("${ensign.kingdee.accessToken.user}")
private String user;
@Value("${ensign.kingdee.accessToken.usertype}")
private String usertype;
public String initBasePath() {
if ("test".equals(env)) {
if (isPublic) {
return testPublicEndPoint;
} else {
return testInnerEndPoint;
}
}
if ("prod".equals(env)) {
if (isPublic) {
return prodPublicEndPoint;
} else {
return prodInnerEndPoint;
}
}
return "";
}
public String initAccessToken() throws IOException, AllKingdeeException {
final String END_POINT = initBasePath() + "/ierp/api/getAppToken.do";
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(END_POINT);
Map<String, String> body = new HashMap<>();
body.put("appId", appId);
body.put("appSecuret", appSecuret);
body.put("tenantid", tenantid);
body.put("accountId", accountId);
body.put("language", language);
HttpEntity reqEntity = new StringEntity(JSONUtil.toJsonStr(body), "utf-8");
post.setEntity(reqEntity);
post.setConfig(initRequestConfig());
HttpResponse response = client.execute(post);
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity resEntity = response.getEntity();
String responseData = EntityUtils.toString(resEntity, "utf-8");
JSONObject jsonObject = JSON.parseObject(responseData);
Boolean status = (Boolean) jsonObject.get("status");
String state = (String) jsonObject.get("state");
if (status && "success".equals(state)) {
JSONObject data = (JSONObject) jsonObject.get("data");
String appToken = (String) data.get("app_token");
if (appToken != null && !appToken.isEmpty()) {
return doGetAccessToken(appToken);
}
}
}
throw new AllKingdeeException("获取AppToken失败");
}
private String doGetAccessToken(String appToken) throws IOException, AllKingdeeException {
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(initBasePath() + "/ierp/api/login.do");
Map<String, String> body = new HashMap<>();
body.put("user", user);
body.put("apptoken", appToken);
body.put("tenantid", tenantid);
body.put("accountId", accountId);
body.put("language", language);
body.put("usertype", usertype);
HttpEntity reqEntity = new StringEntity(JSONUtil.toJsonStr(body), "utf-8");
post.setEntity(reqEntity);
post.setConfig(initRequestConfig());
HttpResponse response = client.execute(post);
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity resEntity = response.getEntity();
String responseData = EntityUtils.toString(resEntity, "utf-8");
JSONObject jsonObject = JSON.parseObject(responseData);
Boolean status = (Boolean) jsonObject.get("status");
String state = (String) jsonObject.get("state");
if (status && "success".equals(state)) {
JSONObject data = (JSONObject) jsonObject.get("data");
String accessToken = (String) data.get("access_token");
if (accessToken != null && !accessToken.isEmpty()) {
return accessToken;
}
}
}
throw new AllKingdeeException("获取AccessToken失败");
}
private RequestConfig initRequestConfig() {
return RequestConfig.custom()
.setConnectTimeout(5000)//连接超时connectionTimeout-->指的是连接一个url的连接等待时间
.setSocketTimeout(5000)// 读取数据超时SocketTimeout-->指的是连接上一个url获取response的返回等待时间
.setConnectionRequestTimeout(5000)
.build();
}
}

View File

@ -26,12 +26,15 @@ import java.nio.file.*;
@Service
public class FileService {
@Value("${collabora.file-storage-path}")
private String BASE_DIR;
@Autowired
private KingdeeService kingdeeService;
public WopiFile getFileInfo(String fileId) {
Path filePath = Paths.get(BASE_DIR, fileId);
Path filePath = Paths.get(kingdeeService.requestFile(fileId));
File targetFile = filePath.toFile();
@ -60,23 +63,18 @@ public class FileService {
return file;
}
public Resource loadFileAsResource(String fileId) {
try {
Path resolvedPath = validateFilePath(fileId);
public File loadFileAsResource(String fileId) {
String requestFile = kingdeeService.requestFile(fileId);
Path resolvedPath = new File(requestFile).toPath();
FileSystemResource resource = new FileSystemResource(resolvedPath);
if (resource.exists() && resource.isReadable() && Files.isRegularFile(resolvedPath)) {
return resource;
}
return null;
} catch (InvalidPathException | SecurityException e) {
throw new RuntimeException("非法文件访问: " + fileId, e);
}
return resolvedPath.toFile();
}
public boolean saveFile(String fileId, InputStream content) {
Path targetPath = validateFilePath(fileId);
String requestFile = kingdeeService.requestFile(fileId);
Path targetPath = new File(requestFile).toPath();
try {
Path tempFile = Files.createTempFile(targetPath.getParent(), "wopi_", ".tmp");
@ -90,43 +88,9 @@ public class FileService {
}
Files.move(tempFile, targetPath, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
setFilePermissions(targetPath);
return true;
} catch (IOException e) {
throw new RuntimeException("文件保存失败: " + targetPath, e);
}
}
private Path validateFilePath(String fileId) {
if (fileId.contains("..") || fileId.contains(":") || fileId.contains("\\\\")) {
throw new SecurityException("非法文件名: " + fileId);
}
Path basePath = Paths.get(BASE_DIR).normalize().toAbsolutePath();
Path resolvedPath = basePath.resolve(fileId).normalize();
if (!resolvedPath.startsWith(basePath)) {
throw new SecurityException("路径越界访问: " + fileId);
}
return resolvedPath;
}
private void setFilePermissions(Path path) throws IOException {
if (!System.getProperty("os.name").toLowerCase().contains("win")) return;
String aclCommand = String.format(
"icacls \"%s\" /grant:r \"%s\":(F) /inheritance:r",
path.toString(),
System.getProperty("user.name")
);
try {
Runtime.getRuntime().exec(aclCommand).waitFor();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}

View File

@ -0,0 +1,87 @@
package vin.vio.collaboraonline.service;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import vin.vio.collaboraonline.exception.AllKingdeeException;
import vin.vio.collaboraonline.kingdee.AuthService;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import static cn.hutool.http.HttpUtil.downloadFile;
/**
* @Description: TODO
* @Date: 2025/2/21 16:40
* @Created: by ZZSLL
*/
@Service
public class KingdeeService {
@Value("${collabora.file-storage-path}")
private String BASE_DIR;
@Autowired
private AuthService authService;
public String requestFile(String attPkId) {
try {
String accessToken = authService.initAccessToken();
URI newUri = new URI(authService.initBasePath() + "/ierp/kapi/v2/yem/yem_receipt/AttachmentsQueryAPI/Query");
ClientHttpRequest delegate = new SimpleClientHttpRequestFactory().createRequest(newUri, HttpMethod.POST);
delegate.getHeaders().add("access_token", accessToken);
delegate.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
Map<String, String> requestBody = new HashMap<>();
requestBody.put("formId", "yem_testbill");
requestBody.put("pkId", attPkId);
requestBody.put("attachKey", "attachmentpanel");
String jsonBody = JSON.toJSONString(requestBody);
// 设置请求体
try (OutputStream os = delegate.getBody()) {
os.write(jsonBody.getBytes(StandardCharsets.UTF_8));
}
ClientHttpResponse response = delegate.execute();
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getBody()));
StringBuilder responseBody = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
responseBody.append(line);
}
String savePath = "";
JSONObject result = JSON.parseObject(responseBody.toString());
if (result.containsKey("status") && result.getBooleanValue("status", false)) {
JSONArray data = result.getJSONArray("data");
if (data != null && !data.isEmpty()) {
JSONObject data0 = data.getJSONObject(0);
String url = data0.getString("url");
String name = data0.getString("name");
savePath = BASE_DIR + "/" + name;
downloadFile(url, savePath);
}
} else {
throw new AllKingdeeException(line);
}
return savePath;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}

View File

@ -8,4 +8,24 @@ spring:
# http://47.99.175.155:59980/browser/dist/cool.html?WOPISrc=http://47.99.175.155:59981/wopi/files/test.docx&lang=zh-cn
# http://<collabora容器IP>:<端口>/browser/dist/cool.html?WOPISrc=http://<本机IP>:<服务端口>/wopi/files/<文件名>&lang=zh-cn
# http://<collabora容器IP>:<端口>/browser/dist/cool.html?WOPISrc=http://<本机IP>:<服务端口>/wopi/files/<文件名>&lang=zh-cn
# http://192.168.3.211:9980/browser/dist/cool.html?WOPISrc=http://192.168.3.104:8080/wopi/files/2087131858671270912&lang=zh-cn
ensign:
kingdee:
env: test
test-public-end-point: 'http://122.4.221.133:8022'
test-inner-end-point: 'http://10.64.112.152:8022'
prod-public-end-point: 'http://122.4.221.130:8022'
prod-inner-end-point: 'http://10.64.111.134:8022'
is-public: true
appToken:
appId: 'CRM'
appSecuret: 'yx.CRM.tksecurit@240924'
tenantid: 'yxzg-topview-dev'
accountId: '1878420900609526784'
language: 'zh_CN'
accessToken:
user: 'COSMIC'
usertype: 'UserName'