diff --git a/pom.xml b/pom.xml index 1290a62..77f2c05 100644 --- a/pom.xml +++ b/pom.xml @@ -53,6 +53,29 @@ spring-boot-starter-test test + + + org.apache.httpcomponents + httpclient + 4.5.14 + + + + cn.hutool + hutool-all + 5.8.32 + + + com.alibaba.fastjson2 + fastjson2 + 2.0.53 + + + + org.apache.httpcomponents + httpmime + 4.5.13 + diff --git a/src/main/java/vin/vio/collaboraonline/controller/WopiController.java b/src/main/java/vin/vio/collaboraonline/controller/WopiController.java index 835316e..96bfde1 100644 --- a/src/main/java/vin/vio/collaboraonline/controller/WopiController.java +++ b/src/main/java/vin/vio/collaboraonline/controller/WopiController.java @@ -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 getFile(@PathVariable String fileId) { + public ResponseEntity 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(); } diff --git a/src/main/java/vin/vio/collaboraonline/exception/AllKingdeeException.java b/src/main/java/vin/vio/collaboraonline/exception/AllKingdeeException.java new file mode 100644 index 0000000..2c8b882 --- /dev/null +++ b/src/main/java/vin/vio/collaboraonline/exception/AllKingdeeException.java @@ -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); + } +} diff --git a/src/main/java/vin/vio/collaboraonline/kingdee/AuthService.java b/src/main/java/vin/vio/collaboraonline/kingdee/AuthService.java new file mode 100644 index 0000000..cd390d4 --- /dev/null +++ b/src/main/java/vin/vio/collaboraonline/kingdee/AuthService.java @@ -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 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 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(); + } + +} diff --git a/src/main/java/vin/vio/collaboraonline/service/FileService.java b/src/main/java/vin/vio/collaboraonline/service/FileService.java index bb5145d..6a8148c 100644 --- a/src/main/java/vin/vio/collaboraonline/service/FileService.java +++ b/src/main/java/vin/vio/collaboraonline/service/FileService.java @@ -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(); - } - } } diff --git a/src/main/java/vin/vio/collaboraonline/service/KingdeeService.java b/src/main/java/vin/vio/collaboraonline/service/KingdeeService.java new file mode 100644 index 0000000..ed761f4 --- /dev/null +++ b/src/main/java/vin/vio/collaboraonline/service/KingdeeService.java @@ -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 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); + } + } +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 94cbb2c..e35dce3 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -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://:<端口>/browser/dist/cool.html?WOPISrc=http://<本机IP>:<服务端口>/wopi/files/<文件名>&lang=zh-cn \ No newline at end of file +# http://:<端口>/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' \ No newline at end of file