package com.rongyichuang.judge; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ActiveProfiles; import javax.sql.DataSource; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.Collections; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.*; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("test") public class JudgeRealFlowWithCOSTest { @LocalServerPort private int port; @Autowired private TestRestTemplate restTemplate; @Autowired private DataSource dataSource; private JdbcTemplate jdbcTemplate; private final ObjectMapper objectMapper = new ObjectMapper(); @Test void testCompleteJudgeFlowWithRealCOS() throws Exception { jdbcTemplate = new JdbcTemplate(dataSource); System.out.println("=== 完整评委创建流程(真实COS上传)==="); // 步骤1: 获取标签列表 System.out.println("\n步骤1: 获取标签列表"); List> availableTags = getAvailableTags(); assertNotNull(availableTags, "标签列表不应为空"); assertFalse(availableTags.isEmpty(), "应该有可用的标签"); System.out.println("✅ 获取到 " + availableTags.size() + " 个标签"); // 步骤2: 保存评委信息 System.out.println("\n步骤2: 保存评委信息"); String judgeId = saveJudgeInfo(availableTags); assertNotNull(judgeId, "评委ID不应为空"); System.out.println("✅ 评委保存成功,ID: " + judgeId); // 步骤3: 获取COS上传凭证 System.out.println("\n步骤3: 获取COS上传凭证"); Map uploadCredentials = getUploadCredentials(); assertNotNull(uploadCredentials, "上传凭证不应为空"); System.out.println("✅ 获取上传凭证成功"); // 步骤4: 真实上传头像到COS System.out.println("\n步骤4: 上传头像到COS"); String cosPath = performRealCOSUpload(uploadCredentials); assertNotNull(cosPath, "COS路径不应为空"); System.out.println("✅ 头像上传到COS成功,路径: " + cosPath); // 步骤5: 保存媒体记录 System.out.println("\n步骤5: 保存媒体记录"); String mediaId = saveMediaRecord(cosPath, judgeId); assertNotNull(mediaId, "媒体ID不应为空"); System.out.println("✅ 媒体记录保存成功,ID: " + mediaId); // 步骤6: 验证数据完整性 System.out.println("\n步骤6: 验证数据完整性"); verifyDataIntegrity(judgeId, mediaId, cosPath); System.out.println("✅ 数据完整性验证通过"); System.out.println("\n🎉 完整流程测试成功!文件已真实上传到腾讯云COS!"); } private List> getAvailableTags() throws Exception { String query = """ query { tagsByCategory(category: "major") { id name code } } """; Map request = Collections.singletonMap("query", query); String response = postGraphQL(request); Map responseMap = objectMapper.readValue(response, Map.class); if (responseMap.containsKey("errors")) { System.out.println("GraphQL错误: " + responseMap.get("errors")); throw new RuntimeException("GraphQL查询失败: " + responseMap.get("errors")); } Map data = (Map) responseMap.get("data"); @SuppressWarnings("unchecked") List> tags = (List>) data.get("tagsByCategory"); if (tags == null || tags.isEmpty()) { // 如果没有专业标签,获取所有标签 String allTagsQuery = """ query { tags { id name code } } """; Map allTagsRequest = Collections.singletonMap("query", allTagsQuery); String allTagsResponse = postGraphQL(allTagsRequest); Map allTagsResponseMap = objectMapper.readValue(allTagsResponse, Map.class); Map allTagsData = (Map) allTagsResponseMap.get("data"); @SuppressWarnings("unchecked") List> allTags = (List>) allTagsData.get("tags"); return allTags != null ? allTags.stream().limit(3).toList() : Collections.emptyList(); } return tags; } private String saveJudgeInfo(List> availableTags) throws Exception { // 选择前2个标签作为专业标签 List majorIds = availableTags.stream() .limit(2) .map(tag -> String.valueOf(tag.get("id"))) .toList(); String timestamp = String.valueOf(System.currentTimeMillis() % 100000); String mutation = String.format(""" mutation { saveJudge(input: { name: "王五" phone: "139%s" gender: 1 description: "高级技术专家,拥有丰富的行业经验" majorIds: [%s] }) { id name phone } } """, timestamp, String.join(", ", majorIds)); Map request = Collections.singletonMap("query", mutation); String response = postGraphQL(request); Map responseMap = objectMapper.readValue(response, Map.class); if (responseMap.containsKey("errors")) { System.out.println("GraphQL错误: " + responseMap.get("errors")); throw new RuntimeException("保存评委失败: " + responseMap.get("errors")); } @SuppressWarnings("unchecked") Map judgeData = (Map) ((Map) responseMap.get("data")).get("saveJudge"); Object idObj = judgeData.get("id"); return idObj != null ? idObj.toString() : null; } private Map getUploadCredentials() throws Exception { String query = """ query { getUploadCredentials { bucket region key presignedUrl expiration } } """; Map request = Collections.singletonMap("query", query); String response = postGraphQL(request); Map responseMap = objectMapper.readValue(response, Map.class); if (responseMap.containsKey("errors")) { System.out.println("GraphQL错误: " + responseMap.get("errors")); throw new RuntimeException("获取上传凭证失败: " + responseMap.get("errors")); } Map data = (Map) responseMap.get("data"); return (Map) data.get("getUploadCredentials"); } private String performRealCOSUpload(Map credentials) throws Exception { // 查找logo文件 File logoFile = findLogoFile(); if (logoFile == null || !logoFile.exists()) { throw new RuntimeException("找不到logo文件"); } // 获取预签名URL String presignedUrl = (String) credentials.get("presignedUrl"); String objectKey = (String) credentials.get("key"); if (presignedUrl == null || presignedUrl.isEmpty()) { throw new RuntimeException("预签名URL为空"); } // 读取文件内容 byte[] fileContent = readFileContent(logoFile); // 使用预签名URL上传 HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(presignedUrl)) .header("Content-Type", "image/jpeg") .PUT(HttpRequest.BodyPublishers.ofByteArray(fileContent)) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200 || response.statusCode() == 201 || response.statusCode() == 204) { return objectKey; } else { throw new RuntimeException("COS上传失败,状态码: " + response.statusCode() + ", 响应: " + response.body()); } } private File findLogoFile() { String[] possiblePaths = { "UI/logo.jpg", "../UI/logo.jpg", "../../UI/logo.jpg", "UI/logo.png", "../UI/logo.png", "../../UI/logo.png" }; for (String path : possiblePaths) { File file = new File(path); if (file.exists()) { return file; } } return null; } private byte[] readFileContent(File file) throws IOException { try (FileInputStream fis = new FileInputStream(file)) { return fis.readAllBytes(); } } private String saveMediaRecord(String cosPath, String judgeId) throws Exception { String mutation = String.format(""" mutation { saveMedia(input: { name: "logo.jpg" path: "%s" fileSize: 67671 fileExt: "jpg" mediaType: 1 targetType: 1 targetId: %s }) { id name path targetType targetId } } """, cosPath, judgeId); Map request = Collections.singletonMap("query", mutation); String response = postGraphQL(request); Map responseMap = objectMapper.readValue(response, Map.class); if (responseMap.containsKey("errors")) { System.out.println("GraphQL错误: " + responseMap.get("errors")); throw new RuntimeException("保存媒体记录失败: " + responseMap.get("errors")); } @SuppressWarnings("unchecked") Map mediaData = (Map) ((Map) responseMap.get("data")).get("saveMedia"); Object idObj = mediaData.get("id"); return idObj != null ? idObj.toString() : null; } private void verifyDataIntegrity(String judgeId, String mediaId, String cosPath) { // 验证评委基本信息 String judgeSql = "SELECT * FROM t_judge WHERE id = ?"; Map judge = jdbcTemplate.queryForMap(judgeSql, Long.parseLong(judgeId)); assertEquals("王五", judge.get("name")); assertTrue(((String) judge.get("phone")).startsWith("139")); assertEquals(1, judge.get("gender")); // 验证媒体记录 String mediaSql = "SELECT * FROM t_media WHERE id = ?"; Map media = jdbcTemplate.queryForMap(mediaSql, Long.parseLong(mediaId)); assertEquals("logo.jpg", media.get("name")); assertEquals(cosPath, media.get("path")); // 验证真实的COS路径 assertEquals(1, media.get("target_type")); assertEquals(Long.parseLong(judgeId), ((Number) media.get("target_id")).longValue()); // 验证标签关联 String tagSql = "SELECT COUNT(*) FROM t_judge_tag WHERE judge_id = ?"; Integer tagCount = jdbcTemplate.queryForObject(tagSql, Integer.class, Long.parseLong(judgeId)); assertTrue(tagCount >= 2, "应该至少有2个标签关联"); System.out.println("✅ 评委信息验证通过"); System.out.println("✅ 媒体记录验证通过,真实COS路径: " + cosPath); System.out.println("✅ 标签关联验证通过,关联数: " + tagCount); } private String postGraphQL(Map request) throws Exception { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity> entity = new HttpEntity<>(request, headers); ResponseEntity response = restTemplate.postForEntity( "http://localhost:" + port + "/graphql", entity, String.class); if (response.getStatusCode().is2xxSuccessful()) { return response.getBody(); } else { throw new RuntimeException("GraphQL request failed with status: " + response.getStatusCode() + " and body: " + response.getBody()); } } }