正在显示
27 个修改的文件
包含
2401 行增加
和
28 行删除
@@ -209,6 +209,31 @@ | @@ -209,6 +209,31 @@ | ||
209 | <optional>true</optional> | 209 | <optional>true</optional> |
210 | </dependency> | 210 | </dependency> |
211 | 211 | ||
212 | + <!--okhttp--> | ||
213 | + <dependency> | ||
214 | + <groupId>com.squareup.okhttp3</groupId> | ||
215 | + <artifactId>okhttp</artifactId> | ||
216 | + <version>4.9.3</version> | ||
217 | + </dependency> | ||
218 | + | ||
219 | + <dependency> | ||
220 | + <groupId>org.apache.tika</groupId> | ||
221 | + <artifactId>tika-core</artifactId> | ||
222 | + <version>1.28.5</version> | ||
223 | + </dependency> | ||
224 | + <dependency> | ||
225 | + <groupId>org.apache.tika</groupId> | ||
226 | + <artifactId>tika-parsers</artifactId> | ||
227 | + <version>1.28.5</version> | ||
228 | + </dependency> | ||
229 | + | ||
230 | + <dependency> | ||
231 | + <groupId>com.itextpdf</groupId> | ||
232 | + <artifactId>itext7-core</artifactId> | ||
233 | + <version>7.2.5</version> | ||
234 | + <type>pom</type> | ||
235 | + </dependency> | ||
236 | + | ||
212 | <!-- Test --> | 237 | <!-- Test --> |
213 | <dependency> | 238 | <dependency> |
214 | <groupId>org.springframework.boot</groupId> | 239 | <groupId>org.springframework.boot</groupId> |
1 | +package com.aigeo.article.controller; | ||
2 | + | ||
3 | +import com.aigeo.article.service.SearchService; | ||
4 | +import com.aigeo.common.Result; | ||
5 | +import io.swagger.v3.oas.annotations.tags.Tag; | ||
6 | +import org.springframework.beans.factory.annotation.Autowired; | ||
7 | +import org.springframework.web.bind.annotation.PostMapping; | ||
8 | +import org.springframework.web.bind.annotation.RequestBody; | ||
9 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
10 | +import org.springframework.web.bind.annotation.RestController; | ||
11 | + | ||
12 | +@RestController | ||
13 | +@RequestMapping("/api/search") | ||
14 | +@Tag(name = "搜索", description = "搜索功能相关接口") | ||
15 | +public class SearchController { | ||
16 | + | ||
17 | + @Autowired | ||
18 | + private SearchService searchService; | ||
19 | + | ||
20 | + @PostMapping("/keywords") | ||
21 | + public Result<String> searchKeywords(@RequestBody String keywords) { | ||
22 | + searchService.search(keywords); | ||
23 | + return Result.success(""); | ||
24 | + } | ||
25 | +} |
1 | +package com.aigeo.article.service.impl; | ||
2 | + | ||
3 | +import com.aigeo.article.service.SearchService; | ||
4 | +import com.aigeo.common.Result; | ||
5 | +import org.springframework.stereotype.Service; | ||
6 | + | ||
7 | +@Service | ||
8 | +public class SearchServiceImpl implements SearchService { | ||
9 | + | ||
10 | + @Override | ||
11 | + public Result<String> search(String keyword) { | ||
12 | + return null; | ||
13 | + } | ||
14 | +} |
@@ -30,34 +30,34 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { | @@ -30,34 +30,34 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { | ||
30 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) | 30 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) |
31 | throws ServletException, IOException { | 31 | throws ServletException, IOException { |
32 | 32 | ||
33 | - final String requestTokenHeader = request.getHeader("Authorization"); | ||
34 | - | ||
35 | - String username = null; | ||
36 | - String jwtToken = null; | ||
37 | - | ||
38 | - if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) { | ||
39 | - jwtToken = requestTokenHeader.substring(7); | ||
40 | - try { | ||
41 | - username = jwtUtil.getUsernameFromToken(jwtToken); | ||
42 | - } catch (Exception e) { | ||
43 | - logger.error("Unable to get JWT Token", e); | ||
44 | - } | ||
45 | - } | ||
46 | - | ||
47 | - if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { | ||
48 | - UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); | ||
49 | - | ||
50 | - // 创建一个临时User对象用于验证token | ||
51 | - com.aigeo.company.entity.User tempUser = new com.aigeo.company.entity.User(); | ||
52 | - tempUser.setUsername(username); | ||
53 | - | ||
54 | - if (jwtUtil.validateToken(jwtToken, tempUser)) { | ||
55 | - UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( | ||
56 | - userDetails, null, userDetails.getAuthorities()); | ||
57 | - authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); | ||
58 | - SecurityContextHolder.getContext().setAuthentication(authToken); | ||
59 | - } | ||
60 | - } | 33 | +// final String requestTokenHeader = request.getHeader("Authorization"); |
34 | +// | ||
35 | +// String username = null; | ||
36 | +// String jwtToken = null; | ||
37 | +// | ||
38 | +// if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) { | ||
39 | +// jwtToken = requestTokenHeader.substring(7); | ||
40 | +// try { | ||
41 | +// username = jwtUtil.getUsernameFromToken(jwtToken); | ||
42 | +// } catch (Exception e) { | ||
43 | +// logger.error("Unable to get JWT Token", e); | ||
44 | +// } | ||
45 | +// } | ||
46 | +// | ||
47 | +// if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { | ||
48 | +// UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); | ||
49 | +// | ||
50 | +// // 创建一个临时User对象用于验证token | ||
51 | +// com.aigeo.company.entity.User tempUser = new com.aigeo.company.entity.User(); | ||
52 | +// tempUser.setUsername(username); | ||
53 | +// | ||
54 | +// if (jwtUtil.validateToken(jwtToken, tempUser)) { | ||
55 | +// UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( | ||
56 | +// userDetails, null, userDetails.getAuthorities()); | ||
57 | +// authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); | ||
58 | +// SecurityContextHolder.getContext().setAuthentication(authToken); | ||
59 | +// } | ||
60 | +// } | ||
61 | chain.doFilter(request, response); | 61 | chain.doFilter(request, response); |
62 | } | 62 | } |
63 | } | 63 | } |
1 | +package com.aigeo.entity; | ||
2 | + | ||
3 | +import com.aigeo.util.article.PlatformTypeEnum; | ||
4 | +import lombok.Data; | ||
5 | + | ||
6 | +@Data | ||
7 | +public class CreateArticleDTO { | ||
8 | + | ||
9 | + private String blogId; | ||
10 | + private String title; | ||
11 | + private Author author; | ||
12 | + private String handle; | ||
13 | + private String body; | ||
14 | + private Integer companyId; | ||
15 | + private String summary; | ||
16 | + private Boolean isPublished; | ||
17 | + private String publishDate; | ||
18 | + private String[] tags; | ||
19 | + private String imageAltText; | ||
20 | + private String imageUrl; | ||
21 | + | ||
22 | + private PlatformTypeEnum platform; | ||
23 | + | ||
24 | + @Data | ||
25 | + public static class Author { | ||
26 | + private String name; | ||
27 | + } | ||
28 | +} |
1 | +package com.aigeo.entity; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | + | ||
5 | +import java.util.List; | ||
6 | + | ||
7 | +@Data | ||
8 | +public class DifyDatasetDocuments { | ||
9 | + private Integer code; | ||
10 | + private List<Document> data; | ||
11 | + private Integer total; | ||
12 | + private Integer page; | ||
13 | + private Integer limit; | ||
14 | + private Boolean has_more; | ||
15 | +} |
1 | +package com.aigeo.entity; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | + | ||
5 | +@Data | ||
6 | +public class DifyRequestDTO { | ||
7 | + private Object inputs; | ||
8 | + private String query; | ||
9 | + private String response_mode; | ||
10 | + private String conversation_id; | ||
11 | + private String user; | ||
12 | + private FileInfos[] files; | ||
13 | + | ||
14 | + @Data | ||
15 | + public static class FileInfos{ | ||
16 | + private String type; | ||
17 | + private String transfer_method; | ||
18 | + private String url; | ||
19 | + } | ||
20 | +} |
1 | +package com.aigeo.entity; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | + | ||
5 | +@Data | ||
6 | +public class DifyResponse { | ||
7 | + /** | ||
8 | + * 任务 ID,用于请求跟踪和下方的停止响应接口 | ||
9 | + */ | ||
10 | + private String task_id; | ||
11 | + /** | ||
12 | + * 消息唯一 ID | ||
13 | + */ | ||
14 | + private String message_id; | ||
15 | + /** | ||
16 | + * 返回文本块内容 | ||
17 | + */ | ||
18 | + private String answer; | ||
19 | + /** | ||
20 | + * 返回文本块事件 | ||
21 | + */ | ||
22 | + private String event; | ||
23 | + /** | ||
24 | + * 会话 ID | ||
25 | + */ | ||
26 | + private String conversation_id; | ||
27 | + /** | ||
28 | + * 创建时间戳 | ||
29 | + */ | ||
30 | + private Long created_at; | ||
31 | + /** | ||
32 | + * 每一轮Agent迭代都会有一个唯一的id | ||
33 | + */ | ||
34 | + private String id; | ||
35 | + | ||
36 | + /** | ||
37 | + * agent_thought在消息中的位置 | ||
38 | + */ | ||
39 | + private Integer position; | ||
40 | + | ||
41 | + /** | ||
42 | + * agent的思考内容 | ||
43 | + */ | ||
44 | + private String thought; | ||
45 | + | ||
46 | + /** | ||
47 | + * 工具调用的返回结果 | ||
48 | + */ | ||
49 | + private String observation; | ||
50 | + | ||
51 | + /** | ||
52 | + * 使用的工具列表,以 ; 分割多个工具 | ||
53 | + */ | ||
54 | + private String tool; | ||
55 | + | ||
56 | + private Object tool_labels; | ||
57 | + | ||
58 | + /** | ||
59 | + * 工具的输入,JSON格式的字符串 | ||
60 | + */ | ||
61 | + private String tool_input; | ||
62 | + | ||
63 | + /** | ||
64 | + * 错误消息 | ||
65 | + */ | ||
66 | + private String message; | ||
67 | + | ||
68 | + /** | ||
69 | + * 当前 agent_thought 关联的文件ID | ||
70 | + */ | ||
71 | + private DifyRequestDTO.FileInfos message_files; | ||
72 | + | ||
73 | + private Integer code; | ||
74 | +} |
1 | +package com.aigeo.entity; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | + | ||
5 | +import java.util.List; | ||
6 | + | ||
7 | +@Data | ||
8 | +public class DifyUploadFileToDatasetDTO { | ||
9 | + private String indexing_technique; | ||
10 | + | ||
11 | + | ||
12 | + private PrecessRule process_rule; | ||
13 | + | ||
14 | + @Data | ||
15 | + public static class PrecessRule { | ||
16 | + private String mode; | ||
17 | + private Rules rules; | ||
18 | + | ||
19 | + @Data | ||
20 | + public static class Rules { | ||
21 | + private List<PreProcessingRules> pre_processing_rules; | ||
22 | + private Segmentation segmentation; | ||
23 | + | ||
24 | + @Data | ||
25 | + public static class Segmentation { | ||
26 | + private String separator; | ||
27 | + private Integer max_tokens; | ||
28 | + } | ||
29 | + | ||
30 | + @Data | ||
31 | + public static class PreProcessingRules { | ||
32 | + private String id; | ||
33 | + private String enabled; | ||
34 | + } | ||
35 | + } | ||
36 | + } | ||
37 | +} |
src/main/java/com/aigeo/entity/Document.java
0 → 100644
1 | +package com.aigeo.entity; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | + | ||
5 | +@Data | ||
6 | +public class Document { | ||
7 | + private String id; | ||
8 | + private String position; | ||
9 | + private String data_source_type; | ||
10 | + private String data_source_info; | ||
11 | + private String dataset_process_rule_id; | ||
12 | + private String name; | ||
13 | + private String created_from; | ||
14 | + private String created_by; | ||
15 | + private String created_at; | ||
16 | + private String tokens; | ||
17 | + private String indexing_status; | ||
18 | + private String error; | ||
19 | + private String enabled; | ||
20 | + private String disabled_at; | ||
21 | + private String disabled_by; | ||
22 | + private String archived; | ||
23 | + private String display_status; | ||
24 | + private String word_count; | ||
25 | + private String hit_count; | ||
26 | + private String doc_form; | ||
27 | +} |
1 | +package com.aigeo.entity; | ||
2 | + | ||
3 | +import com.aigeo.util.article.PlatformTypeEnum; | ||
4 | +import lombok.Data; | ||
5 | + | ||
6 | +@Data | ||
7 | +public class UpdateArticleDTO { | ||
8 | + private String id; | ||
9 | + private String title; | ||
10 | + private String handle; | ||
11 | + private String body; | ||
12 | + private String summary; | ||
13 | + private String[] tags; | ||
14 | + private String imageAltText; | ||
15 | + private String imageUrl; | ||
16 | + private Integer companyId; | ||
17 | + | ||
18 | + private PlatformTypeEnum platform; | ||
19 | +} |
1 | +package com.aigeo.socket; | ||
2 | + | ||
3 | +import com.alibaba.fastjson2.JSONException; | ||
4 | +import com.alibaba.fastjson2.JSONObject; | ||
5 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
6 | +import jakarta.websocket.*; | ||
7 | +import jakarta.websocket.server.ServerEndpoint; | ||
8 | +import org.springframework.stereotype.Component; | ||
9 | + | ||
10 | +import java.io.IOException; | ||
11 | +import java.util.concurrent.ConcurrentHashMap; | ||
12 | + | ||
13 | +@ServerEndpoint("/ws/chat/{userId}") | ||
14 | +@Component | ||
15 | +public class WebSocketAiServer { | ||
16 | + | ||
17 | +// public static AiSerivce aiSerivce; | ||
18 | + | ||
19 | +// TokenInfo tokenInfo = TokenInfo.getIntance(); | ||
20 | + | ||
21 | + private static final ConcurrentHashMap<String, Session> sessionMap = new ConcurrentHashMap<>(); | ||
22 | + | ||
23 | + | ||
24 | + @OnOpen | ||
25 | + public void onOpen(Session session) { | ||
26 | + sessionMap.put(session.getId(),session); | ||
27 | + System.out.println("New connection: " + session.getId()); | ||
28 | + } | ||
29 | + | ||
30 | + @OnClose | ||
31 | + public void onClose(Session session) { | ||
32 | + sessionMap.remove(session.getId()); | ||
33 | + System.out.println("Connection closed: " + session.getId()); | ||
34 | + } | ||
35 | + | ||
36 | + @OnMessage | ||
37 | + public void onMessage(String message, Session session) { | ||
38 | +// SocketDTO socketDTO = JSONObject.parseObject(message, SocketDTO.class); | ||
39 | +// socketDTO.setId(session.getId()); | ||
40 | +// // 解析消息 | ||
41 | +// try { | ||
42 | +// // 调用 reply 方法处理消息 | ||
43 | +// aiSerivce.chat(socketDTO); | ||
44 | +// } catch (JSONException e) { | ||
45 | +// e.printStackTrace(); | ||
46 | +// // 发送错误信息给客户端 | ||
47 | +// DifyResponse difyResponse = new DifyResponse(); | ||
48 | +// difyResponse.setCode(500); | ||
49 | +// sendMessage(session.getId(), difyResponse); | ||
50 | +// } catch (Exception e) { | ||
51 | +// e.printStackTrace(); | ||
52 | +// // 发送错误信息给客户端 | ||
53 | +// DifyResponse difyResponse = new DifyResponse(); | ||
54 | +// difyResponse.setCode(500); | ||
55 | +// sendMessage(session.getId(), difyResponse); | ||
56 | +// } | ||
57 | + } | ||
58 | + | ||
59 | + @OnError | ||
60 | + public void onError(Session session, Throwable throwable) { | ||
61 | + System.out.println("Error occurred: " + throwable.getMessage()); | ||
62 | + } | ||
63 | + | ||
64 | + public static void sendMessage(String sessionId, Object message) { | ||
65 | + Session session = sessionMap.get(sessionId); | ||
66 | + if (session != null && session.isOpen()) { | ||
67 | + try { | ||
68 | + ObjectMapper objectMapper = new ObjectMapper(); | ||
69 | + String jsonMessage; | ||
70 | + try { | ||
71 | + jsonMessage = objectMapper.writeValueAsString(message); | ||
72 | + } catch (Exception e) { | ||
73 | + e.printStackTrace(); | ||
74 | + jsonMessage = "Error: Failed to serialize message"; | ||
75 | + } | ||
76 | + session.getBasicRemote().sendText(jsonMessage); | ||
77 | + } catch (IOException e) { | ||
78 | + e.printStackTrace(); | ||
79 | + } | ||
80 | + } | ||
81 | + } | ||
82 | + | ||
83 | + public static void sendMessageToAll(Object message) { | ||
84 | + ObjectMapper objectMapper = new ObjectMapper(); | ||
85 | + String jsonMessage; | ||
86 | + try { | ||
87 | + jsonMessage = objectMapper.writeValueAsString(message); | ||
88 | + } catch (Exception e) { | ||
89 | + e.printStackTrace(); | ||
90 | + jsonMessage = "Error: Failed to serialize message"; | ||
91 | + } | ||
92 | + | ||
93 | + // 遍历 sessionMap 的所有 Session | ||
94 | + for (Session session : sessionMap.values()) { | ||
95 | + if (session.isOpen()) { | ||
96 | + try { | ||
97 | + session.getBasicRemote().sendText(jsonMessage); | ||
98 | + } catch (IOException e) { | ||
99 | + e.printStackTrace(); | ||
100 | + } | ||
101 | + } | ||
102 | + } | ||
103 | + } | ||
104 | +} | ||
105 | + |
src/main/java/com/aigeo/util/DifyAiUtil.java
0 → 100644
1 | +package com.aigeo.util; | ||
2 | + | ||
3 | +import com.aigeo.entity.*; | ||
4 | +import com.aigeo.socket.WebSocketAiServer; | ||
5 | +import com.alibaba.fastjson2.JSON; | ||
6 | +import com.alibaba.fastjson2.JSONObject; | ||
7 | +import okhttp3.*; | ||
8 | +import okio.BufferedSource; | ||
9 | +import org.apache.tika.exception.TikaException; | ||
10 | +import org.springframework.web.multipart.MultipartFile; | ||
11 | + | ||
12 | +import java.io.IOException; | ||
13 | +import java.util.ArrayList; | ||
14 | +import java.util.HashMap; | ||
15 | + | ||
16 | +/** | ||
17 | + * <b>Dify AI工具类.</b> | ||
18 | + * <p> | ||
19 | + * 文件详情: 如需修改请联系开发人员<br> | ||
20 | + * 版权所有: 深圳市科飞时速网络技术有限公司(0755-88843705)<br> | ||
21 | + * 技术支持: info@21gmail.com<br> | ||
22 | + * 开始时间: 2025/08/19 16:36<br> | ||
23 | + * 最后修改: 2025/08/19 16:36<br> | ||
24 | + * | ||
25 | + * @author : 温志锋 | ||
26 | + * @version : 3.0 | ||
27 | + */ | ||
28 | +public class DifyAiUtil { | ||
29 | + | ||
30 | + //测试知识库ID:8b3c4cdd-01a3-453b-8e12-bc7c0d5268db | ||
31 | + private static final String DIFY_BASE_URL="http://193.112.177.8:8088/v1"; | ||
32 | + private static final String API_KEY="app-sxppRrqW8OMd6Ak07iprjn2t"; | ||
33 | + private static final String DATASET_KEY="dataset-2nhuEkzhNshtvZ6L6Ut3G5hC"; | ||
34 | + | ||
35 | + private static final String DIFY_CHAT_URL="/chat-messages"; | ||
36 | + private static final String DATASET_ENDPOINT="/datasets/"; | ||
37 | + private static final String DATASET_DOCUMENT_ENDPOINT="/document/create-by-text"; | ||
38 | + private static final String DATASET_UPLOADFILE_ENDPOINT="/document/create-by-file"; | ||
39 | + | ||
40 | + private static final MediaType JSON_TYPE = MediaType.get("application/json; charset=utf-8"); | ||
41 | + | ||
42 | + private static final OkHttpClient client = new OkHttpClient(); | ||
43 | + | ||
44 | + /** | ||
45 | + * 回复 | ||
46 | + * @param message 用户消息 | ||
47 | + * @param sessionId 会话ID | ||
48 | + */ | ||
49 | + public static void reply(String message,String sessionId){ | ||
50 | + DifyRequestDTO difyRequestDTO = JSONObject.parseObject(message, DifyRequestDTO.class); | ||
51 | + difyRequestDTO.setInputs(new Object()); | ||
52 | + difyRequestDTO.setUser("anhssd"); | ||
53 | + difyRequestDTO.setResponse_mode("streaming"); | ||
54 | + | ||
55 | + String jsonBody = JSON.toJSONString(difyRequestDTO); | ||
56 | + | ||
57 | + // 创建请求体 | ||
58 | + RequestBody requestBody = RequestBody.create( | ||
59 | + jsonBody, | ||
60 | + JSON_TYPE | ||
61 | + ); | ||
62 | + | ||
63 | + // 创建请求 | ||
64 | + Request request = new Request.Builder() | ||
65 | + .url(DIFY_BASE_URL+DIFY_CHAT_URL) | ||
66 | + .addHeader("Authorization", "Bearer " + API_KEY) | ||
67 | + .addHeader("Content-Type", "application/json") | ||
68 | + .post(requestBody) | ||
69 | + .build(); | ||
70 | + | ||
71 | + // 发送请求并处理流式响应 | ||
72 | + client.newCall(request).enqueue(new Callback() { | ||
73 | + @Override | ||
74 | + public void onFailure(Call call, IOException e) { | ||
75 | + e.printStackTrace(); | ||
76 | + } | ||
77 | + | ||
78 | + @Override | ||
79 | + public void onResponse(Call call, Response response) throws IOException { | ||
80 | + if (!response.isSuccessful()) { | ||
81 | + System.err.println("请求失败: " + response.code() + " - " + response.body().string()); | ||
82 | + return; | ||
83 | + } | ||
84 | + | ||
85 | + processStream(response,sessionId); | ||
86 | + } | ||
87 | + }); | ||
88 | + } | ||
89 | + | ||
90 | + /** | ||
91 | + * 创建知识库 | ||
92 | + * @param datasetName 知识库名称 | ||
93 | + * @param description 知识库描述 | ||
94 | + */ | ||
95 | + public static void createDataset(String datasetName, String description) { | ||
96 | + HashMap<String, Object> map = new HashMap<>(); | ||
97 | + map.put("name", datasetName); | ||
98 | + map.put("description", description); | ||
99 | + map.put("permission", "all_team_members"); | ||
100 | + | ||
101 | + String jsonBody = JSON.toJSONString(map); | ||
102 | + | ||
103 | + RequestBody requestBody = RequestBody.create(jsonBody, JSON_TYPE); | ||
104 | + // 创建请求 | ||
105 | + Request request = new Request.Builder() | ||
106 | + .url(DIFY_BASE_URL + DATASET_ENDPOINT) | ||
107 | + .addHeader("Authorization", "Bearer " + DATASET_KEY) | ||
108 | + .addHeader("Content-Type", "application/json") | ||
109 | + .post(requestBody) | ||
110 | + .build(); | ||
111 | + | ||
112 | + // 发送请求 | ||
113 | + client.newCall(request).enqueue(new Callback() { | ||
114 | + @Override | ||
115 | + public void onFailure(Call call, IOException e) { | ||
116 | + e.printStackTrace(); | ||
117 | + } | ||
118 | + | ||
119 | + @Override | ||
120 | + public void onResponse(Call call, Response response) throws IOException { | ||
121 | + if (!response.isSuccessful()) { | ||
122 | + System.err.println("请求失败: " + response.code() + " - " + response.body().string()); | ||
123 | + return; | ||
124 | + } | ||
125 | + | ||
126 | + System.out.println("知识库创建成功: " + response.body().string()); | ||
127 | + } | ||
128 | + }); | ||
129 | + } | ||
130 | + | ||
131 | + /** | ||
132 | + * 获取知识库列表 | ||
133 | + */ | ||
134 | + public static void getDatasetList() { | ||
135 | + // 创建请求 | ||
136 | + Request request = new Request.Builder() | ||
137 | + .url(DIFY_BASE_URL + DATASET_ENDPOINT) | ||
138 | + .addHeader("Authorization", "Bearer " + DATASET_KEY) | ||
139 | + .build(); | ||
140 | + | ||
141 | + try { | ||
142 | + // 发送请求并获取响应 | ||
143 | + Response response = client.newCall(request).execute(); | ||
144 | + | ||
145 | + if (!response.isSuccessful()) { | ||
146 | + System.err.println("请求失败: " + response.code() + " - " + response.body().string()); | ||
147 | + return; | ||
148 | + } | ||
149 | + | ||
150 | + if (response.body() != null) { | ||
151 | + System.out.println("数据集列表: " + response.body().string()); | ||
152 | + } | ||
153 | + } catch (IOException e) { | ||
154 | + System.err.println("获取数据集列表失败: " + e.getMessage()); | ||
155 | + e.printStackTrace(); | ||
156 | + } | ||
157 | + } | ||
158 | + | ||
159 | + /** | ||
160 | + * 上传文件到知识库 | ||
161 | + * @param datasetId 知识库ID | ||
162 | + * @param file 文件 | ||
163 | + * @param fileName 文件名 | ||
164 | + * @throws IOException IO异常 | ||
165 | + */ | ||
166 | + public static DocumentUploadRes uploadFileToDataset(String datasetId, MultipartFile file, String fileName) throws IOException, TikaException { | ||
167 | + // 构建URL | ||
168 | + String uploadUrl = DIFY_BASE_URL + DATASET_ENDPOINT + datasetId + DATASET_UPLOADFILE_ENDPOINT; | ||
169 | + DifyUploadFileToDatasetDTO datasetDTO = new DifyUploadFileToDatasetDTO(); | ||
170 | + datasetDTO.setIndexing_technique("high_quality"); | ||
171 | + DifyUploadFileToDatasetDTO.PrecessRule precessRule = new DifyUploadFileToDatasetDTO.PrecessRule(); | ||
172 | + precessRule.setMode("custom"); | ||
173 | + DifyUploadFileToDatasetDTO.PrecessRule.Rules rules = new DifyUploadFileToDatasetDTO.PrecessRule.Rules(); | ||
174 | + DifyUploadFileToDatasetDTO.PrecessRule.Rules.Segmentation segmentation = new DifyUploadFileToDatasetDTO.PrecessRule.Rules.Segmentation(); | ||
175 | + segmentation.setSeparator("###"); | ||
176 | + segmentation.setMax_tokens(500); | ||
177 | + DifyUploadFileToDatasetDTO.PrecessRule.Rules.PreProcessingRules processingRules = new DifyUploadFileToDatasetDTO.PrecessRule.Rules.PreProcessingRules(); | ||
178 | + processingRules.setId("remove_extra_spaces"); | ||
179 | + processingRules.setEnabled("true"); | ||
180 | + DifyUploadFileToDatasetDTO.PrecessRule.Rules.PreProcessingRules processingRules1 = new DifyUploadFileToDatasetDTO.PrecessRule.Rules.PreProcessingRules(); | ||
181 | + processingRules1.setId("remove_urls_emails"); | ||
182 | + processingRules1.setEnabled("true"); | ||
183 | + ArrayList<DifyUploadFileToDatasetDTO.PrecessRule.Rules.PreProcessingRules> list = new ArrayList<>(); | ||
184 | + list.add(processingRules); | ||
185 | + list.add(processingRules1); | ||
186 | + rules.setPre_processing_rules(list); | ||
187 | + rules.setSegmentation(segmentation); | ||
188 | + precessRule.setRules(rules); | ||
189 | + datasetDTO.setProcess_rule(precessRule); | ||
190 | + String jsonString = JSONObject.toJSONString(datasetDTO); | ||
191 | + //System.out.println("Request JSON: " + jsonString); | ||
192 | + // 关键修改:使用正确的媒体类型 application/json | ||
193 | + RequestBody dataBody = RequestBody.create( | ||
194 | + jsonString, | ||
195 | + MediaType.parse("text/plain") | ||
196 | + ); | ||
197 | + | ||
198 | + RequestBody requestBody = new MultipartBody.Builder() | ||
199 | + .setType(MultipartBody.FORM) | ||
200 | + // 修改点:使用 application/json 类型传递JSON数据 | ||
201 | + .addFormDataPart("data", null, dataBody) | ||
202 | + .addFormDataPart("file", fileName, | ||
203 | + RequestBody.create(file.getBytes(), MediaType.parse("application/octet-stream"))) | ||
204 | + .build(); | ||
205 | + // 创建请求 | ||
206 | + Request request = new Request.Builder() | ||
207 | + .url(uploadUrl) | ||
208 | + .addHeader("Authorization", "Bearer " + DATASET_KEY) | ||
209 | + .post(requestBody) | ||
210 | + .build(); | ||
211 | + | ||
212 | + try (Response response = client.newCall(request).execute()) { | ||
213 | + if (!response.isSuccessful()) { | ||
214 | + String errorBody = response.body() != null ? response.body().string() : "null"; | ||
215 | + System.err.println("上传文件请求失败: " + response.code() + " - " + errorBody); | ||
216 | + DocumentUploadRes documentUploadRes = new DocumentUploadRes(); | ||
217 | + documentUploadRes.setCode(response.code()); | ||
218 | + return documentUploadRes; | ||
219 | + } | ||
220 | + if (response.body() != null) { | ||
221 | + String responseBody = response.body().string(); | ||
222 | + return JSONObject.parseObject(responseBody, DocumentUploadRes.class); // 直接返回结果 | ||
223 | + } else { | ||
224 | + DocumentUploadRes documentUploadRes = new DocumentUploadRes(); | ||
225 | + documentUploadRes.setCode(400); | ||
226 | + return documentUploadRes; | ||
227 | + } | ||
228 | + } | ||
229 | + } | ||
230 | + | ||
231 | + /** | ||
232 | + * 上传知识库内容并创建文档至知识库 | ||
233 | + * @param datasetId 知识库ID | ||
234 | + * @param documentName 文档名称 | ||
235 | + * @param text 文本内容 | ||
236 | + */ | ||
237 | + public static void createDocumentByText(String datasetId, String documentName, String text) throws IOException { | ||
238 | + // 构建URL | ||
239 | + String url = DIFY_BASE_URL + DATASET_ENDPOINT + datasetId + DATASET_DOCUMENT_ENDPOINT; | ||
240 | + | ||
241 | + // 构建请求数据 | ||
242 | + JSONObject requestData = new JSONObject(); | ||
243 | + requestData.put("name", documentName); | ||
244 | + requestData.put("text", text); | ||
245 | + requestData.put("indexing_technique", "high_quality"); | ||
246 | + | ||
247 | + // 构建处理规则 | ||
248 | + JSONObject processRule = new JSONObject(); | ||
249 | + processRule.put("mode", "automatic"); | ||
250 | + requestData.put("process_rule", processRule); | ||
251 | + | ||
252 | + String jsonBody = requestData.toJSONString(); | ||
253 | + | ||
254 | + // 创建请求体 | ||
255 | + RequestBody requestBody = RequestBody.create(jsonBody, JSON_TYPE); | ||
256 | + | ||
257 | + // 创建请求 | ||
258 | + Request request = new Request.Builder() | ||
259 | + .url(url) | ||
260 | + .addHeader("Authorization", "Bearer " + DATASET_KEY) | ||
261 | + .addHeader("Content-Type", "application/json") | ||
262 | + .post(requestBody) | ||
263 | + .build(); | ||
264 | + | ||
265 | + // 发送请求 | ||
266 | + client.newCall(request).enqueue(new Callback() { | ||
267 | + @Override | ||
268 | + public void onFailure(Call call, IOException e) { | ||
269 | + System.err.println("创建文档失败: " + e.getMessage()); | ||
270 | + e.printStackTrace(); | ||
271 | + } | ||
272 | + | ||
273 | + @Override | ||
274 | + public void onResponse(Call call, Response response) throws IOException { | ||
275 | + if (!response.isSuccessful()) { | ||
276 | + String errorBody = response.body() != null ? response.body().string() : "null"; | ||
277 | + System.err.println("创建文档请求失败: " + response.code() + " - " + errorBody); | ||
278 | + return; | ||
279 | + } | ||
280 | + | ||
281 | + if (response.body() != null) { | ||
282 | + System.out.println("文档创建成功: " + response.body().string()); | ||
283 | + } | ||
284 | + } | ||
285 | + }); | ||
286 | + } | ||
287 | + | ||
288 | + /** | ||
289 | + * 获取指定数据集中特定文档的详细信息 | ||
290 | + * @param datasetId 数据集ID | ||
291 | + * @param documentId 文档ID | ||
292 | + * @throws IOException IO异常 | ||
293 | + */ | ||
294 | + public static void getDocumentById(String datasetId, String documentId) throws IOException { | ||
295 | + // 构建URL | ||
296 | + String url = DIFY_BASE_URL + DATASET_ENDPOINT + datasetId + "/documents/" + documentId; | ||
297 | + | ||
298 | + // 创建请求 | ||
299 | + Request request = new Request.Builder() | ||
300 | + .url(url) | ||
301 | + .addHeader("Authorization", "Bearer " + DATASET_KEY) | ||
302 | + .get() // 明确指定GET方法 | ||
303 | + .build(); | ||
304 | + | ||
305 | + // 发送请求 | ||
306 | + client.newCall(request).enqueue(new Callback() { | ||
307 | + @Override | ||
308 | + public void onFailure(Call call, IOException e) { | ||
309 | + System.err.println("获取文档失败: " + e.getMessage()); | ||
310 | + e.printStackTrace(); | ||
311 | + } | ||
312 | + | ||
313 | + @Override | ||
314 | + public void onResponse(Call call, Response response) throws IOException { | ||
315 | + if (!response.isSuccessful()) { | ||
316 | + String errorBody = response.body() != null ? response.body().string() : "null"; | ||
317 | + System.err.println("获取文档请求失败: " + response.code() + " - " + errorBody); | ||
318 | + return; | ||
319 | + } | ||
320 | + | ||
321 | + if (response.body() != null) { | ||
322 | + System.out.println("文档详情: " + response.body().string()); | ||
323 | + } | ||
324 | + } | ||
325 | + }); | ||
326 | + } | ||
327 | + | ||
328 | + /** | ||
329 | + * 获取指定知识库中的文档列表 | ||
330 | + * @param datasetId 数据集ID | ||
331 | + * @param page 页码(从1开始) | ||
332 | + * @param limit 每页返回条数 | ||
333 | + * @param keyword 搜索关键词 | ||
334 | + * @return 响应内容 | ||
335 | + * @throws IOException IO异常 | ||
336 | + */ | ||
337 | + public static DifyDatasetDocuments getDatasetDocuments(String datasetId, Integer page, Integer limit, String keyword) throws IOException { | ||
338 | + // 构建基础URL | ||
339 | + String url = DIFY_BASE_URL + DATASET_ENDPOINT + datasetId + "/documents"; | ||
340 | + | ||
341 | + // 构建查询参数 | ||
342 | + HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder(); | ||
343 | + | ||
344 | + // 添加可选参数 | ||
345 | + if (page != null && page > 0) { | ||
346 | + urlBuilder.addQueryParameter("page", String.valueOf(page)); | ||
347 | + } | ||
348 | + | ||
349 | + if (limit != null && limit > 0) { | ||
350 | + urlBuilder.addQueryParameter("limit", String.valueOf(limit)); | ||
351 | + } | ||
352 | + | ||
353 | + if (keyword != null && !keyword.trim().isEmpty()) { | ||
354 | + urlBuilder.addQueryParameter("keyword", keyword); | ||
355 | + } | ||
356 | + | ||
357 | + // 构建最终URL | ||
358 | + String finalUrl = urlBuilder.build().toString(); | ||
359 | + | ||
360 | + // 创建请求 | ||
361 | + Request request = new Request.Builder() | ||
362 | + .url(finalUrl) | ||
363 | + .addHeader("Authorization", "Bearer " + DATASET_KEY) | ||
364 | + .build(); | ||
365 | + | ||
366 | + // 发送请求并处理响应 | ||
367 | + try (Response response = client.newCall(request).execute()) { | ||
368 | + if (!response.isSuccessful()) { | ||
369 | + DifyDatasetDocuments documents = new DifyDatasetDocuments(); | ||
370 | + documents.setCode(500); | ||
371 | + return documents; | ||
372 | + } | ||
373 | + | ||
374 | + if (response.body() != null) { | ||
375 | + DifyDatasetDocuments documents = JSONObject.parseObject(response.body().string(), DifyDatasetDocuments.class); | ||
376 | + documents.setCode(200); | ||
377 | + return documents; | ||
378 | + } else { | ||
379 | + DifyDatasetDocuments documents = new DifyDatasetDocuments(); | ||
380 | + documents.setCode(500); | ||
381 | + return documents; | ||
382 | + } | ||
383 | + } catch (IOException e) { | ||
384 | + System.err.println("获取文档列表失败: " + e.getMessage()); | ||
385 | + throw e; | ||
386 | + } | ||
387 | + } | ||
388 | + | ||
389 | + /** | ||
390 | + * 同步方式删除指定数据集中的特定文档 | ||
391 | + * @param datasetId 数据集ID | ||
392 | + * @param documentId 文档ID | ||
393 | + * @return 删除操作结果 | ||
394 | + * @throws IOException IO异常 | ||
395 | + */ | ||
396 | + public static boolean deleteDocumentSync(String datasetId, String documentId) throws IOException { | ||
397 | + // 构建URL | ||
398 | + String url = DIFY_BASE_URL + DATASET_ENDPOINT + datasetId + "/documents/" + documentId; | ||
399 | + | ||
400 | + // 创建DELETE请求 | ||
401 | + Request request = new Request.Builder() | ||
402 | + .url(url) | ||
403 | + .addHeader("Authorization", "Bearer " + DATASET_KEY) | ||
404 | + .delete() | ||
405 | + .build(); | ||
406 | + | ||
407 | + // 发送请求并处理响应 | ||
408 | + try (Response response = client.newCall(request).execute()) { | ||
409 | + if (!response.isSuccessful()) { | ||
410 | + String errorBody = response.body() != null ? response.body().string() : "null"; | ||
411 | + System.err.println("删除文档请求失败: " + response.code() + " - " + errorBody); | ||
412 | + return false; | ||
413 | + } | ||
414 | + | ||
415 | + System.out.println("文档删除成功: " + response.code()); | ||
416 | + return true; | ||
417 | + } catch (IOException e) { | ||
418 | + System.err.println("删除文档失败: " + e.getMessage()); | ||
419 | + throw e; | ||
420 | + } | ||
421 | + } | ||
422 | + | ||
423 | + | ||
424 | + private static void processStream(Response response,String sessionId) throws IOException { | ||
425 | + try (BufferedSource source = response.body().source()) { | ||
426 | + StringBuilder eventBuffer = new StringBuilder(); | ||
427 | + | ||
428 | + while (!source.exhausted()) { | ||
429 | + String line = source.readUtf8Line(); | ||
430 | + if (line == null) break; | ||
431 | + | ||
432 | + if (line.startsWith("data:")) { | ||
433 | + eventBuffer.setLength(0); | ||
434 | + eventBuffer.append(line.substring(5).trim()); | ||
435 | + } else if (line.isEmpty() && eventBuffer.length() > 0) { | ||
436 | + handleEvent(eventBuffer.toString(),sessionId); | ||
437 | + eventBuffer.setLength(0); | ||
438 | + } | ||
439 | + } | ||
440 | + } | ||
441 | + } | ||
442 | + | ||
443 | + private static void handleEvent(String eventData,String sessionId) { | ||
444 | + try { | ||
445 | + DifyResponse event = JSON.parseObject(eventData, DifyResponse.class); | ||
446 | + | ||
447 | + switch (event.getEvent()) { | ||
448 | + case "agent_thought": | ||
449 | + //System.out.println("[思考] " + event.getThought()); | ||
450 | + break; | ||
451 | + case "agent_message": | ||
452 | + System.out.print(event.getAnswer()); | ||
453 | + event.setCode(200); | ||
454 | + WebSocketAiServer.sendMessage(sessionId,event); | ||
455 | + break; | ||
456 | + case "message_end": | ||
457 | + event.setCode(203); | ||
458 | + System.out.println("\n[对话结束]"); | ||
459 | + WebSocketAiServer.sendMessage(sessionId,event); | ||
460 | + break; | ||
461 | + | ||
462 | + case "error": | ||
463 | + event.setCode(500); | ||
464 | + System.err.println("[错误] " + event.getMessage()); | ||
465 | + WebSocketAiServer.sendMessage(sessionId,event); | ||
466 | + break; | ||
467 | + default: | ||
468 | + System.out.println("[事件] " + event.getEvent()); | ||
469 | + } | ||
470 | + } catch (Exception e) { | ||
471 | + System.err.println("解析错误: " + eventData); | ||
472 | + e.printStackTrace(); | ||
473 | + } | ||
474 | + } | ||
475 | +} |
1 | +package com.aigeo.util; | ||
2 | + | ||
3 | +import org.apache.poi.util.Units; | ||
4 | +import org.apache.poi.xwpf.usermodel.ParagraphAlignment; | ||
5 | +import org.apache.poi.xwpf.usermodel.XWPFDocument; | ||
6 | +import org.apache.poi.xwpf.usermodel.XWPFParagraph; | ||
7 | +import org.apache.poi.xwpf.usermodel.XWPFRun; | ||
8 | +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTInd; | ||
9 | +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr; | ||
10 | +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSpacing; | ||
11 | +import org.openxmlformats.schemas.wordprocessingml.x2006.main.STLineSpacingRule; | ||
12 | + | ||
13 | +import java.io.FileOutputStream; | ||
14 | +import java.io.InputStream; | ||
15 | +import java.math.BigInteger; | ||
16 | +import java.net.URL; | ||
17 | +import java.util.regex.Matcher; | ||
18 | +import java.util.regex.Pattern; | ||
19 | + | ||
20 | +public class DocumentCreator { | ||
21 | + public static String create(String content) { | ||
22 | + try { | ||
23 | + //生成一个随机文件名称 | ||
24 | + long l = System.currentTimeMillis(); | ||
25 | + String fileName = "D:/LsYwxProject/YwxErpApi/yiwaixiaoerp/YwxErp/upload/aidocx/" + l + ".docx"; | ||
26 | + createFormattedDocx(content, fileName); | ||
27 | + System.out.println("文档创建成功:自然的诗篇.docx"); | ||
28 | + return fileName; | ||
29 | + } catch (Exception e) { | ||
30 | + System.err.println("创建文档时出错: " + e.getMessage()); | ||
31 | + e.printStackTrace(); | ||
32 | + return null; | ||
33 | + } | ||
34 | + } | ||
35 | + | ||
36 | + public static void createFormattedDocx(String content, String fileName) throws Exception { | ||
37 | + // 创建新文档 | ||
38 | + XWPFDocument document = new XWPFDocument(); | ||
39 | + | ||
40 | + // 按换行符分割内容 | ||
41 | + String[] parts = content.split("\n\n"); | ||
42 | + | ||
43 | + if (parts.length == 0) return; | ||
44 | + | ||
45 | + // 处理标题(第一部分) | ||
46 | + String title = parts[0]; | ||
47 | + // 移除标题前的Markdown标记 | ||
48 | + if (title.startsWith("# ")) { | ||
49 | + title = title.substring(2); | ||
50 | + } | ||
51 | + createHeading(document, title, 1); | ||
52 | + | ||
53 | + // 处理其余部分 | ||
54 | + for (int i = 1; i < parts.length; i++) { | ||
55 | + String part = parts[i]; | ||
56 | + | ||
57 | + // 检查是否是图片标记 | ||
58 | + if (part.startsWith("![")) { | ||
59 | + String imageUrl = extractImageUrl(part); | ||
60 | + if (imageUrl != null) { | ||
61 | + // 下载并插入图片 | ||
62 | + insertImage(document, imageUrl); | ||
63 | + } | ||
64 | + } else { | ||
65 | + // 处理普通文本段落 | ||
66 | + createFormattedParagraph(document, part); | ||
67 | + } | ||
68 | + } | ||
69 | + | ||
70 | + // 保存文件 | ||
71 | + try (FileOutputStream out = new FileOutputStream(fileName)) { | ||
72 | + document.write(out); | ||
73 | + } | ||
74 | + } | ||
75 | + | ||
76 | + // 从Markdown图片标记中提取URL | ||
77 | + private static String extractImageUrl(String markdown) { | ||
78 | + // 正则表达式匹配Markdown图片语法 | ||
79 | + Pattern pattern = Pattern.compile("!\\[.*?\\]\\((.*?)\\)"); | ||
80 | + Matcher matcher = pattern.matcher(markdown); | ||
81 | + if (matcher.find()) { | ||
82 | + return matcher.group(1); | ||
83 | + } | ||
84 | + return null; | ||
85 | + } | ||
86 | + | ||
87 | + // 下载并插入图片 | ||
88 | + private static void insertImage(XWPFDocument doc, String imageUrl) throws Exception { | ||
89 | + try { | ||
90 | + // 创建新段落用于放置图片 | ||
91 | + XWPFParagraph para = doc.createParagraph(); | ||
92 | + para.setAlignment(ParagraphAlignment.CENTER); | ||
93 | + XWPFRun run = para.createRun(); | ||
94 | + | ||
95 | + // 下载图片 | ||
96 | + URL url = new URL(imageUrl); | ||
97 | + try (InputStream is = url.openStream()) { | ||
98 | + // 插入图片(宽度15厘米,高度按比例自动计算) | ||
99 | + run.addPicture(is, | ||
100 | + XWPFDocument.PICTURE_TYPE_JPEG, | ||
101 | + "image.jpg", | ||
102 | + Units.toEMU(400), // 宽度15厘米 | ||
103 | + Units.toEMU(200)); // 高度10厘米(实际会按比例调整) | ||
104 | + } | ||
105 | + | ||
106 | + // 添加图片标题(可选) | ||
107 | + //XWPFParagraph caption = doc.createParagraph(); | ||
108 | + //caption.setAlignment(ParagraphAlignment.CENTER); | ||
109 | + //XWPFRun captionRun = caption.createRun(); | ||
110 | + //captionRun.setText("自然的画卷"); | ||
111 | + //captionRun.setFontSize(10); | ||
112 | + //captionRun.setFontFamily("宋体"); | ||
113 | + //captionRun.setItalic(true); | ||
114 | + | ||
115 | + } catch (Exception e) { | ||
116 | + System.err.println("无法插入图片: " + e.getMessage()); | ||
117 | + // 创建错误信息段落 | ||
118 | + XWPFParagraph errorPara = doc.createParagraph(); | ||
119 | + XWPFRun errorRun = errorPara.createRun(); | ||
120 | + errorRun.setText("[图片加载失败: " + imageUrl + "]"); | ||
121 | + errorRun.setColor("FF0000"); | ||
122 | + } | ||
123 | + } | ||
124 | + | ||
125 | + // 创建标题方法 | ||
126 | + private static void createHeading(XWPFDocument doc, String text, int level) { | ||
127 | + XWPFParagraph heading = doc.createParagraph(); | ||
128 | + heading.setStyle("Heading" + level); | ||
129 | + | ||
130 | + XWPFRun run = heading.createRun(); | ||
131 | + run.setText(text); | ||
132 | + run.setBold(true); | ||
133 | + run.setFontSize(18); | ||
134 | + run.setFontFamily("楷体"); | ||
135 | + | ||
136 | + // 设置标题居中 | ||
137 | + heading.setAlignment(ParagraphAlignment.CENTER); | ||
138 | + | ||
139 | + // 设置段后间距 | ||
140 | + CTPPr ppr = heading.getCTP().getPPr(); | ||
141 | + if (ppr == null) ppr = heading.getCTP().addNewPPr(); | ||
142 | + CTSpacing spacing = ppr.isSetSpacing() ? ppr.getSpacing() : ppr.addNewSpacing(); | ||
143 | + spacing.setAfter(BigInteger.valueOf(400)); // 20磅间距 | ||
144 | + } | ||
145 | + | ||
146 | + // 创建格式化的正文段落 | ||
147 | + private static void createFormattedParagraph(XWPFDocument doc, String text) { | ||
148 | + XWPFParagraph para = doc.createParagraph(); | ||
149 | + XWPFRun run = para.createRun(); | ||
150 | + run.setText(text); | ||
151 | + run.setFontSize(12); | ||
152 | + run.setFontFamily("宋体"); | ||
153 | + | ||
154 | + // 设置首行缩进和行距 | ||
155 | + CTPPr ppr = para.getCTP().getPPr(); | ||
156 | + if (ppr == null) ppr = para.getCTP().addNewPPr(); | ||
157 | + | ||
158 | + // 首行缩进2字符 (1字符=400) | ||
159 | + CTInd ind = ppr.isSetInd() ? ppr.getInd() : ppr.addNewInd(); | ||
160 | + ind.setFirstLine(BigInteger.valueOf(800)); | ||
161 | + | ||
162 | + // 设置行距 (1.5倍行距) | ||
163 | + CTSpacing spacing = ppr.isSetSpacing() ? ppr.getSpacing() : ppr.addNewSpacing(); | ||
164 | + spacing.setLineRule(STLineSpacingRule.AUTO); | ||
165 | + spacing.setLine(BigInteger.valueOf(360)); // 1.5倍行距 = 1.5*240=360 | ||
166 | + | ||
167 | + // 设置段后间距 | ||
168 | + spacing.setAfter(BigInteger.valueOf(100)); // 5磅段落间距 | ||
169 | + } | ||
170 | + | ||
171 | + // 下载并插入图片 | ||
172 | + //private static void insertImage(XWPFDocument doc, String imageUrl) throws Exception { | ||
173 | + // try { | ||
174 | + // // 创建新段落用于放置图片 | ||
175 | + // XWPFParagraph para = doc.createParagraph(); | ||
176 | + // para.setAlignment(ParagraphAlignment.CENTER); | ||
177 | + // XWPFRun run = para.createRun(); | ||
178 | + // | ||
179 | + // // 下载图片并获取原始尺寸 | ||
180 | + // URL url = new URL(imageUrl); | ||
181 | + // try (InputStream is = url.openStream()) { | ||
182 | + // // 将输入流转换为字节数组,以便多次使用 | ||
183 | + // byte[] imageBytes = IOUtils.toByteArray(is); | ||
184 | + // ByteArrayInputStream bis = new ByteArrayInputStream(imageBytes); | ||
185 | + // | ||
186 | + // // 获取图片尺寸 | ||
187 | + // BufferedImage bufferedImage = ImageIO.read(bis); | ||
188 | + // if (bufferedImage != null) { | ||
189 | + // int originalWidth = bufferedImage.getWidth(); | ||
190 | + // int originalHeight = bufferedImage.getHeight(); | ||
191 | + // | ||
192 | + // // 重置bis以供图片插入使用 | ||
193 | + // bis.reset(); | ||
194 | + // bis = new ByteArrayInputStream(imageBytes); | ||
195 | + // | ||
196 | + // // 插入图片(使用原始宽高,转换为EMU单位) | ||
197 | + // run.addPicture(new ByteArrayInputStream(imageBytes), | ||
198 | + // XWPFDocument.PICTURE_TYPE_JPEG, | ||
199 | + // "image.jpg", | ||
200 | + // Units.toEMU(400), // 宽度15厘米 | ||
201 | + // Units.toEMU(200)); // 高度10厘米 | ||
202 | + // } else { | ||
203 | + // // 如果无法获取尺寸,使用默认尺寸 | ||
204 | + // run.addPicture(new ByteArrayInputStream(imageBytes), | ||
205 | + // XWPFDocument.PICTURE_TYPE_JPEG, | ||
206 | + // "image.jpg", | ||
207 | + // Units.toEMU(400), // 宽度15厘米 | ||
208 | + // Units.toEMU(200)); // 高度10厘米 | ||
209 | + // } | ||
210 | + // } | ||
211 | + // | ||
212 | + // } catch (Exception e) { | ||
213 | + // System.err.println("无法插入图片: " + e.getMessage()); | ||
214 | + // // 创建错误信息段落 | ||
215 | + // XWPFParagraph errorPara = doc.createParagraph(); | ||
216 | + // XWPFRun errorRun = errorPara.createRun(); | ||
217 | + // errorRun.setText("[图片加载失败: " + imageUrl + "]"); | ||
218 | + // errorRun.setColor("FF0000"); | ||
219 | + // } | ||
220 | + //} | ||
221 | +} |
1 | +package com.aigeo.util; | ||
2 | + | ||
3 | +import org.apache.tika.Tika; | ||
4 | +import org.apache.tika.exception.TikaException; | ||
5 | + | ||
6 | +import java.io.IOException; | ||
7 | +import java.io.InputStream; | ||
8 | +import java.util.regex.Pattern; | ||
9 | + | ||
10 | +public class DocumentParse { | ||
11 | + private final Tika tika; | ||
12 | + | ||
13 | + // 定义有意义字符的正则表达式 | ||
14 | + private static final Pattern MEANINGFUL_CHAR_PATTERN = Pattern.compile("[\\s\\u4e00-\\u9fa5a-zA-Z0-9¥$€£¢₹₽₩₪₨₦₡₫₴₵₸₺₼₾₿.,;:'\"!?()-]*"); | ||
15 | + | ||
16 | + // 字符限制 | ||
17 | + private static final int CHINESE_CHAR_LIMIT = 30000; | ||
18 | + private static final int ENGLISH_CHAR_LIMIT = 100000; | ||
19 | + | ||
20 | + // 中文比例阈值 | ||
21 | + private static final double CHINESE_THRESHOLD = 0.4; | ||
22 | + | ||
23 | + public DocumentParse() { | ||
24 | + this.tika = new Tika(); | ||
25 | + } | ||
26 | + | ||
27 | +// public String parse(InputStream inputStream) throws TikaException, IOException, DocumentParseException { | ||
28 | +// // 使用Tika解析文档内容 | ||
29 | +// String rawContent = this.tika.parseToString(inputStream); | ||
30 | +// | ||
31 | +// // 过滤特殊字符,只保留有意义的字符 | ||
32 | +// String filteredContent = filterMeaningfulCharacters(rawContent); | ||
33 | +// | ||
34 | +// // 检查字符数是否超过限制 | ||
35 | +// validateContentLength(filteredContent); | ||
36 | +// | ||
37 | +// return filteredContent; | ||
38 | +// } | ||
39 | + | ||
40 | + /** | ||
41 | + * 过滤特殊字符,只保留有意义的字符 | ||
42 | + * @param content 原始内容 | ||
43 | + * @return 过滤后的内容 | ||
44 | + */ | ||
45 | + private String filterMeaningfulCharacters(String content) { | ||
46 | + if (content == null || content.isEmpty()) { | ||
47 | + return ""; | ||
48 | + } | ||
49 | + // 使用正则表达式匹配有意义的字符 | ||
50 | + StringBuilder result = new StringBuilder(); | ||
51 | + java.util.regex.Matcher matcher = MEANINGFUL_CHAR_PATTERN.matcher(content); | ||
52 | + | ||
53 | + while (matcher.find()) { | ||
54 | + result.append(matcher.group()); | ||
55 | + } | ||
56 | + | ||
57 | + return result.toString(); | ||
58 | + } | ||
59 | + | ||
60 | + /** | ||
61 | + * 验证内容长度是否超过限制 | ||
62 | + * @param content 过滤后的内容 | ||
63 | + * @throws DocumentParseException 当内容超过限制时抛出异常 | ||
64 | + */ | ||
65 | +// private void validateContentLength(String content) throws DocumentParseException { | ||
66 | +// if (content == null || content.isEmpty()) { | ||
67 | +// return; | ||
68 | +// } | ||
69 | +// | ||
70 | +// // 计算中文字符比例 | ||
71 | +// double chineseRatio = calculateChineseRatio(content); | ||
72 | +// | ||
73 | +// if (chineseRatio > CHINESE_THRESHOLD) { | ||
74 | +// // 中文文档处理 | ||
75 | +// if (content.length() > CHINESE_CHAR_LIMIT) { | ||
76 | +// throw new DocumentParseException("您的文档字符超过限制,请缩减文档再次上传"); | ||
77 | +// } | ||
78 | +// } else { | ||
79 | +// // 英文文档处理 | ||
80 | +// if (content.length() > ENGLISH_CHAR_LIMIT) { | ||
81 | +// throw new DocumentParseException("您的文档字符超过限制,请缩减文档再次上传"); | ||
82 | +// } | ||
83 | +// } | ||
84 | +// } | ||
85 | + | ||
86 | + /** | ||
87 | + * 计算中文字符在总字符中的比例 | ||
88 | + * @param content 文档内容 | ||
89 | + * @return 中文字符比例 | ||
90 | + */ | ||
91 | + private double calculateChineseRatio(String content) { | ||
92 | + if (content == null || content.isEmpty()) { | ||
93 | + return 0.0; | ||
94 | + } | ||
95 | + | ||
96 | + int totalChars = content.length(); | ||
97 | + int chineseChars = 0; | ||
98 | + | ||
99 | + for (char c : content.toCharArray()) { | ||
100 | + // 判断是否为中文字符(基本汉字范围) | ||
101 | + if (c >= 0x4e00 && c <= 0x9fa5) { | ||
102 | + chineseChars++; | ||
103 | + } | ||
104 | + } | ||
105 | + | ||
106 | + return (double) chineseChars / totalChars; | ||
107 | + } | ||
108 | +} | ||
109 | + |
src/main/java/com/aigeo/util/FtpUtil.java
0 → 100644
1 | +package com.aigeo.util; | ||
2 | + | ||
3 | +import java.io.ByteArrayOutputStream; | ||
4 | +import java.io.FileInputStream; | ||
5 | +import java.io.IOException; | ||
6 | +import java.io.InputStream; | ||
7 | +import java.util.ArrayList; | ||
8 | +import java.util.List; | ||
9 | + | ||
10 | +public class FtpUtil { | ||
11 | + | ||
12 | + // FTP服务器配置参数 | ||
13 | + private static final String SERVER = "193.112.177.8"; // FTP服务器地址 | ||
14 | + private static final int PORT = 21; // FTP服务器端口,默认21 | ||
15 | + private static final String USERNAME = "user1"; // FTP用户名 | ||
16 | + private static final String PASSWORD = "sz123321"; // FTP密码 | ||
17 | + | ||
18 | + // 本地文件路径及远程存储路径 | ||
19 | + private static final String LOCAL_FILE_PATH = "C:\\Users\\Lenovo\\Desktop\\sql.txt"; | ||
20 | + private static final String REMOTE_FILE_PATH = "/1/sql.txt"; | ||
21 | + | ||
22 | +// public static void upload(FtpDTO ftpDTO) { | ||
23 | +// FTPClient ftpClient = new FTPClient(); | ||
24 | +// try { | ||
25 | +// // 1. 连接FTP服务器 | ||
26 | +// System.out.println("正在连接到FTP服务器 " + ftpDTO.getFtpHost() + "..."); | ||
27 | +// ftpClient.connect(ftpDTO.getFtpHost(), PORT); | ||
28 | +// int replyCode = ftpClient.getReplyCode(); | ||
29 | +// if (!FTPReply.isPositiveCompletion(replyCode)) { | ||
30 | +// System.err.println("FTP服务器拒绝连接,响应代码:" + replyCode); | ||
31 | +// return; | ||
32 | +// } | ||
33 | +// System.out.println("连接成功,准备登录..."); | ||
34 | +// | ||
35 | +// // 2. 登录FTP服务器 | ||
36 | +// boolean success = ftpClient.login(ftpDTO.getUser(), ftpDTO.getPassword()); | ||
37 | +// if (!success) { | ||
38 | +// System.err.println("FTP登录失败,请检查用户名和密码。"); | ||
39 | +// return; | ||
40 | +// } | ||
41 | +// System.out.println("登录成功!"); | ||
42 | +// | ||
43 | +// // 3. 设置FTP客户端工作模式 | ||
44 | +// // 进入被动模式,以适应防火墙/NAT环境 | ||
45 | +// ftpClient.enterLocalPassiveMode(); | ||
46 | +// // 设置文件传输类型为二进制文件,确保上传文件内容正确 | ||
47 | +// ftpClient.setFileType(FTP.BINARY_FILE_TYPE); | ||
48 | +// | ||
49 | +// // 4. 上传文件 | ||
50 | +// System.out.println("开始上传文件:" + ftpDTO.getSourcePath()); | ||
51 | +// // 使用try-with-resources确保InputStream自动关闭 | ||
52 | +// try (InputStream inputStream = new FileInputStream(ftpDTO.getSourcePath())) { | ||
53 | +// boolean uploadSuccess = ftpClient.storeFile(ftpDTO.getTargetPath(), inputStream); | ||
54 | +// if (uploadSuccess) { | ||
55 | +// System.out.println("文件上传成功!保存到:" + ftpDTO.getTargetPath()); | ||
56 | +// } else { | ||
57 | +// System.err.println("文件上传失败,请检查文件路径和网络连接。"); | ||
58 | +// } | ||
59 | +// } | ||
60 | +// | ||
61 | +// // 5. 登出并断开FTP连接 | ||
62 | +// ftpClient.logout(); | ||
63 | +// System.out.println("已登出FTP服务器。"); | ||
64 | +// | ||
65 | +// } catch (IOException ex) { | ||
66 | +// System.err.println("发生异常:" + ex.getMessage()); | ||
67 | +// ex.printStackTrace(); | ||
68 | +// } finally { | ||
69 | +// // 6. 确保FTP连接被断开 | ||
70 | +// if (ftpClient.isConnected()) { | ||
71 | +// try { | ||
72 | +// ftpClient.disconnect(); | ||
73 | +// System.out.println("FTP连接已关闭。"); | ||
74 | +// } catch (IOException ex) { | ||
75 | +// System.err.println("关闭FTP连接时发生异常:" + ex.getMessage()); | ||
76 | +// ex.printStackTrace(); | ||
77 | +// } | ||
78 | +// } | ||
79 | +// } | ||
80 | +// } | ||
81 | + | ||
82 | +// public static List<String> listFileNames(ApiConfig apiConfig, String directoryPath) { | ||
83 | +// FTPClient ftpClient = new FTPClient(); | ||
84 | +// List<String> fileNames = new ArrayList<>(); | ||
85 | +// | ||
86 | +// try { | ||
87 | +// // 连接并登录FTP服务器 | ||
88 | +// ftpClient.connect(apiConfig.getApiUrl(), PORT); | ||
89 | +// int replyCode = ftpClient.getReplyCode(); | ||
90 | +// if (!FTPReply.isPositiveCompletion(replyCode)) { | ||
91 | +// return null; | ||
92 | +// } | ||
93 | +// | ||
94 | +// boolean success = ftpClient.login(apiConfig.getApiId(), apiConfig.getApiKey()); | ||
95 | +// if (!success) { | ||
96 | +// return null; | ||
97 | +// } | ||
98 | +// | ||
99 | +// // 进入被动模式 | ||
100 | +// ftpClient.enterLocalPassiveMode(); | ||
101 | +// | ||
102 | +// // 获取文件列表 | ||
103 | +// FTPFile[] files; | ||
104 | +// if (directoryPath != null && !directoryPath.isEmpty()) { | ||
105 | +// files = ftpClient.listFiles(directoryPath); | ||
106 | +// } else { | ||
107 | +// files = ftpClient.listFiles(); | ||
108 | +// } | ||
109 | +// | ||
110 | +// // 提取文件名称 | ||
111 | +// for (FTPFile file : files) { | ||
112 | +// fileNames.add(file.getName()); | ||
113 | +// } | ||
114 | +// | ||
115 | +// return fileNames; | ||
116 | +// | ||
117 | +// } catch (IOException ex) { | ||
118 | +// return null; | ||
119 | +// } finally { | ||
120 | +// // 确保连接被正确关闭 | ||
121 | +// if (ftpClient.isConnected()) { | ||
122 | +// try { | ||
123 | +// ftpClient.logout(); | ||
124 | +// ftpClient.disconnect(); | ||
125 | +// } catch (IOException ex) { | ||
126 | +// System.err.println("关闭FTP连接时发生异常:" + ex.getMessage()); | ||
127 | +// ex.printStackTrace(); | ||
128 | +// } | ||
129 | +// } | ||
130 | +// } | ||
131 | +// } | ||
132 | + | ||
133 | + /** | ||
134 | + * 获取FTP服务器当前目录下的文件列表 | ||
135 | + * @return FTPFile数组,包含目录中的文件和子目录信息 | ||
136 | + */ | ||
137 | +// public static FTPFile[] getlistFiles() { | ||
138 | +// return listFiles(null); | ||
139 | +// } | ||
140 | + | ||
141 | + /** | ||
142 | + * 打印指定目录下的文件列表信息 | ||
143 | + * @param directoryPath 目录路径 | ||
144 | + */ | ||
145 | +// public static void printFileList(String directoryPath) { | ||
146 | +// FTPFile[] files = listFiles(directoryPath); | ||
147 | +// | ||
148 | +// if (files.length == 0) { | ||
149 | +// System.out.println("目录为空或获取失败"); | ||
150 | +// return; | ||
151 | +// } | ||
152 | +// | ||
153 | +// System.out.println("目录 " + (directoryPath != null ? directoryPath : "当前目录") + " 中的文件列表:"); | ||
154 | +// for (FTPFile file : files) { | ||
155 | +// String fileType = file.isDirectory() ? "目录" : "文件"; | ||
156 | +// System.out.printf("%-10s %-20s %10d bytes%n", | ||
157 | +// fileType, file.getName(), file.getSize()); | ||
158 | +// } | ||
159 | +// } | ||
160 | + | ||
161 | + | ||
162 | +// public static byte[] downloadFile(ApiConfig apiConfig, String targetPath) { | ||
163 | +// FTPClient ftpClient = new FTPClient(); | ||
164 | +// try { | ||
165 | +// // 1. 连接FTP服务器 | ||
166 | +// System.out.println("正在连接到FTP服务器 " + apiConfig.getApiUrl() + "..."); | ||
167 | +// ftpClient.connect(apiConfig.getApiUrl(), PORT); | ||
168 | +// int replyCode = ftpClient.getReplyCode(); | ||
169 | +// if (!FTPReply.isPositiveCompletion(replyCode)) { | ||
170 | +// System.err.println("FTP服务器拒绝连接,响应代码:" + replyCode); | ||
171 | +// return null; | ||
172 | +// } | ||
173 | +// System.out.println("连接成功,准备登录..."); | ||
174 | +// | ||
175 | +// // 2. 登录FTP服务器 | ||
176 | +// boolean success = ftpClient.login(apiConfig.getApiId(), apiConfig.getApiKey()); | ||
177 | +// if (!success) { | ||
178 | +// System.err.println("FTP登录失败,请检查用户名和密码。"); | ||
179 | +// return null; | ||
180 | +// } | ||
181 | +// System.out.println("登录成功!"); | ||
182 | +// | ||
183 | +// // 3. 设置FTP客户端工作模式 | ||
184 | +// // 进入被动模式,以适应防火墙/NAT环境 | ||
185 | +// ftpClient.enterLocalPassiveMode(); | ||
186 | +// // 设置文件传输类型为二进制文件,确保下载文件内容正确 | ||
187 | +// ftpClient.setFileType(FTP.BINARY_FILE_TYPE); | ||
188 | +// | ||
189 | +// // 4. 下载文件 | ||
190 | +// System.out.println("开始下载文件:" + targetPath); | ||
191 | +// ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); | ||
192 | +// boolean downloadSuccess = ftpClient.retrieveFile(targetPath, outputStream); | ||
193 | +// if (downloadSuccess) { | ||
194 | +// System.out.println("文件下载成功!文件大小:" + outputStream.size() + " 字节"); | ||
195 | +// return outputStream.toByteArray(); | ||
196 | +// } else { | ||
197 | +// System.err.println("文件下载失败,请检查文件路径和网络连接。"); | ||
198 | +// return null; | ||
199 | +// } | ||
200 | +// | ||
201 | +// } catch (IOException ex) { | ||
202 | +// System.err.println("下载文件时发生异常:" + ex.getMessage()); | ||
203 | +// ex.printStackTrace(); | ||
204 | +// return null; | ||
205 | +// } finally { | ||
206 | +// // 5. 确保FTP连接被正确关闭 | ||
207 | +// if (ftpClient.isConnected()) { | ||
208 | +// try { | ||
209 | +// ftpClient.logout(); | ||
210 | +// ftpClient.disconnect(); | ||
211 | +// System.out.println("FTP连接已关闭。"); | ||
212 | +// } catch (IOException ex) { | ||
213 | +// System.err.println("关闭FTP连接时发生异常:" + ex.getMessage()); | ||
214 | +// ex.printStackTrace(); | ||
215 | +// } | ||
216 | +// } | ||
217 | +// } | ||
218 | +// } | ||
219 | +} |
src/main/java/com/aigeo/util/GoogleUtil.java
0 → 100644
1 | +package com.aigeo.util; | ||
2 | + | ||
3 | +import okhttp3.HttpUrl; | ||
4 | +import okhttp3.OkHttpClient; | ||
5 | +import okhttp3.Request; | ||
6 | +import okhttp3.Response; | ||
7 | + | ||
8 | +import java.io.IOException; | ||
9 | + | ||
10 | +public class GoogleUtil { | ||
11 | + | ||
12 | + private static final String API_KEY = ""; | ||
13 | + public static void search(){ | ||
14 | + OkHttpClient client = new OkHttpClient(); | ||
15 | + | ||
16 | + HttpUrl.Builder urlBuilder = HttpUrl.get("https://www.searchapi.io/api/v1/search").newBuilder(); | ||
17 | + urlBuilder.addQueryParameter("engine", "google"); | ||
18 | + urlBuilder.addQueryParameter("q", "chatgpt"); | ||
19 | + urlBuilder.addQueryParameter("api_key", API_KEY); | ||
20 | + | ||
21 | + Request request = new Request.Builder() | ||
22 | + .url(urlBuilder.build()) | ||
23 | + .build(); | ||
24 | + | ||
25 | + Response response = null; | ||
26 | + try { | ||
27 | + response = client.newCall(request).execute(); | ||
28 | + System.out.println(response.body().string()); | ||
29 | + } catch (IOException e) { | ||
30 | + throw new RuntimeException(e); | ||
31 | + } | ||
32 | + | ||
33 | + | ||
34 | + } | ||
35 | + | ||
36 | +} |
src/main/java/com/aigeo/util/PbootUtil.java
0 → 100644
1 | +package com.aigeo.util; | ||
2 | + | ||
3 | +import okhttp3.*; | ||
4 | +import org.apache.commons.codec.digest.DigestUtils; | ||
5 | + | ||
6 | +import java.io.IOException; | ||
7 | +import java.util.HashMap; | ||
8 | +import java.util.Map; | ||
9 | + | ||
10 | +public class PbootUtil { | ||
11 | + private static final String BASE_URL = "http://web.szcxybz.com/admin.php/content/"; | ||
12 | + private static final String API_APP_ID = "admin"; | ||
13 | + private static final String API_SECRET_KEY = "123456"; | ||
14 | + private static final OkHttpClient httpClient = new OkHttpClient(); | ||
15 | + private static final String MEDIA_TYPE_JSON = "application/json; charset=utf-8"; | ||
16 | + private static final String MEDIA_TYPE_FORM = "application/x-www-form-urlencoded; charset=utf-8"; | ||
17 | + | ||
18 | + /** | ||
19 | + * 通用API请求方法 | ||
20 | + * @param url 请求路径 | ||
21 | + * @param method 请求方法 (GET/POST) | ||
22 | + * @param params 请求参数 | ||
23 | + * @return 响应结果 | ||
24 | + */ | ||
25 | + public static String apiRequest(String url, String method, Map<String, Object> params) throws IOException { | ||
26 | + // 获取当前时间戳 | ||
27 | + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); | ||
28 | + | ||
29 | + // 生成签名 | ||
30 | + String signature = generateSignature(API_APP_ID, API_SECRET_KEY, timestamp); | ||
31 | + | ||
32 | + // 构造请求参数 | ||
33 | + Map<String, Object> requestParams = new HashMap<>(); | ||
34 | + requestParams.put("appid", API_APP_ID); | ||
35 | + requestParams.put("timestamp", timestamp); | ||
36 | + requestParams.put("signature", signature); | ||
37 | + | ||
38 | + // 添加传入的参数 | ||
39 | + if (params != null) { | ||
40 | + requestParams.putAll(params); | ||
41 | + } | ||
42 | + | ||
43 | + if ("POST".equalsIgnoreCase(method)) { | ||
44 | + return postRequest(url, requestParams); | ||
45 | + } else { | ||
46 | + return getRequest(url, requestParams); | ||
47 | + } | ||
48 | + } | ||
49 | + | ||
50 | + /** | ||
51 | + * 发送POST请求 | ||
52 | + * @param url 请求路径 | ||
53 | + * @param params 请求参数 | ||
54 | + * @return 响应结果 | ||
55 | + */ | ||
56 | + private static String postRequest(String url, Map<String, Object> params) throws IOException { | ||
57 | + // 构建表单数据而不是JSON | ||
58 | + FormBody.Builder formBuilder = new FormBody.Builder(); | ||
59 | + for (Map.Entry<String, Object> entry : params.entrySet()) { | ||
60 | + formBuilder.add(entry.getKey(), String.valueOf(entry.getValue())); | ||
61 | + } | ||
62 | + | ||
63 | + RequestBody body = formBuilder.build(); | ||
64 | + | ||
65 | + Request request = new Request.Builder() | ||
66 | + .url(BASE_URL + url) | ||
67 | + .post(body) | ||
68 | + .addHeader("Content-Type", MEDIA_TYPE_FORM) | ||
69 | + .build(); | ||
70 | + | ||
71 | + try (Response response = httpClient.newCall(request).execute()) { | ||
72 | + if (response.isSuccessful()) { | ||
73 | + return response.body().string(); | ||
74 | + } else { | ||
75 | + throw new IOException("请求失败: " + response.code() + " " + response.message()); | ||
76 | + } | ||
77 | + } | ||
78 | + } | ||
79 | + | ||
80 | + /** | ||
81 | + * 发送GET请求 | ||
82 | + * @param url 请求路径 | ||
83 | + * @param params 请求参数 | ||
84 | + * @return 响应结果 | ||
85 | + */ | ||
86 | + private static String getRequest(String url, Map<String, Object> params) throws IOException { | ||
87 | + // 构建带参数的URL | ||
88 | + String fullUrl = buildUrlWithParams(BASE_URL + url, params); | ||
89 | + | ||
90 | + Request request = new Request.Builder() | ||
91 | + .url(fullUrl) | ||
92 | + .get() | ||
93 | + .addHeader("Content-Type", MEDIA_TYPE_JSON) | ||
94 | + .build(); | ||
95 | + | ||
96 | + try (Response response = httpClient.newCall(request).execute()) { | ||
97 | + if (response.isSuccessful()) { | ||
98 | + return response.body().string(); | ||
99 | + } else { | ||
100 | + throw new IOException("请求失败: " + response.code() + " " + response.message()); | ||
101 | + } | ||
102 | + } | ||
103 | + } | ||
104 | + | ||
105 | + /** | ||
106 | + * 构建带查询参数的URL | ||
107 | + * @param baseUrl 基础URL | ||
108 | + * @param params 查询参数 | ||
109 | + * @return 完整URL | ||
110 | + */ | ||
111 | + private static String buildUrlWithParams(String baseUrl, Map<String, Object> params) { | ||
112 | + if (params == null || params.isEmpty()) { | ||
113 | + return baseUrl; | ||
114 | + } | ||
115 | + | ||
116 | + StringBuilder urlBuilder = new StringBuilder(baseUrl); | ||
117 | + urlBuilder.append("?"); | ||
118 | + | ||
119 | + boolean first = true; | ||
120 | + for (Map.Entry<String, Object> entry : params.entrySet()) { | ||
121 | + if (!first) { | ||
122 | + urlBuilder.append("&"); | ||
123 | + } | ||
124 | + urlBuilder.append(entry.getKey()) | ||
125 | + .append("=") | ||
126 | + .append(entry.getValue()); | ||
127 | + first = false; | ||
128 | + } | ||
129 | + | ||
130 | + return urlBuilder.toString(); | ||
131 | + } | ||
132 | + | ||
133 | + /** | ||
134 | + * 生成PbootCMS API签名(双层MD5加密) | ||
135 | + * @param appid 应用ID | ||
136 | + * @param secret 密钥 | ||
137 | + * @param timestamp 时间戳 | ||
138 | + * @return 签名字符串 | ||
139 | + */ | ||
140 | + private static String generateSignature(String appid, String secret, String timestamp) { | ||
141 | + // 构造签名字符串: appid + secret + timestamp | ||
142 | + String sign = appid + secret + timestamp; | ||
143 | + | ||
144 | + // 双层MD5加密 | ||
145 | + String firstMd5 = DigestUtils.md5Hex(sign); | ||
146 | + return DigestUtils.md5Hex(firstMd5); | ||
147 | + } | ||
148 | + | ||
149 | + // 使用示例 | ||
150 | + //public static void main(String[] args) { | ||
151 | + // try { | ||
152 | + // // GET请求示例 | ||
153 | + // //String getResult = apiRequest("site", "GET", null); | ||
154 | + // //System.out.println("GET结果: " + getResult); | ||
155 | + // | ||
156 | + // // POST请求示例 | ||
157 | + // Map<String, Object> postParams = new HashMap<>(); | ||
158 | + // postParams.put("contacts", "测试文章标题"); | ||
159 | + // postParams.put("name", "nimhjcws"); | ||
160 | + // postParams.put("tel", "14523687549"); | ||
161 | + // postParams.put("mobile", "12345678914"); | ||
162 | + // postParams.put("content", ";lsdjfpolasndlwiojnkldoifdfnddfd"); | ||
163 | + // | ||
164 | + // String postResult = apiRequest("add", "POST", postParams); | ||
165 | + // System.out.println("POST结果: " + postResult); | ||
166 | + // | ||
167 | + // } catch (IOException e) { | ||
168 | + // e.printStackTrace(); | ||
169 | + // } | ||
170 | + //} | ||
171 | +} |
1 | +package com.aigeo.util; | ||
2 | + | ||
3 | +import com.itextpdf.io.image.ImageData; | ||
4 | +import com.itextpdf.io.image.ImageDataFactory; | ||
5 | +import com.itextpdf.kernel.colors.ColorConstants; | ||
6 | +import com.itextpdf.kernel.font.PdfFont; | ||
7 | +import com.itextpdf.kernel.font.PdfFontFactory; | ||
8 | +import com.itextpdf.kernel.pdf.PdfDocument; | ||
9 | +import com.itextpdf.kernel.pdf.PdfWriter; | ||
10 | +import com.itextpdf.layout.Document; | ||
11 | +import com.itextpdf.layout.element.Image; | ||
12 | +import com.itextpdf.layout.element.Paragraph; | ||
13 | +import com.itextpdf.layout.properties.TextAlignment; | ||
14 | +import com.itextpdf.layout.properties.UnitValue; | ||
15 | + | ||
16 | +import java.io.File; | ||
17 | +import java.io.FileOutputStream; | ||
18 | +import java.net.URL; | ||
19 | +import java.util.regex.Matcher; | ||
20 | +import java.util.regex.Pattern; | ||
21 | + | ||
22 | +public class PdfCreateUtil { | ||
23 | + public static String create(String content) { | ||
24 | + try { | ||
25 | + long l = System.currentTimeMillis(); | ||
26 | + String fileName = "D:/LsYwxProject/YwxErpApi/yiwaixiaoerp/YwxErp/upload/aidocx/" + l + ".pdf"; | ||
27 | + createFormattedPdfWithIText(content, fileName); | ||
28 | + System.out.println("文档创建成功:自然的诗篇.pdf"); | ||
29 | + return fileName; | ||
30 | + } catch (Exception e) { | ||
31 | + System.err.println("创建文档时出错: " + e.getMessage()); | ||
32 | + e.printStackTrace(); | ||
33 | + } | ||
34 | + return "success"; | ||
35 | + } | ||
36 | + | ||
37 | + public static void createFormattedPdfWithIText(String content, String fileName) throws Exception { | ||
38 | + // 确保输出目录存在 | ||
39 | + File outputFile = new File(fileName); | ||
40 | + File parentDir = outputFile.getParentFile(); | ||
41 | + if (parentDir != null && !parentDir.exists()) { | ||
42 | + parentDir.mkdirs(); | ||
43 | + } | ||
44 | + | ||
45 | + // 创建PDF文档 | ||
46 | + PdfWriter writer = new PdfWriter(new FileOutputStream(outputFile)); | ||
47 | + PdfDocument pdf = new PdfDocument(writer); | ||
48 | + Document document = new Document(pdf); | ||
49 | + | ||
50 | + // 尝试设置中文字体 | ||
51 | + try { | ||
52 | + PdfFont chineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H"); | ||
53 | + document.setFont(chineseFont); | ||
54 | + System.out.println("中文字体设置成功"); | ||
55 | + } catch (Exception e) { | ||
56 | + System.err.println("无法设置中文字体,将使用默认字体: " + e.getMessage()); | ||
57 | + } | ||
58 | + | ||
59 | + try { | ||
60 | + // 按段落分割内容(两个换行符) | ||
61 | + String[] paragraphs = content.split("\n"); | ||
62 | + | ||
63 | + System.out.println("检测到 " + paragraphs.length + " 个段落"); | ||
64 | + | ||
65 | + // 遍历所有段落 | ||
66 | + for (int i = 0; i < paragraphs.length; i++) { | ||
67 | + String paragraph = paragraphs[i].trim(); | ||
68 | + System.out.println("处理第 " + (i+1) + " 段: " + (paragraph.length() > 50 ? paragraph.substring(0, 50) + "..." : paragraph)); | ||
69 | + | ||
70 | + if (paragraph.isEmpty()) { | ||
71 | + continue; | ||
72 | + } | ||
73 | + | ||
74 | + // 检查是否是标题 | ||
75 | + if (paragraph.startsWith("# ")) { | ||
76 | + // 处理以 # 开头的标题 | ||
77 | + String title = paragraph.substring(2).trim(); // 移除 "# " | ||
78 | + System.out.println("发现标题: " + title); | ||
79 | + //.setTextAlignment(TextAlignment.CENTER) | ||
80 | + Paragraph titlePara = new Paragraph(title) | ||
81 | + .setFontSize(18) | ||
82 | + .setBold() | ||
83 | + .setMarginBottom(20); | ||
84 | + document.add(titlePara); | ||
85 | + } | ||
86 | + else if (paragraph.startsWith("**") && paragraph.endsWith("**")) { | ||
87 | + // 处理以 ** 开头和结尾的标题 | ||
88 | + String title = paragraph.substring(2, paragraph.length() - 2).trim(); // 移除开头和结尾的 ** | ||
89 | + System.out.println("发现标题: " + title); | ||
90 | + | ||
91 | + Paragraph titlePara = new Paragraph(title) | ||
92 | + .setFontSize(18) | ||
93 | + .setBold() | ||
94 | + | ||
95 | + .setMarginBottom(20); | ||
96 | + document.add(titlePara); | ||
97 | + } | ||
98 | + // 检查是否是图片 | ||
99 | + else if (paragraph.startsWith("![")) { | ||
100 | + String imageUrl = extractImageUrl(paragraph); | ||
101 | + if (imageUrl != null) { | ||
102 | + System.out.println("发现图片,URL: " + imageUrl); | ||
103 | + try { | ||
104 | + ImageData imageData = ImageDataFactory.create(new URL(imageUrl)); | ||
105 | + Image image = new Image(imageData); | ||
106 | + image.setAutoScale(true); | ||
107 | + image.setMaxWidth(UnitValue.createPointValue(400)); | ||
108 | + image.setTextAlignment(TextAlignment.CENTER); | ||
109 | + document.add(image); | ||
110 | + } catch (Exception e) { | ||
111 | + System.err.println("无法加载图片: " + e.getMessage()); | ||
112 | + Paragraph errorPara = new Paragraph("[图片加载失败: " + imageUrl + "]") | ||
113 | + .setFontColor(ColorConstants.RED); | ||
114 | + document.add(errorPara); | ||
115 | + } | ||
116 | + } | ||
117 | + } | ||
118 | + // 普通文本段落 | ||
119 | + else { | ||
120 | + System.out.println("发现文本段落,长度: " + paragraph.length()); | ||
121 | + if (!paragraph.isEmpty()) { | ||
122 | + Paragraph textPara = new Paragraph(paragraph) | ||
123 | + .setFontSize(12) | ||
124 | + .setMarginBottom(10) | ||
125 | + .setFirstLineIndent(20); | ||
126 | + document.add(textPara); | ||
127 | + } | ||
128 | + } | ||
129 | + } | ||
130 | + | ||
131 | + System.out.println("所有内容处理完成"); | ||
132 | + } finally { | ||
133 | + document.close(); | ||
134 | + System.out.println("PDF文档已关闭"); | ||
135 | + } | ||
136 | + } | ||
137 | + | ||
138 | + | ||
139 | + private static String extractImageUrl(String markdown) { | ||
140 | + Pattern pattern = Pattern.compile("!\\[.*?\\]\\((.*?)\\)"); | ||
141 | + Matcher matcher = pattern.matcher(markdown); | ||
142 | + if (matcher.find()) { | ||
143 | + return matcher.group(1); | ||
144 | + } | ||
145 | + return null; | ||
146 | + } | ||
147 | +} | ||
148 | + |
1 | +package com.aigeo.util.article; | ||
2 | + | ||
3 | +import org.springframework.beans.factory.annotation.Autowired; | ||
4 | +import org.springframework.stereotype.Component; | ||
5 | + | ||
6 | +// 文章服务门面类 | ||
7 | +@Component | ||
8 | +public class ArticleOperationFacade { | ||
9 | + | ||
10 | + @Autowired | ||
11 | + private ArticleServiceFactory articleServiceFactory; | ||
12 | + | ||
13 | + public void publishArticle(PlatformTypeEnum platformType, Object articleData, Integer companyId) { | ||
14 | + ArticlePlatformService service = articleServiceFactory.getArticleService(platformType); | ||
15 | +// return service.publishArticle(articleData, companyId); | ||
16 | + } | ||
17 | + | ||
18 | + public void updateArticle(PlatformTypeEnum platformType, Object articleData, Integer companyId) { | ||
19 | + ArticlePlatformService service = articleServiceFactory.getArticleService(platformType); | ||
20 | +// return service.updateArticle(articleData, companyId); | ||
21 | + } | ||
22 | + | ||
23 | + public void deleteArticle(PlatformTypeEnum platformType, String articleId, Integer companyId) { | ||
24 | + ArticlePlatformService service = articleServiceFactory.getArticleService(platformType); | ||
25 | +// return service.deleteArticle(articleId, companyId); | ||
26 | + } | ||
27 | +} |
1 | +package com.aigeo.util.article; | ||
2 | + | ||
3 | + | ||
4 | +public interface ArticlePlatformService { | ||
5 | + void publishArticle(Object articleData, Integer companyId); | ||
6 | + void updateArticle(Object articleData, Integer companyId); | ||
7 | + void deleteArticle(String articleId, Integer companyId); | ||
8 | + | ||
9 | + String getPlatform(); | ||
10 | +} |
1 | +package com.aigeo.util.article; | ||
2 | + | ||
3 | +import org.springframework.beans.factory.annotation.Autowired; | ||
4 | +import org.springframework.stereotype.Component; | ||
5 | + | ||
6 | +import java.util.Map; | ||
7 | +import java.util.concurrent.ConcurrentHashMap; | ||
8 | + | ||
9 | +@Component | ||
10 | +public class ArticleServiceFactory { | ||
11 | + | ||
12 | + private Map<String, ArticlePlatformService> platformServices; | ||
13 | + | ||
14 | + private final Map<String, ArticlePlatformService> serviceMap = new ConcurrentHashMap<>(); | ||
15 | + | ||
16 | + @Autowired | ||
17 | + public void setPlatformServices(Map<String, ArticlePlatformService> platformServices) { | ||
18 | + this.platformServices = platformServices; | ||
19 | + initServiceMap(); | ||
20 | + } | ||
21 | + | ||
22 | + private void initServiceMap() { | ||
23 | + for (ArticlePlatformService service : platformServices.values()) { | ||
24 | + serviceMap.put(service.getPlatform(), service); | ||
25 | + } | ||
26 | + } | ||
27 | + | ||
28 | + public ArticlePlatformService getArticleService(PlatformTypeEnum platformType) { | ||
29 | + ArticlePlatformService service = serviceMap.get(platformType.getPlatform()); | ||
30 | + if (service == null) { | ||
31 | + throw new IllegalArgumentException("不支持的平台类型: " + platformType); | ||
32 | + } | ||
33 | + return service; | ||
34 | + } | ||
35 | + | ||
36 | + public ArticlePlatformService getArticleService(String platform) { | ||
37 | + ArticlePlatformService service = serviceMap.get(platform); | ||
38 | + if (service == null) { | ||
39 | + throw new IllegalArgumentException("不支持的平台类型: " + platform); | ||
40 | + } | ||
41 | + return service; | ||
42 | + } | ||
43 | +} |
1 | +package com.aigeo.util.article; | ||
2 | + | ||
3 | +public enum PlatformTypeEnum { | ||
4 | + SHOPIFY("shopify"), | ||
5 | + WORDPRESS("wordpress"); | ||
6 | + | ||
7 | + private final String platform; | ||
8 | + | ||
9 | + PlatformTypeEnum(String platform) { | ||
10 | + this.platform = platform; | ||
11 | + } | ||
12 | + | ||
13 | + public String getPlatform() { | ||
14 | + return platform; | ||
15 | + } | ||
16 | +} |
1 | +package com.aigeo.util.article; | ||
2 | + | ||
3 | +import com.aigeo.entity.CreateArticleDTO; | ||
4 | +import com.aigeo.entity.UpdateArticleDTO; | ||
5 | +import com.alibaba.fastjson2.JSONObject; | ||
6 | +import okhttp3.OkHttpClient; | ||
7 | +import okhttp3.Request; | ||
8 | +import okhttp3.Response; | ||
9 | +import org.apache.commons.lang3.StringUtils; | ||
10 | +import org.springframework.beans.factory.annotation.Value; | ||
11 | +import org.springframework.stereotype.Component; | ||
12 | + | ||
13 | +import javax.annotation.PostConstruct; | ||
14 | +import java.io.IOException; | ||
15 | + | ||
16 | +@Component | ||
17 | +public class Shopify implements ArticlePlatformService { | ||
18 | + private static final OkHttpClient okHttpClient = new OkHttpClient(); | ||
19 | + | ||
20 | +// @Value("${openapi.url}") | ||
21 | + private String api_url; | ||
22 | + | ||
23 | + private static String staticApiUrl; | ||
24 | + | ||
25 | + @PostConstruct | ||
26 | + public void init() { | ||
27 | + staticApiUrl = api_url; | ||
28 | + } | ||
29 | + | ||
30 | + @Override | ||
31 | + public void publishArticle(Object articleData, Integer companyId) { | ||
32 | + // 实现Shopify发布文章逻辑 | ||
33 | +// if (!(articleData instanceof CreateArticleDTO)) { | ||
34 | +// return BaseResponse.error(ReturnCodeEnum.ERROR, "参数类型错误"); | ||
35 | +// } | ||
36 | + | ||
37 | + CreateArticleDTO createArticleDTO = (CreateArticleDTO) articleData; | ||
38 | + | ||
39 | +// // 参数校验 | ||
40 | +// if (StringUtils.isBlank(createArticleDTO.getBlogId())) { | ||
41 | +// return BaseResponse.error(ReturnCodeEnum.ERROR, "博客ID不能为空"); | ||
42 | +// } | ||
43 | +// if (StringUtils.isBlank(createArticleDTO.getTitle())) { | ||
44 | +// return BaseResponse.error(ReturnCodeEnum.ERROR, "标题不能为空"); | ||
45 | +// } | ||
46 | +// if (createArticleDTO.getAuthor() == null || StringUtils.isBlank(createArticleDTO.getAuthor().getName())) { | ||
47 | +// return BaseResponse.error(ReturnCodeEnum.ERROR, "作者信息不能为空"); | ||
48 | +// } | ||
49 | +// if (StringUtils.isBlank(createArticleDTO.getBody())) { | ||
50 | +// return BaseResponse.error(ReturnCodeEnum.ERROR, "文章内容不能为空"); | ||
51 | +// } | ||
52 | + | ||
53 | + // 构建请求体 | ||
54 | + JSONObject requestBody = new JSONObject(); | ||
55 | + requestBody.put("blogId", createArticleDTO.getBlogId()); | ||
56 | + requestBody.put("title", createArticleDTO.getTitle()); | ||
57 | + | ||
58 | + // 构建author对象 | ||
59 | + JSONObject authorObject = new JSONObject(); | ||
60 | + authorObject.put("name", createArticleDTO.getAuthor().getName()); | ||
61 | + requestBody.put("author", authorObject); | ||
62 | + | ||
63 | + requestBody.put("handle", generateUniqueHandle(createArticleDTO.getTitle())); | ||
64 | + requestBody.put("body", createArticleDTO.getBody()); | ||
65 | + requestBody.put("companyId", companyId); | ||
66 | + | ||
67 | + // 构建请求 | ||
68 | + Request request = new Request.Builder() | ||
69 | + .url("http:127.0.0.1:8009" + "/shopify/createArticle") | ||
70 | + .post(okhttp3.RequestBody.create( | ||
71 | + requestBody.toJSONString(), | ||
72 | + okhttp3.MediaType.parse("application/json; charset=utf-8") | ||
73 | + )) | ||
74 | + .build(); | ||
75 | + | ||
76 | + // 发送请求 | ||
77 | + try (Response response = okHttpClient.newCall(request).execute()) { | ||
78 | + if (response.isSuccessful()) { | ||
79 | + String responseBody = response.body().string(); | ||
80 | + JSONObject jsonResponse = JSONObject.parseObject(responseBody); | ||
81 | + int code = jsonResponse.getIntValue("code"); | ||
82 | + String message = jsonResponse.getString("message"); | ||
83 | +// if (code != 200) { | ||
84 | +// return BaseResponse.error(ReturnCodeEnum.ERROR, message); | ||
85 | +// } else { | ||
86 | +// return BaseResponse.success(jsonResponse.get("data")); | ||
87 | +// } | ||
88 | + } else { | ||
89 | +// return BaseResponse.error(ReturnCodeEnum.SYSTEM_ERROR); | ||
90 | + } | ||
91 | + } catch (IOException e) { | ||
92 | + e.printStackTrace(); | ||
93 | +// return BaseResponse.error(ReturnCodeEnum.SYSTEM_ERROR); | ||
94 | + } | ||
95 | + } | ||
96 | + | ||
97 | + @Override | ||
98 | + public void updateArticle(Object articleData, Integer companyId) { | ||
99 | +// if (!(articleData instanceof UpdateArticleDTO)) { | ||
100 | +// return BaseResponse.error(ReturnCodeEnum.ERROR, "参数类型错误"); | ||
101 | +// } | ||
102 | + | ||
103 | + UpdateArticleDTO updateArticleDTO = (UpdateArticleDTO) articleData; | ||
104 | + | ||
105 | + // 参数校验 | ||
106 | +// if (StringUtils.isBlank(updateArticleDTO.getId())) { | ||
107 | +// return BaseResponse.error(ReturnCodeEnum.ERROR, "文章ID不能为空"); | ||
108 | +// } | ||
109 | +// if (StringUtils.isBlank(updateArticleDTO.getTitle())) { | ||
110 | +// return BaseResponse.error(ReturnCodeEnum.ERROR, "标题不能为空"); | ||
111 | +// } | ||
112 | +// if (StringUtils.isBlank(updateArticleDTO.getBody())) { | ||
113 | +// return BaseResponse.error(ReturnCodeEnum.ERROR, "文章内容不能为空"); | ||
114 | +// } | ||
115 | + | ||
116 | + // 构建请求体 | ||
117 | + JSONObject requestBody = new JSONObject(); | ||
118 | + requestBody.put("id", updateArticleDTO.getId()); | ||
119 | + requestBody.put("title", updateArticleDTO.getTitle()); | ||
120 | + requestBody.put("handle", updateArticleDTO.getHandle() != null ? | ||
121 | + updateArticleDTO.getHandle() : generateUniqueHandle(updateArticleDTO.getTitle())); | ||
122 | + requestBody.put("body", updateArticleDTO.getBody()); | ||
123 | + requestBody.put("companyId", companyId); | ||
124 | + | ||
125 | + // 可选字段 | ||
126 | + if (StringUtils.isNotBlank(updateArticleDTO.getSummary())) { | ||
127 | + requestBody.put("summary", updateArticleDTO.getSummary()); | ||
128 | + } | ||
129 | + | ||
130 | + if (updateArticleDTO.getTags() != null && updateArticleDTO.getTags().length > 0) { | ||
131 | + requestBody.put("tags", updateArticleDTO.getTags()); | ||
132 | + } | ||
133 | + | ||
134 | + if (StringUtils.isNotBlank(updateArticleDTO.getImageAltText())) { | ||
135 | + requestBody.put("imageAltText", updateArticleDTO.getImageAltText()); | ||
136 | + } | ||
137 | + | ||
138 | + if (StringUtils.isNotBlank(updateArticleDTO.getImageUrl())) { | ||
139 | + requestBody.put("imageUrl", updateArticleDTO.getImageUrl()); | ||
140 | + } | ||
141 | + | ||
142 | + // 构建请求 | ||
143 | + Request request = new Request.Builder() | ||
144 | + .url(staticApiUrl + "/shopify/updateArticle") | ||
145 | + .post(okhttp3.RequestBody.create( | ||
146 | + requestBody.toJSONString(), | ||
147 | + okhttp3.MediaType.parse("application/json; charset=utf-8") | ||
148 | + )) | ||
149 | + .build(); | ||
150 | + | ||
151 | + // 发送请求 | ||
152 | + try (Response response = okHttpClient.newCall(request).execute()) { | ||
153 | + if (response.isSuccessful()) { | ||
154 | + String responseBody = response.body().string(); | ||
155 | + JSONObject jsonResponse = JSONObject.parseObject(responseBody); | ||
156 | + int code = jsonResponse.getIntValue("code"); | ||
157 | + String message = jsonResponse.getString("message"); | ||
158 | +// if (code != 200) { | ||
159 | +// return BaseResponse.error(ReturnCodeEnum.ERROR, message); | ||
160 | +// } else { | ||
161 | +// return BaseResponse.success(jsonResponse.get("data")); | ||
162 | +// } | ||
163 | + } else { | ||
164 | +// return BaseResponse.error(ReturnCodeEnum.SYSTEM_ERROR); | ||
165 | + } | ||
166 | + } catch (IOException e) { | ||
167 | + e.printStackTrace(); | ||
168 | +// return BaseResponse.error(ReturnCodeEnum.SYSTEM_ERROR); | ||
169 | + } | ||
170 | + } | ||
171 | + | ||
172 | + @Override | ||
173 | + public void deleteArticle(String articleId, Integer companyId) { | ||
174 | + // 参数校验 | ||
175 | +// if (StringUtils.isBlank(articleId)) { | ||
176 | +// return BaseResponse.error(ReturnCodeEnum.ERROR, "文章ID不能为空"); | ||
177 | +// } | ||
178 | + | ||
179 | + // 构建带参数的URL | ||
180 | + String deleteUrl = staticApiUrl + "/shopify/deleteArticle?articleId=" + articleId; | ||
181 | + | ||
182 | + // 构建DELETE请求 | ||
183 | + Request request = new Request.Builder() | ||
184 | + .url(deleteUrl) | ||
185 | + .delete() | ||
186 | + .build(); | ||
187 | + | ||
188 | + // 发送请求 | ||
189 | + try (Response response = okHttpClient.newCall(request).execute()) { | ||
190 | + if (response.isSuccessful()) { | ||
191 | + String responseBody = response.body().string(); | ||
192 | + JSONObject jsonResponse = JSONObject.parseObject(responseBody); | ||
193 | + int code = jsonResponse.getIntValue("code"); | ||
194 | + String message = jsonResponse.getString("message"); | ||
195 | +// if (code != 200) { | ||
196 | +// return BaseResponse.error(ReturnCodeEnum.ERROR, message); | ||
197 | +// } else { | ||
198 | +// return BaseResponse.success(jsonResponse.get("data")); | ||
199 | +// } | ||
200 | + } else { | ||
201 | +// return BaseResponse.error(ReturnCodeEnum.SYSTEM_ERROR); | ||
202 | + } | ||
203 | + } catch (IOException e) { | ||
204 | + e.printStackTrace(); | ||
205 | +// return BaseResponse.error(ReturnCodeEnum.SYSTEM_ERROR); | ||
206 | + } | ||
207 | + } | ||
208 | + | ||
209 | + @Override | ||
210 | + public String getPlatform() { | ||
211 | + return "shopify"; | ||
212 | + } | ||
213 | + | ||
214 | + private static String generateUniqueHandle(String title) { | ||
215 | + // 创建URL友好的handle | ||
216 | + String cleanTitle = title.toLowerCase() | ||
217 | + .replaceAll("[^a-zA-Z0-9\\u4e00-\\u9fa5\\s-]", "") // 保留中文字符 | ||
218 | + .replaceAll("\\s+", "-") | ||
219 | + .replaceAll("-+", "-") | ||
220 | + .replaceAll("^-|-$", ""); | ||
221 | + | ||
222 | + // 限制长度并确保唯一性 | ||
223 | + if (cleanTitle.length() > 40) { | ||
224 | + cleanTitle = cleanTitle.substring(0, 40); | ||
225 | + } | ||
226 | + | ||
227 | + // 添加时间戳确保唯一性 | ||
228 | + String timestamp = String.valueOf(System.currentTimeMillis() % 100000); | ||
229 | + return cleanTitle + "-" + timestamp; | ||
230 | + } | ||
231 | +} |
1 | +// 修改 WordPress.java 文件,实现 deleteArticle 方法 | ||
2 | +package com.aigeo.util.article; | ||
3 | + | ||
4 | +import com.aigeo.entity.CreateArticleDTO; | ||
5 | +import com.aigeo.entity.UpdateArticleDTO; | ||
6 | +import com.alibaba.fastjson2.JSONObject; | ||
7 | +import okhttp3.*; | ||
8 | +import org.springframework.stereotype.Component; | ||
9 | + | ||
10 | +import java.io.IOException; | ||
11 | +import java.util.concurrent.TimeUnit; | ||
12 | + | ||
13 | +@Component | ||
14 | +public class WordPress implements ArticlePlatformService { | ||
15 | + | ||
16 | + private static final OkHttpClient okHttpClient = new OkHttpClient(); | ||
17 | + | ||
18 | + private static final String BASE_URL = "https://web1.szcxybz.com/wp-json/wp/v2/posts"; | ||
19 | + private static final String TOKEN_URL = "https://web1.szcxybz.com/wp-json/jwt-auth/v1/token"; | ||
20 | + | ||
21 | + private static String cachedToken = null; | ||
22 | + private static long tokenExpiryTime = 0; | ||
23 | + private static final long TOKEN_REFRESH_BUFFER = TimeUnit.MINUTES.toMillis(5);// 5分钟缓冲 | ||
24 | + | ||
25 | + private static final OkHttpClient httpClient = new OkHttpClient(); | ||
26 | + private static final String MEDIA_TYPE = "application/json; charset=utf-8"; | ||
27 | + | ||
28 | + private static final String USERNAME = "admin"; | ||
29 | + | ||
30 | + private static final String PASSWORD = "123456"; | ||
31 | + | ||
32 | + @Override | ||
33 | + public void publishArticle(Object articleData, Integer companyId) { | ||
34 | + // 获取有效的 token | ||
35 | + String token = null; | ||
36 | + try { | ||
37 | + token = getValidToken(); | ||
38 | + } catch (IOException e) { | ||
39 | + e.printStackTrace(); | ||
40 | +// return BaseResponse.error(ReturnCodeEnum.SYSTEM_ERROR); | ||
41 | + } | ||
42 | +// if (token == null) { | ||
43 | +// System.out.println("无法获取有效的认证 token"); | ||
44 | +// return BaseResponse.error(ReturnCodeEnum.SYSTEM_ERROR); | ||
45 | +// } | ||
46 | + CreateArticleDTO createArticleDTO = (CreateArticleDTO) articleData; | ||
47 | + String styledContent = createArticleDTO.getBody().replace("<style>", "<!-- Style block will be removed by WordPress -->") | ||
48 | + .replace("</style>", ""); | ||
49 | + JSONObject contentObject = new JSONObject(); | ||
50 | + contentObject.put("raw", styledContent); | ||
51 | + JSONObject jsonBody = new JSONObject(); | ||
52 | + jsonBody.put("title", createArticleDTO.getTitle()); | ||
53 | + jsonBody.put("content", contentObject); | ||
54 | + jsonBody.put("status", "publish"); | ||
55 | + //jsonBody.put("author", author); | ||
56 | + | ||
57 | + RequestBody body = RequestBody.create( | ||
58 | + jsonBody.toString(), | ||
59 | + MediaType.get(MEDIA_TYPE) | ||
60 | + ); | ||
61 | + | ||
62 | + Request request = new Request.Builder() | ||
63 | + .url(BASE_URL) | ||
64 | + .post(body) | ||
65 | + .addHeader("Authorization", "Bearer " + token) | ||
66 | + .addHeader("Content-Type", "application/json") | ||
67 | + .build(); | ||
68 | + | ||
69 | + try (Response response = okHttpClient.newCall(request).execute()) { | ||
70 | + if (response.isSuccessful()) { | ||
71 | +// System.out.println("响应内容: " + response.body().string()); | ||
72 | + String res = response.body().string(); | ||
73 | +// return BaseResponse.success(res); | ||
74 | + } else { | ||
75 | + System.out.println("发布失败: " + response.code()); | ||
76 | + System.out.println("错误信息: " + response.body().string()); | ||
77 | +// return BaseResponse.error(ReturnCodeEnum.ERROR,"发布失败"); | ||
78 | + } | ||
79 | + } catch (IOException e) { | ||
80 | + e.printStackTrace(); | ||
81 | +// return BaseResponse.error(ReturnCodeEnum.SYSTEM_ERROR); | ||
82 | + } | ||
83 | + } | ||
84 | + | ||
85 | + @Override | ||
86 | + public void updateArticle(Object articleData, Integer companyId) { | ||
87 | + // 检查参数类型 | ||
88 | +// if (!(articleData instanceof UpdateArticleDTO)) { | ||
89 | +// return BaseResponse.error(ReturnCodeEnum.ERROR, "参数类型错误"); | ||
90 | +// } | ||
91 | + UpdateArticleDTO updateArticleDTO = (UpdateArticleDTO) articleData; | ||
92 | + // 参数校验 | ||
93 | +// if (updateArticleDTO.getId() == null || updateArticleDTO.getId().isEmpty()) { | ||
94 | +// return BaseResponse.error(ReturnCodeEnum.ERROR, "文章ID不能为空"); | ||
95 | +// } | ||
96 | + // 获取有效的 token | ||
97 | + String token = null; | ||
98 | + try { | ||
99 | + token = getValidToken(); | ||
100 | + } catch (IOException e) { | ||
101 | + e.printStackTrace(); | ||
102 | +// return BaseResponse.error(ReturnCodeEnum.SYSTEM_ERROR); | ||
103 | + } | ||
104 | +// if (token == null) { | ||
105 | +// System.out.println("无法获取有效的认证 token"); | ||
106 | +// return BaseResponse.error(ReturnCodeEnum.SYSTEM_ERROR); | ||
107 | +// } | ||
108 | + // 构建更新文章的URL | ||
109 | + String updateUrl = BASE_URL + "/" + updateArticleDTO.getId(); | ||
110 | + | ||
111 | + // 构建请求体 | ||
112 | + JSONObject jsonBody = new JSONObject(); | ||
113 | + | ||
114 | + // 只添加非空字段进行更新 | ||
115 | + if (updateArticleDTO.getTitle() != null && !updateArticleDTO.getTitle().isEmpty()) { | ||
116 | + jsonBody.put("title", updateArticleDTO.getTitle()); | ||
117 | + } | ||
118 | + | ||
119 | + if (updateArticleDTO.getBody() != null && !updateArticleDTO.getBody().isEmpty()) { | ||
120 | + String styledContent = updateArticleDTO.getBody().replace("<style>", "<!-- Style block will be removed by WordPress -->") | ||
121 | + .replace("</style>", ""); | ||
122 | + JSONObject contentObject = new JSONObject(); | ||
123 | + contentObject.put("raw", styledContent); | ||
124 | + jsonBody.put("content", contentObject); | ||
125 | + } | ||
126 | + | ||
127 | + // 可选字段 | ||
128 | + if (updateArticleDTO.getSummary() != null) { | ||
129 | + jsonBody.put("excerpt", updateArticleDTO.getSummary()); | ||
130 | + } | ||
131 | + | ||
132 | + // 添加状态字段(如果需要) | ||
133 | + jsonBody.put("status", "publish"); | ||
134 | + | ||
135 | + RequestBody body = RequestBody.create( | ||
136 | + jsonBody.toString(), | ||
137 | + MediaType.get(MEDIA_TYPE) | ||
138 | + ); | ||
139 | + | ||
140 | + // 构建PUT请求 | ||
141 | + Request request = new Request.Builder() | ||
142 | + .url(updateUrl) | ||
143 | + .put(body) // 使用PUT方法更新文章 | ||
144 | + .addHeader("Authorization", "Bearer " + token) | ||
145 | + .addHeader("Content-Type", "application/json") | ||
146 | + .build(); | ||
147 | + | ||
148 | + try (Response response = okHttpClient.newCall(request).execute()) { | ||
149 | + if (response.isSuccessful()) { | ||
150 | + String res = response.body().string(); | ||
151 | +// return BaseResponse.success(res); | ||
152 | + } else { | ||
153 | + System.out.println("更新文章失败: " + response.code()); | ||
154 | + System.out.println("错误信息: " + response.body().string()); | ||
155 | +// return BaseResponse.error(ReturnCodeEnum.ERROR, "更新文章失败,状态码: " + response.code()); | ||
156 | + } | ||
157 | + } catch (IOException e) { | ||
158 | + e.printStackTrace(); | ||
159 | +// return BaseResponse.error(ReturnCodeEnum.SYSTEM_ERROR); | ||
160 | + } | ||
161 | + } | ||
162 | + | ||
163 | + @Override | ||
164 | + public void deleteArticle(String articleId, Integer companyId) { | ||
165 | + // 获取有效的 token | ||
166 | + String token = null; | ||
167 | + try { | ||
168 | + token = getValidToken(); | ||
169 | + } catch (IOException e) { | ||
170 | + e.printStackTrace(); | ||
171 | +// return BaseResponse.error(ReturnCodeEnum.SYSTEM_ERROR); | ||
172 | + } | ||
173 | + if (token == null) { | ||
174 | + System.out.println("无法获取有效的认证 token"); | ||
175 | +// return BaseResponse.error(ReturnCodeEnum.SYSTEM_ERROR); | ||
176 | + } | ||
177 | + | ||
178 | + // 构建删除文章的URL,将文章ID附加到基础URL后面 | ||
179 | + String deleteUrl = BASE_URL + "/" + articleId; | ||
180 | + | ||
181 | + // 构建DELETE请求 | ||
182 | + Request request = new Request.Builder() | ||
183 | + .url(deleteUrl) | ||
184 | + .delete() | ||
185 | + .addHeader("Authorization", "Bearer " + token) | ||
186 | + .addHeader("Content-Type", "application/json") | ||
187 | + .build(); | ||
188 | + | ||
189 | + try (Response response = okHttpClient.newCall(request).execute()) { | ||
190 | + if (response.isSuccessful()) { | ||
191 | + String res = response.body().string(); | ||
192 | +// return BaseResponse.success(res); | ||
193 | + } else { | ||
194 | + System.out.println("删除文章失败: " + response.code()); | ||
195 | + System.out.println("错误信息: " + response.body().string()); | ||
196 | +// return BaseResponse.error(ReturnCodeEnum.ERROR, "删除文章失败,状态码: " + response.code()); | ||
197 | + } | ||
198 | + } catch (IOException e) { | ||
199 | + e.printStackTrace(); | ||
200 | +// return BaseResponse.error(ReturnCodeEnum.SYSTEM_ERROR); | ||
201 | + } | ||
202 | + } | ||
203 | + | ||
204 | + @Override | ||
205 | + public String getPlatform() { | ||
206 | + return "wordpress"; | ||
207 | + } | ||
208 | + | ||
209 | + private static String getValidToken() throws IOException { | ||
210 | + // 检查是否有缓存的 token 且未过期 | ||
211 | + if (cachedToken != null && System.currentTimeMillis() < (tokenExpiryTime - TOKEN_REFRESH_BUFFER)) { | ||
212 | + return cachedToken; | ||
213 | + } | ||
214 | + System.out.println("token过期,重新获取token"); | ||
215 | + // 获取新 token | ||
216 | + String newToken = fetchNewToken(); | ||
217 | + if (newToken != null) { | ||
218 | + cachedToken = newToken; | ||
219 | + // 假设 token 有效期为 24 小时,实际应根据返回的过期时间设置 | ||
220 | + tokenExpiryTime = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(2); | ||
221 | + } | ||
222 | + | ||
223 | + return newToken; | ||
224 | + } | ||
225 | + | ||
226 | + // 获取新的 JWT token | ||
227 | + private static String fetchNewToken() throws IOException { | ||
228 | + JSONObject loginData = new JSONObject(); | ||
229 | + loginData.put("username", USERNAME); | ||
230 | + loginData.put("password", PASSWORD); | ||
231 | + | ||
232 | + RequestBody body = RequestBody.create( | ||
233 | + loginData.toString(), | ||
234 | + MediaType.get(MEDIA_TYPE) | ||
235 | + ); | ||
236 | + | ||
237 | + Request request = new Request.Builder() | ||
238 | + .url(TOKEN_URL) | ||
239 | + .post(body) | ||
240 | + .addHeader("Content-Type", "application/json") | ||
241 | + .build(); | ||
242 | + | ||
243 | + try (Response response = httpClient.newCall(request).execute()) { | ||
244 | + if (response.isSuccessful()) { | ||
245 | + String responseBody = response.body().string(); | ||
246 | + JSONObject tokenResponse = JSONObject.parseObject(responseBody); | ||
247 | + | ||
248 | + // 实际项目中应该从响应中获取真正的过期时间 | ||
249 | + // tokenResponse.getLong("expires_in") 或类似字段 | ||
250 | + | ||
251 | + return tokenResponse.getString("token"); | ||
252 | + } else { | ||
253 | + System.out.println("获取 token 失败: " + response.code()); | ||
254 | + System.out.println("错误信息: " + response.body().string()); | ||
255 | + return null; | ||
256 | + } | ||
257 | + } | ||
258 | + } | ||
259 | +} |
-
请 注册 或 登录 后发表评论