diff --git a/bocai.db b/bocai.db index 2dccf35..046189b 100644 Binary files a/bocai.db and b/bocai.db differ diff --git a/src/main/java/com/tem/bocai/repository/LotteryResultRepository.java b/src/main/java/com/tem/bocai/repository/LotteryResultRepository.java index 3a81a4e..304ca9d 100644 --- a/src/main/java/com/tem/bocai/repository/LotteryResultRepository.java +++ b/src/main/java/com/tem/bocai/repository/LotteryResultRepository.java @@ -5,9 +5,13 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; @Repository public interface LotteryResultRepository extends JpaRepository { // 根据时间查询(使用like匹配日期部分) List findByTimeContaining(String date); + + // 查询最新的记录(按time降序取第一条) + Optional findTopByOrderByTimeDesc(); } \ No newline at end of file diff --git a/src/main/java/com/tem/bocai/schedules/CrawlerSchedule.java b/src/main/java/com/tem/bocai/schedules/CrawlerSchedule.java index 9b6fcfe..1e0e79c 100644 --- a/src/main/java/com/tem/bocai/schedules/CrawlerSchedule.java +++ b/src/main/java/com/tem/bocai/schedules/CrawlerSchedule.java @@ -1,7 +1,9 @@ package com.tem.bocai.schedules; import com.tem.bocai.entity.LoginInfoResult; +import com.tem.bocai.entity.LotteryResult; import com.tem.bocai.repository.LoginInfoRepository; +import com.tem.bocai.repository.LotteryResultRepository; import com.tem.bocai.util.*; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; @@ -11,10 +13,12 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import us.codecraft.webmagic.Spider; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.util.List; +import java.util.Map; +import java.util.Optional; @Component @Slf4j @@ -24,6 +28,10 @@ public class CrawlerSchedule { private TokenCacheService tokenCacheService; @Autowired private LoginInfoRepository loginInfoRepository; + @Autowired + private LotteryResultRepository lotteryResultRepository; + + private static final int MAX_CRA = 3; private static final Integer ONOFF = 0; @Value("${pypath}") @@ -31,11 +39,11 @@ public class CrawlerSchedule { // 每天凌晨2点执行爬取开奖结果 //@Scheduled(cron = "0 0 2 * * ?") // 每7秒执行一次爬取开奖结果 - //@Scheduled(cron = "*/9 * * * * ?") + @Scheduled(cron = "*/9 * * * * ?") /*@Scheduled(cron = "0 6-59/5 7-23 * * ?") @Scheduled(cron = "0 0-55/5 0-6 * * ?")*/ // 从7:00分30秒起每5分钟执行一次 - @Scheduled(cron = "30 0/5 * * * ?") + //@Scheduled(cron = "30 0/5 * * * ?") public void executeLotteryDraw() { log.info("开始爬取开奖结果"); int retryCount = 0; @@ -45,10 +53,13 @@ public class CrawlerSchedule { if (firstByOrderByCreateTimeDesc == null) { return; } + if(firstByOrderByCreateTimeDesc.getOnOff() == ONOFF){ + return; + } String token = tokenCacheService.getToken(); - if (token == null || token.isEmpty()) { - return; - } + if (token == null || token.isEmpty()) { + return; + } while (!success && retryCount < MAX_CRA) { log.info("\n=== 第 " + (retryCount + 1) + " 次尝试获取开奖结果 ==="); if (token == null || token.isEmpty()) { @@ -99,6 +110,31 @@ public class CrawlerSchedule { } + //@Scheduled(cron = "50 0/5 * * * ?") + public void executePksHistory() { + log.info("开始获取历史开奖结果"); + // 获取API数据 + List> historyList = HttpClientExample.getPksHistoryList(); + // 获取数据库最新时间 + Optional latestTimeOpt = lotteryResultRepository.findTopByOrderByTimeDesc() + .map(LotteryResult::getTime); + + // 判断是否需要更新 + boolean needUpdate = latestTimeOpt + .map(latestTime -> historyList.stream() + .noneMatch(item -> latestTime.equals(item.get("time")))) + .orElse(true); // 数据库为空时需要更新 + + if (needUpdate) { + log.info("需要更新数据,最新时间:{}", + latestTimeOpt.orElse("数据库为空")); + HttpClientExample.getPksHistory(historyList, pypath); + } else { + log.info("数据已是最新,无需更新"); + } + } + + /*public void executeLotteryDraw() { System.out.println("开始爬取开奖结果..."); String token = tokenCacheService.getToken(); @@ -119,7 +155,7 @@ public class CrawlerSchedule { // 从7:00分30秒起每5分钟执行一次爬取今日已经结算 - @Scheduled(cron = "35 0/5 * * * ?") + //@Scheduled(cron = "35 0/5 * * * ?") public void executeSettlement() { log.info("开始爬取今日已经结算..."); int retryCount = 0; @@ -128,6 +164,11 @@ public class CrawlerSchedule { if (token == null || token.isEmpty()) { return; } + LoginInfoResult firstByOrderByCreateTimeDesc = loginInfoRepository.findFirstByOrderByCreateTimeDesc() + .orElse(null); + if(firstByOrderByCreateTimeDesc.getOnOff() == ONOFF){ + return; + } while (!success && retryCount < MAX_CRA) { log.info("\n=== 第 " + (retryCount + 1) + " 次尝试获取今日注单 ==="); if (token == null || token.isEmpty()) { @@ -141,8 +182,7 @@ public class CrawlerSchedule { // 创建爬虫实例,传入token CompletedTodayCrawler crawler = new CompletedTodayCrawler(token); - LoginInfoResult firstByOrderByCreateTimeDesc = loginInfoRepository.findFirstByOrderByCreateTimeDesc() - .orElse(null); + // 执行爬虫 String url = firstByOrderByCreateTimeDesc.getLoginUrl()+"/member/bets?settled=true"; @@ -195,6 +235,6 @@ public class CrawlerSchedule { .run(); } }*/ - + } diff --git a/src/main/java/com/tem/bocai/util/HttpClientExample.java b/src/main/java/com/tem/bocai/util/HttpClientExample.java new file mode 100644 index 0000000..60fa59a --- /dev/null +++ b/src/main/java/com/tem/bocai/util/HttpClientExample.java @@ -0,0 +1,232 @@ +package com.tem.bocai.util; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import lombok.extern.slf4j.Slf4j; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; +import org.apache.hc.client5.http.ssl.NoopHostnameVerifier; +import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.ParseException; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.ssl.SSLContexts; +import org.apache.hc.core5.ssl.TrustStrategy; +import org.apache.hc.core5.util.Timeout; +import org.springframework.beans.factory.annotation.Value; + +import javax.net.ssl.SSLContext; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URL; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.text.SimpleDateFormat; +import java.util.*; + + +@Slf4j +public class HttpClientExample { + + public static String getPksHistory(List> resultList ,String pypath) { + // 将数据写入SQLite数据库 + SQLiteUtil.writeToSQLite(resultList); + // 将数据写入JSON文件(保留原有功能) + writeToJsonFile(resultList,pypath); + log.info("历史爬虫打印结果===" + resultList); + return ""; + } + + public static List> getPksHistoryList(){ + List> resultList = new ArrayList<>(); + String todayDate = DateUtils.getTodayDate(); + try { + String url = "https://api.api168168.com/pks/getPksHistoryList.do?lotCode=10058&"+todayDate; + + // 设置代理 127.0.0.1:7890 + Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 7890)); + + HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(proxy); + conn.setRequestMethod("GET"); + conn.setRequestProperty("Origin", "https://xy678kjw.com"); + conn.setRequestProperty("Referer", "https://xy678kjw.com/"); + conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36"); + conn.setConnectTimeout(10000); + conn.setReadTimeout(10000); + + BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8")); + StringBuilder response = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + response.append(line); + } + reader.close(); + + // 使用Jackson解析JSON + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(response.toString()); + + // 检查是否成功 + int errorCode = rootNode.get("errorCode").asInt(); + if (errorCode == 0) { + // 获取data数组 + JsonNode dataArray = rootNode + .get("result") + .get("data"); + + if (dataArray.isArray()) { + for (JsonNode item : dataArray) { + Map map = new HashMap<>(); + + // 解析原始数据 + String preDrawCode = item.get("preDrawCode").asText(); + String[] codeArray = preDrawCode.split(","); + + // 转换为整数数组 + int[] result = new int[codeArray.length]; + for (int i = 0; i < codeArray.length; i++) { + result[i] = Integer.parseInt(codeArray[i].trim()); + } + + // 计算sum1和sum2 + int sum1 = 0; + int sum2 = 0; + for (int i = 0; i < 5; i++) { + sum1 += result[i]; + } + for (int i = 5; i < 10; i++) { + sum2 += result[i]; + } + + // 计算winner(前五个号码的和) + int winner = sum1; + + // 计算冠亚和(前两个号码的和) + int championSum = result[0] + result[1]; + + // 判断冠亚单双 + String GD2 = (championSum % 2 == 0) ? "冠亚双" : "冠亚单"; + + // 判断冠亚大小(冠亚和11为大,11为小) + String GD1 = (championSum > 11) ? "冠亚大" : "冠亚小"; + if (championSum == 11) { + GD1 = "冠亚小"; // 特殊处理:和值为11算小 + } + + // 计算龙虎(1-5位对比) + List GLH_result = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + if (result[i] > result[9 - i]) { + GLH_result.add("龙"); + } else { + GLH_result.add("虎"); + } + } + + // 构建转换后的map + map.put("result", result); + map.put("sum1", sum1); + map.put("sum2", sum2); + map.put("winner", winner); + map.put("GD2", GD2); + map.put("GD1", GD1); + map.put("GLH_result", GLH_result); + map.put("id", String.valueOf(item.get("preDrawIssue").asLong())); + map.put("time", item.get("preDrawTime").asText()); + + resultList.add(map); + } + } + } + + // 打印转换后的结果列表 + System.out.println("Total records: " + resultList.size()); + + // 使用ObjectMapper将结果列表转换为JSON格式输出 + ObjectMapper outputMapper = new ObjectMapper(); + String jsonOutput = outputMapper.writerWithDefaultPrettyPrinter().writeValueAsString(resultList); + System.out.println(jsonOutput); + + // 或者以自定义格式输出(可选) + System.out.println("\n=== 转换后的数据格式 ==="); + for (Map map : resultList) { + System.out.println("{"); + System.out.println(" \"result\" : " + Arrays.toString((int[]) map.get("result")) + ","); + System.out.println(" \"sum1\" : " + map.get("sum1") + ","); + System.out.println(" \"sum2\" : " + map.get("sum2") + ","); + System.out.println(" \"winner\" : " + map.get("winner") + ","); + System.out.println(" \"GD2\" : \"" + map.get("GD2") + "\","); + System.out.println(" \"GD1\" : \"" + map.get("GD1") + "\","); + System.out.println(" \"GLH_result\" : " + map.get("GLH_result") + ","); + System.out.println(" \"id\" : \"" + map.get("id") + "\","); + System.out.println(" \"time\" : \"" + map.get("time") + "\""); + System.out.println("},"); + } + + } catch (Exception e) { + e.printStackTrace(); + } + return resultList; + } + + + public static void writeToJsonFile(List> resultList,String pypath) { + try { + // 创建 ObjectMapper 实例 + ObjectMapper objectMapper = new ObjectMapper(); + + // 设置 JSON 格式化(可选,更易读) + objectMapper.enable(SerializationFeature.INDENT_OUTPUT); + + // 定义输出目录 + String directoryPath = pypath+"/current_data"; // 项目根目录下的 output/json 文件夹 + + // 使用年月日作为文件名(格式:result_yyyyMMdd.json) + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + String dateStr = dateFormat.format(new Date()); + String fileName = "result_" + dateStr + ".json"; + String filePath = directoryPath + "/" + fileName; + + // 创建目录(如果不存在) + File directory = new File(directoryPath); + if (!directory.exists()) { + directory.mkdirs(); // 创建多级目录 + } + + // 创建文件对象 + File outputFile = new File(filePath); + + // 如果文件已存在,删除旧文件(实现替换功能) + if (outputFile.exists()) { + boolean deleted = outputFile.delete(); + if (!deleted) { + throw new IOException("无法删除已存在的文件: " + filePath); + } + System.out.println("已删除旧文件,准备创建新文件: " + fileName); + } + + // 将 List 写入 JSON 文件 + objectMapper.writeValue(outputFile, resultList); + log.info("数据已成功写入文件: " + outputFile.getAbsolutePath()); + } catch (IOException e) { + e.printStackTrace(); + log.error("写入 JSON 文件失败: " + e.getMessage(), e); + throw new RuntimeException("写入 JSON 文件失败: " + e.getMessage(), e); + } + } + +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 43c9b10..b3cfcf5 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -10,4 +10,4 @@ spring.jpa.show-sql=true spring.jpa.database-platform=org.hibernate.community.dialect.SQLiteDialect spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true -pypath:d:/py/PyModel \ No newline at end of file +pypath:c:/py/PyModel \ No newline at end of file