From 9e1d8308caed7592f65c975c9fa9428084aee401 Mon Sep 17 00:00:00 2001 From: xuelijun <977662702@qq.com> Date: Fri, 23 Jan 2026 17:09:48 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=BC=80=E5=A7=8B=E6=97=B6?= =?UTF-8?q?=E9=97=B42?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bocai.db | Bin 122880 -> 122880 bytes .../com/tem/bocai/entity/CompletedToday.java | 2 +- .../repository/CompletedTodayRepository.java | 1 + .../tem/bocai/schedules/CrawlerSchedule.java | 2 +- .../tem/bocai/util/CompletedTodayCrawler.java | 26 ++- .../java/com/tem/bocai/util/SQLiteUtil.java | 155 +++++++++++++++++- 6 files changed, 176 insertions(+), 10 deletions(-) diff --git a/bocai.db b/bocai.db index 2a8f08710d395a955cedbcbb9e6121312d8b0be8..e04e9a75bbf602b28ba98d23165bb901b12cbce0 100644 GIT binary patch delta 738 zcmZoTz}|3xeS);06axc;91!yZF*gv~PSi1$m159Kd&SGe$iU9g&A_vXFO+{LM>j7o zw=PdM$M(&F3j7?KyE)1jS$I}4>TGP(VB~7D=4Thz)@E$coy^Up%*Z=gpX>DGrCcJD zr*bMxUcoQKC@^_9zs%$WZUt68UKR$9$?JF`1vr7$F)%c_^0A96Dl)c&Zr0(IU=&Wy z&(F?GRVYg=O3p|u(l9bL)!gjFw{IgKFHkWv-vI{xN&G5&2Y{~F#8;oh!^yzuYAnoX zY;0s;WM*J!WNc_^WNu_`YHnm{VPkL5k6FJs#fkKskE(8BQ{`>rMfuUN&U+)Mq92}NVe5~mmtgA%AL5Q_q_Fc9+rG4E!J2PgO! cGae9_WWchOh4GI(h&?TVk$XG00pr*80Ez~mLI3~& delta 513 zcmaKnze@sP9LC?TTKD^2P3po>&!Xuv-XC|J9uXP2G_^EDTR~e>(3*;yOLA*tG)8uV z1d)zG|Ab@oM`W)BCQ=Qb<$0f%&-1PIrCMLQT1o*zC?~)IP2jfIcoxw5>ARuTktDZ} z{$L*9M-{Xkxn(TN-6&;aIcmulpfKMui-hi!a!%BhiCfcOxj2Q z?bKHxF>B7E@J>I3T)UD{8^n~XLIIjiWEqPP?V3-WE^Nav-`Ehi*Ut`S7FLIsbsNyysG?Dptl6>KLMyIXrllC diff --git a/src/main/java/com/tem/bocai/entity/CompletedToday.java b/src/main/java/com/tem/bocai/entity/CompletedToday.java index 42295a0..893120a 100644 --- a/src/main/java/com/tem/bocai/entity/CompletedToday.java +++ b/src/main/java/com/tem/bocai/entity/CompletedToday.java @@ -18,7 +18,7 @@ public class CompletedToday { private String betId; // @Column(name = "time", nullable = false) - private String time; // + private Date time; // //下注金额 @Column(name = "bet_amount", nullable = false) diff --git a/src/main/java/com/tem/bocai/repository/CompletedTodayRepository.java b/src/main/java/com/tem/bocai/repository/CompletedTodayRepository.java index 29ff172..c86e0d7 100644 --- a/src/main/java/com/tem/bocai/repository/CompletedTodayRepository.java +++ b/src/main/java/com/tem/bocai/repository/CompletedTodayRepository.java @@ -15,6 +15,7 @@ import java.util.Optional; public interface CompletedTodayRepository extends JpaRepository { // 根据时间范围查询resultAmount的总和 + //Double sumResultAmountByCreateTimeAfter(Date startTime); @Query("SELECT SUM(ct.resultAmount) FROM CompletedToday ct WHERE ct.time > :startTime") Double sumResultAmountByCreateTimeAfter(@Param("startTime") Date startTime); diff --git a/src/main/java/com/tem/bocai/schedules/CrawlerSchedule.java b/src/main/java/com/tem/bocai/schedules/CrawlerSchedule.java index 92f59a1..44b7196 100644 --- a/src/main/java/com/tem/bocai/schedules/CrawlerSchedule.java +++ b/src/main/java/com/tem/bocai/schedules/CrawlerSchedule.java @@ -111,7 +111,7 @@ public class CrawlerSchedule { // 每7秒执行一次爬取今日已经结算 - //@Scheduled(cron = "*/7 * * * * ?") + @Scheduled(cron = "*/7 * * * * ?") public void executeSettlement() { System.out.println("开始爬取今日已经结算..."); int retryCount = 0; diff --git a/src/main/java/com/tem/bocai/util/CompletedTodayCrawler.java b/src/main/java/com/tem/bocai/util/CompletedTodayCrawler.java index 7dd8ae2..a57308c 100644 --- a/src/main/java/com/tem/bocai/util/CompletedTodayCrawler.java +++ b/src/main/java/com/tem/bocai/util/CompletedTodayCrawler.java @@ -19,6 +19,9 @@ import us.codecraft.webmagic.selector.Html; import java.io.File; import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -289,14 +292,34 @@ public class CompletedTodayCrawler implements PageProcessor { /** * 转换数据结构以适应数据库 */ + /** + * 转换数据结构以适应数据库(使用Java 8日期时间API) + */ private List convertForDatabase(List> betList) { List completedTodayList = new ArrayList<>(); + // 根据你实际的日期字符串格式调整 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + for (Map bet : betList) { CompletedToday completedToday = new CompletedToday(); completedToday.setBetId((String) bet.get("bet_id")); - completedToday.setTime((String) bet.get("time")); + + // 转换String时间到Date + try { + String timeStr = (String) bet.get("time"); + // 将LocalDateTime转换为Date + LocalDateTime localDateTime = LocalDateTime.parse(timeStr, formatter); + Date timeDate = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); + completedToday.setTime(timeDate); + } catch (Exception e) { + // 处理日期解析异常,这里设置为当前时间作为默认值 + completedToday.setTime(new Date()); + // 也可以记录日志 + System.err.println("日期解析失败: " + bet.get("time")); + } + completedToday.setBetAmount((Double)bet.get("bet_amount")); completedToday.setResultAmount((Double) bet.get("result_amount")); completedToday.setResult((String) bet.get("result")); @@ -307,6 +330,7 @@ public class CompletedTodayCrawler implements PageProcessor { } + /** * 保存HTML用于调试 */ diff --git a/src/main/java/com/tem/bocai/util/SQLiteUtil.java b/src/main/java/com/tem/bocai/util/SQLiteUtil.java index c2bc6f9..28ca95f 100644 --- a/src/main/java/com/tem/bocai/util/SQLiteUtil.java +++ b/src/main/java/com/tem/bocai/util/SQLiteUtil.java @@ -191,15 +191,16 @@ public class SQLiteUtil { /** * 批量插入今日完结数据 */ - public static boolean saveCompletedToday(List list) { + /* public static boolean saveCompletedToday(List list) { if (list == null || list.isEmpty()) return false; String sql = """ INSERT OR IGNORE INTO completed_today (bet_id, time, bet_amount, result, result_amount, create_time, update_time) - VALUES (?, ?, ?, ?, ?, ?, ?) + VALUES (?, datetime(?), ?, ?, ?, ?, ?) """; - + // 使用ISO8601格式: yyyy-MM-dd HH:mm:ss + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { @@ -209,14 +210,18 @@ public class SQLiteUtil { if (data == null || data.getBetId() == null) continue; pstmt.setString(1, data.getBetId()); - pstmt.setString(2, data.getTime()); + // 核心修复:毫秒转秒+setLong传入,适配datetime(?),无setString + if (data.getTime() != null) { + long secondTs = data.getTime().getTime() / 1000; // 毫秒→秒级时间戳 + pstmt.setLong(2, secondTs); + } else { + long secondTs = System.currentTimeMillis() / 1000; // 空值兜底当前秒级时间 + pstmt.setLong(2, secondTs); + } pstmt.setDouble(3, data.getBetAmount() != null ? data.getBetAmount() : 0.0); pstmt.setString(4, data.getResult() != null ? data.getResult() : "未知"); pstmt.setDouble(5, data.getResultAmount() != null ? data.getResultAmount() : 0.0); - // 使用ISO8601格式: yyyy-MM-dd HH:mm:ss - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - // 处理create_time if (data.getCreateTime() != null) { pstmt.setString(6, sdf.format(data.getCreateTime())); @@ -242,8 +247,144 @@ public class SQLiteUtil { e.printStackTrace(); return false; } + }*/ + + /** + * 批量插入今日完结数据(终极稳定版:解决SQLite datetime()解析问题+非空约束+批量插入) + */ + public static boolean saveCompletedToday(List list) { + if (list == null || list.isEmpty()) { + System.out.println("[SQL日志] 待插入数据为空,直接返回"); + return false; + } + + // 核心:删除datetime()函数,直接插入标准时间字符串 + String sql = """ + INSERT OR REPLACE INTO completed_today + (bet_id, time, bet_amount, result, result_amount, create_time, update_time) + VALUES (?, ?, ?, ?, ?, ?, ?) + """; + // 全局统一标准时间格式(SQL兼容,yyyy-MM-dd HH:mm:ss,支持所有时间查询) + SimpleDateFormat timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + int validDataCount = 0; + Connection conn = null; + PreparedStatement pstmt = null; + + try { + conn = getConnection(); + if (conn == null) { + System.err.println("[SQL日志] 获取数据库连接失败!"); + return false; + } + pstmt = conn.prepareStatement(sql); + conn.setAutoCommit(false); // 开启事务 + + // 打印基础信息 + System.out.println("[SQL日志] 待执行SQL:" + sql.replaceAll("\\s+", " ")); + System.out.println("[SQL日志] 原始传入数据量:" + list.size()); + + for (CompletedToday data : list) { + // 过滤无效数据(含空串校验) + if (data == null || data.getBetId() == null || data.getBetId().trim().isEmpty()) { + System.out.println("[SQL日志] 过滤无效数据:data为null 或 bet_id为null/空串"); + continue; + } + validDataCount++; + + // 1. bet_id:唯一标识 + pstmt.setString(1, data.getBetId().trim()); + + // 核心修复:Java层格式化为标准时间字符串,直接传入(无datetime(),彻底解决非空) + String timeStr; + if (data.getTime() != null) { + timeStr = timeFormat.format(data.getTime()); + } else { + timeStr = timeFormat.format(new Date()); // 空值兜底当前时间 + } + pstmt.setString(2, timeStr); + + // 3. 下注金额:空值兜底0.0 + Double betAmount = data.getBetAmount() != null ? data.getBetAmount() : 0.0; + pstmt.setDouble(3, betAmount); + + // 4. 输赢结果:空值兜底"未知" + String result = data.getResult() != null ? data.getResult().trim() : "未知"; + pstmt.setString(4, result); + + // 5. 输赢金额:空值兜底0.0 + Double resultAmount = data.getResultAmount() != null ? data.getResultAmount() : 0.0; + pstmt.setDouble(5, resultAmount); + + // 6. 创建时间:空值兜底当前时间 + String createTime = data.getCreateTime() != null ? timeFormat.format(data.getCreateTime()) : timeFormat.format(new Date()); + pstmt.setString(6, createTime); + + // 7. 更新时间:空值兜底当前时间 + String updateTime = data.getUpdateTime() != null ? timeFormat.format(data.getUpdateTime()) : timeFormat.format(new Date()); + pstmt.setString(7, updateTime); + + // 打印单条参数日志(含最终存入的time字符串) + System.out.printf("[SQL日志] 批量参数%d:bet_id=%s, time=%s, bet_amount=%.2f, result=%s, result_amount=%.2f, create_time=%s, update_time=%s%n", + validDataCount, data.getBetId(), timeStr, betAmount, result, resultAmount, createTime, updateTime); + pstmt.addBatch(); + } + + // 校验有效数据量 + if (validDataCount == 0) { + System.out.println("[SQL日志] 无有效批量数据,执行回滚"); + conn.rollback(); + return false; + } + System.out.println("[SQL日志] 有效批量数据量(已加入Batch):" + validDataCount); + + // 执行批量插入并统计结果 + int[] executeResults = pstmt.executeBatch(); + int successCount = 0; + for (int res : executeResults) { + if (res == java.sql.Statement.SUCCESS_NO_INFO || res > 0) { + successCount++; + } + } + conn.commit(); // 提交事务 + System.out.println("[SQL日志] 批量插入成功:总执行条数=" + executeResults.length + ",实际插入/替换条数=" + successCount); + return successCount > 0; + + } catch (SQLException e) { + // 打印完整异常信息 + System.err.println("[SQL异常] 批量插入失败,原因:"); + System.err.println("错误码(ErrorCode):" + e.getErrorCode()); + System.err.println("异常信息:" + e.getMessage()); + e.printStackTrace(); + // 异常回滚(判断自动提交模式) + try { + if (conn != null && !conn.getAutoCommit()) { + conn.rollback(); + System.out.println("[SQL日志] 异常触发事务回滚成功"); + } + } catch (SQLException ex) { + System.err.println("[SQL异常] 事务回滚失败:" + ex.getMessage()); + ex.printStackTrace(); + } + return false; + } finally { + // 手动关闭资源,恢复连接状态 + try { + if (pstmt != null) pstmt.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + try { + if (conn != null) { + conn.setAutoCommit(true); // 恢复自动提交 + conn.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } } + /** * 关闭数据库资源 */