diff --git a/src/main/java/com/tem/bocai/controller/LoginCrawler.java b/src/main/java/com/tem/bocai/controller/LoginCrawler.java index 2455772..0451bc7 100644 --- a/src/main/java/com/tem/bocai/controller/LoginCrawler.java +++ b/src/main/java/com/tem/bocai/controller/LoginCrawler.java @@ -14,7 +14,7 @@ public class LoginCrawler { private final LoginService loginService; - // 构造函数注入 + // 登入 public LoginCrawler(LoginService loginService) { this.loginService = loginService; } @@ -24,6 +24,7 @@ public class LoginCrawler { return ResponseEntity.ok(result); } + //今日已结爬取 @GetMapping("/ocr/completedToday") public ResponseEntity completedToday() throws IOException, TesseractException { String result = loginService.completedToday(); diff --git a/src/main/java/com/tem/bocai/entity/LoginInfoResult.java b/src/main/java/com/tem/bocai/entity/LoginInfoResult.java new file mode 100644 index 0000000..dfffc66 --- /dev/null +++ b/src/main/java/com/tem/bocai/entity/LoginInfoResult.java @@ -0,0 +1,47 @@ +package com.tem.bocai.entity; + +import jakarta.persistence.*; +import lombok.Data; +import net.sourceforge.tess4j.TesseractException; +import org.springframework.http.ResponseEntity; + +import java.io.IOException; +import java.util.Date; +import java.util.List; + +@Entity //用户登入信息 +@Table(name = "login_info") +@Data +public class LoginInfoResult { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "user_name", nullable = false) + private String username; // + + @Column(name = "password", nullable = false) + private String password; // + + + @Column(name = "login_url", nullable = false) + private String loginUrl; + + //限制赢多少 + @Column(name = "win_num", nullable = false) + private Integer winNum; + + //限制输多少 + @Column(name = "lose_num", nullable = false) + private Integer loseNum; + +/* @Column(name = "current_num", nullable = false) + private Integer currentNum;*/ + + @Column(name = "create_time", nullable = false) + private Date createTime; + + @Column(name = "update_time", nullable = false) + private Date updateTime; + } \ No newline at end of file diff --git a/src/main/java/com/tem/bocai/service/LoginService.java b/src/main/java/com/tem/bocai/service/LoginService.java index 50758d3..47aa515 100644 --- a/src/main/java/com/tem/bocai/service/LoginService.java +++ b/src/main/java/com/tem/bocai/service/LoginService.java @@ -5,9 +5,6 @@ public interface LoginService { String loginAutomatic(String username, String password,String loginUrl,Integer winNum,Integer loseNum); - //获取token - String getToken(String username, String password, String loginUrl); - //获取token String completedToday(); diff --git a/src/main/java/com/tem/bocai/service/impl/LoginServiceImpl.java b/src/main/java/com/tem/bocai/service/impl/LoginServiceImpl.java index 016b7de..921f803 100644 --- a/src/main/java/com/tem/bocai/service/impl/LoginServiceImpl.java +++ b/src/main/java/com/tem/bocai/service/impl/LoginServiceImpl.java @@ -1,10 +1,8 @@ package com.tem.bocai.service.impl; +import com.tem.bocai.entity.LoginInfoResult; import com.tem.bocai.service.LoginService; -import com.tem.bocai.util.CompletedTodayCrawler; -import com.tem.bocai.util.LotteryDataPipeline; -import com.tem.bocai.util.LotteryWebMagicCrawler; -import com.tem.bocai.util.TokenCacheService; +import com.tem.bocai.util.*; import org.springframework.stereotype.Service; import net.sourceforge.tess4j.Tesseract; import net.sourceforge.tess4j.TesseractException; @@ -24,6 +22,7 @@ import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.*; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Map; @@ -37,7 +36,7 @@ import us.codecraft.webmagic.Spider; @Service public class LoginServiceImpl implements LoginService { private static final String BASE_URL = "https://4701268539-esh.qdk63ayw8g.com"; - private static final int MAX_RETRY = 5; + private static final int MAX_RETRY = 10; @Autowired private Tesseract tesseract; @Autowired @@ -49,9 +48,11 @@ public class LoginServiceImpl implements LoginService { for (int attempt = 1; attempt <= MAX_RETRY; attempt++) { System.out.println("\n=== 第 " + attempt + " 次尝试 ==="); try { - token = attemptLogin(); + token = tokenCacheService.attemptLogin(); tokenCacheService.saveToken(token); if (token != null && !token.isEmpty()) { + //保存用户信息 + addLoginInfo(username, password, loginUrl, winNum, loseNum); // 2. 创建爬虫实例,传入token LotteryWebMagicCrawler crawler = new LotteryWebMagicCrawler(token); @@ -75,7 +76,7 @@ public class LoginServiceImpl implements LoginService { } if (attempt < MAX_RETRY) { - waitForRetry(attempt); + tokenCacheService.waitForRetry(attempt); } } catch (Exception e) { System.err.println("第 " + attempt + " 次尝试失败: " + e.getMessage()); @@ -85,29 +86,6 @@ public class LoginServiceImpl implements LoginService { return ""; } - - @Override - public String getToken(String username, String password, String loginUrl) { - String token = ""; - for (int attempt = 1; attempt <= MAX_RETRY; attempt++) { - System.out.println("\n=== 第 " + attempt + " 次尝试 ==="); - try { - token = attemptLogin(); - - if (token != null && !token.isEmpty()) { - return token; - } - if (attempt < MAX_RETRY) { - waitForRetry(attempt); - } - } catch (Exception e) { - System.err.println("第 " + attempt + " 次尝试失败: " + e.getMessage()); - e.printStackTrace(); - } - } - return ""; - } - @Override public String completedToday() { String token = tokenCacheService.getToken(); @@ -127,309 +105,23 @@ public class LoginServiceImpl implements LoginService { return ""; } - /** - * 单次登录尝试 - */ - private String attemptLogin() throws IOException, TesseractException, InterruptedException { - CookieStore cookieStore = new BasicCookieStore(); - try (CloseableHttpClient httpClient = createHttpClient(cookieStore)) { - // 1. 获取验证码 - byte[] imageData = fetchCaptcha(httpClient); - if (imageData == null) { - return null; - } - // 2. OCR识别验证码 - String code = processCaptcha(imageData); - if (code == null || code.length() != 4) { - return null; - } - // 3. 执行登录 - return performLogin(httpClient, cookieStore, code); - } catch (Exception e) { - throw new IOException("登录尝试失败", e); - } + /** + * 添加登录信息 + */ + public boolean addLoginInfo(String username, String password, + String loginUrl, Integer winNum, Integer loseNum) { + LoginInfoResult loginInfo = new LoginInfoResult(); + loginInfo.setUsername(username); + loginInfo.setPassword(password); + loginInfo.setLoginUrl(loginUrl); + loginInfo.setWinNum(winNum != null ? winNum : 0); + loginInfo.setLoseNum(loseNum != null ? loseNum : 0); + loginInfo.setCreateTime(new Date()); + loginInfo.setUpdateTime(new Date()); + + return SQLiteUtil.addOrUpdateLoginInfo(loginInfo); } - /** - * 创建HttpClient - */ - private CloseableHttpClient createHttpClient(CookieStore cookieStore) { - return HttpClients.custom() - .setDefaultCookieStore(cookieStore) - .build(); - } - - /** - * 获取验证码图片 - */ - private byte[] fetchCaptcha(CloseableHttpClient httpClient) - throws IOException, InterruptedException { - - System.out.println("获取验证码..."); - - // 添加随机延迟 - Thread.sleep(1000 + (long) (Math.random() * 1000)); - - HttpGet getCaptcha = new HttpGet(BASE_URL + "/code"); - setCommonHeaders(getCaptcha); - getCaptcha.setHeader("Referer", BASE_URL + "/login"); - - try (CloseableHttpResponse captchaResponse = httpClient.execute(getCaptcha)) { - int captchaStatus = captchaResponse.getStatusLine().getStatusCode(); - System.out.println("验证码响应状态码: " + captchaStatus); - - if (captchaStatus == 200) { - return EntityUtils.toByteArray(captchaResponse.getEntity()); - } else if (captchaStatus == 429) { - System.out.println("获取验证码被限速,等待后重试..."); - Thread.sleep(3000); - } else { - System.out.println("获取验证码失败: " + captchaStatus); - } - } - - return null; - } - - /** - * 处理验证码识别 - */ - private String processCaptcha(byte[] imageData) - throws IOException, TesseractException { - - BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageData)); - String rawOcr = tesseract.doOCR(image); - - // 清理验证码 - String code = rawOcr.replaceAll("\\s+", "").trim(); - code = code.replaceAll("[^0-9]", ""); // 只保留数字 - System.out.println("OCR原始结果: " + rawOcr); - System.out.println("清理后验证码: [" + code + "] 长度: " + code.length()); - // 保存图片用于调试 - //saveCaptchaImage(image); - - return code; - } - - /** - * 保存验证码图片 - */ -/* private void saveCaptchaImage(BufferedImage image) throws IOException { - String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); - File output = new File("captcha_" + timestamp + ".png"); - ImageIO.write(image, "png", output); - System.out.println("验证码图片已保存到: " + output.getAbsolutePath()); - }*/ - - /** - * 执行登录请求 - */ - private String performLogin(CloseableHttpClient httpClient, - CookieStore cookieStore, - String code) throws IOException, InterruptedException { - - System.out.println("执行登录..."); - // 等待一下再发送登录请求 - Thread.sleep(1500 + (long) (Math.random() * 1000)); - - HttpPost loginPost = createLoginRequest(code); - - try (CloseableHttpResponse loginResponse = httpClient.execute(loginPost)) { - return processLoginResponse(loginResponse, cookieStore); - } - } - - /** - * 创建登录请求 - */ - private HttpPost createLoginRequest(String code) throws UnsupportedEncodingException { - HttpPost loginPost = new HttpPost(BASE_URL + "/login"); - - // 设置请求头 - setCommonHeaders(loginPost); - loginPost.setHeader("Referer", BASE_URL + "/login"); - loginPost.setHeader("Origin", BASE_URL); - loginPost.setHeader("Accept", "application/json, text/plain, */*"); - - // 构建登录参数 - List params = new ArrayList<>(); - params.add(new BasicNameValuePair("type", "1")); - params.add(new BasicNameValuePair("account", "pmk1")); - params.add(new BasicNameValuePair("password", "Asd123123")); - params.add(new BasicNameValuePair("code", code)); - - loginPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8")); - - // 禁用自动重定向 - RequestConfig requestConfig = RequestConfig.custom() - .setRedirectsEnabled(false) - .build(); - loginPost.setConfig(requestConfig); - - return loginPost; - } - - /** - * 处理登录响应 - */ - private String processLoginResponse(CloseableHttpResponse loginResponse, - CookieStore cookieStore) throws IOException, InterruptedException { - - int statusCode = loginResponse.getStatusLine().getStatusCode(); - System.out.println("登录响应状态码: " + statusCode); - - // 处理限速 - if (statusCode == 429) { - handleRateLimit(loginResponse); - return null; - } - - // 打印响应头 - printResponseHeaders(loginResponse); - - // 检查重定向 - if (statusCode == 302) { - if (checkRedirectForError(loginResponse)) { - return null; - } - } - - // 读取响应体 - String tokenFromBody = extractTokenFromResponseBody(loginResponse); - if (tokenFromBody != null) { - return tokenFromBody; - } - - // 从cookies中提取token - return extractTokenFromCookies(cookieStore, statusCode); - } - - /** - * 处理速率限制 - */ - private void handleRateLimit(CloseableHttpResponse response) throws InterruptedException { - System.out.println("登录请求被限速 (429 Too Many Requests)"); - - Header retryAfterHeader = response.getFirstHeader("Retry-After"); - if (retryAfterHeader != null) { - try { - int retryAfterSeconds = Integer.parseInt(retryAfterHeader.getValue()); - System.out.println("服务器要求等待 " + retryAfterSeconds + " 秒"); - Thread.sleep(retryAfterSeconds * 1000L); - } catch (NumberFormatException e) { - System.out.println("等待5秒后重试"); - Thread.sleep(5000); - } - } else { - System.out.println("等待3秒后重试"); - Thread.sleep(3000); - } - } - - /** - * 检查重定向是否包含错误 - */ - private boolean checkRedirectForError(CloseableHttpResponse response) { - Header locationHeader = response.getFirstHeader("Location"); - if (locationHeader != null) { - String location = locationHeader.getValue(); - System.out.println("重定向到: " + location); - - if (location.contains("e=3")) { - System.out.println("验证码错误 (e=3)"); - return true; - } - } - return false; - } - - /** - * 打印响应头 - */ - private void printResponseHeaders(CloseableHttpResponse response) { - System.out.println("响应头:"); - for (Header header : response.getAllHeaders()) { - System.out.println(" " + header.getName() + ": " + header.getValue()); - } - } - - /** - * 从响应体中提取token - */ - private String extractTokenFromResponseBody(CloseableHttpResponse response) throws IOException { - if (response.getEntity() != null) { - String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8"); - if (responseBody != null && !responseBody.isEmpty()) { - System.out.println("响应体: " + responseBody); - - // 检查响应体中是否有token(JSON格式) - if (responseBody.contains("\"token\"")) { - // 简单提取token - int start = responseBody.indexOf("\"token\":\""); - if (start != -1) { - start += 9; - int end = responseBody.indexOf("\"", start); - if (end != -1) { - String token = responseBody.substring(start, end); - System.out.println("\n[SUCCESS] 从响应体找到Token!"); - System.out.println("Token: " + token); - return token; - } - } - } - } - // 消耗实体 - EntityUtils.consume(response.getEntity()); - } - return null; - } - - /** - * 从cookies中提取token - */ - private String extractTokenFromCookies(CookieStore cookieStore, int statusCode) { - List cookies = cookieStore.getCookies(); - System.out.println("所有cookies (" + cookies.size() + "个):"); - - String token = null; - for (Cookie cookie : cookies) { - System.out.println(" " + cookie.getName() + " = " + cookie.getValue()); - - if ("token".equals(cookie.getName()) || - cookie.getName().toLowerCase().contains("token")) { - token = cookie.getValue(); - } - } - - if (token != null && !token.isEmpty()) { - System.out.println("\n[SUCCESS] Login OK!"); - System.out.println("Token: " + token); - return token; - } else if (statusCode == 200) { - System.out.println("登录返回200但没有找到token,可能需要检查其他认证方式"); - } - - return null; - } - - /** - * 设置通用请求头 - */ - private void setCommonHeaders(HttpRequestBase request) { - request.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"); - request.setHeader("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8"); - request.setHeader("Accept-Encoding", "gzip, deflate, br"); - request.setHeader("Connection", "keep-alive"); - request.setHeader("Upgrade-Insecure-Requests", "1"); - } - - /** - * 等待重试 - */ - private void waitForRetry(int attempt) throws InterruptedException { - System.out.println("\n等待2秒后进行下一次尝试..."); - Thread.sleep(2000); - } } diff --git a/src/main/java/com/tem/bocai/util/SQLiteUtil.java b/src/main/java/com/tem/bocai/util/SQLiteUtil.java index 6968fef..1dca1d5 100644 --- a/src/main/java/com/tem/bocai/util/SQLiteUtil.java +++ b/src/main/java/com/tem/bocai/util/SQLiteUtil.java @@ -1,5 +1,7 @@ package com.tem.bocai.util; +import com.tem.bocai.entity.LoginInfoResult; + import java.sql.*; import java.util.ArrayList; import java.util.List; @@ -24,121 +26,6 @@ public class SQLiteUtil { return DriverManager.getConnection(DB_URL); } - /** - * 初始化数据库表结构 - */ - /*public static void initDatabase() { - Connection conn = null; - Statement stmt = null; - - try { - conn = getConnection(); - stmt = conn.createStatement(); - - // 开启事务 - conn.setAutoCommit(false); - *//* // 删除旧表 - stmt.execute("DROP TABLE IF EXISTS lottery_results"); - System.out.println("已删除旧表");*//* - // 检查表是否存在 - boolean tableExists = false; - try (ResultSet rs = conn.getMetaData().getTables(null, null, "lottery_results", null)) { - tableExists = rs.next(); - } - - if (tableExists) { - System.out.println("表已存在,检查列结构..."); - // checkAndUpdateTableStructure(conn); - } else { - System.out.println("表不存在,创建新表..."); - createTable(conn); - } - - // 提交事务 - conn.commit(); - System.out.println("数据库表初始化/更新成功"); - - } catch (SQLException e) { - try { - if (conn != null) conn.rollback(); - } catch (SQLException ex) { - ex.printStackTrace(); - } - e.printStackTrace(); - System.err.println("数据库表初始化失败: " + e.getMessage()); - } finally { - closeResources(null, stmt, conn); - } - }*/ - - /** - * 创建新表 - */ - private static void createTable(Connection conn) throws SQLException { - String createTableSQL = """ - CREATE TABLE lottery_results ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - time TEXT, - result TEXT, - winner INTEGER, - gd1 TEXT, - gd2 TEXT, - sum1 INTEGER, - sum2 INTEGER, - glh_result TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) - """; - - try (Statement stmt = conn.createStatement()) { - stmt.execute(createTableSQL); - System.out.println("表创建成功"); - } - } - - /** - * 检查并更新表结构 - */ - private static void checkAndUpdateTableStructure(Connection conn) throws SQLException { - // 定义需要的列和类型 - String[][] requiredColumns = { - {"result", "TEXT"}, - {"winner", "INTEGER"}, - {"gd1", "TEXT"}, - {"gd2", "TEXT"}, - {"sum1", "INTEGER"}, - {"sum2", "INTEGER"}, - {"glh_result", "TEXT"}, - {"created_at", "TIMESTAMP"}, - {"updated_at", "TIMESTAMP"} - }; - - // 获取现有列 - List existingColumns = new ArrayList<>(); - try (ResultSet rs = conn.getMetaData().getColumns(null, null, "lottery_results", null)) { - while (rs.next()) { - existingColumns.add(rs.getString("COLUMN_NAME").toLowerCase()); - } - } - - // 添加缺失的列 - try (Statement stmt = conn.createStatement()) { - for (String[] column : requiredColumns) { - String columnName = column[0]; - String columnType = column[1]; - - if (!existingColumns.contains(columnName.toLowerCase())) { - String alterSQL = String.format( - "ALTER TABLE lottery_results ADD COLUMN %s %s", - columnName, columnType - ); - stmt.execute(alterSQL); - System.out.println("已添加列: " + columnName); - } - } - } - } /** * 将爬虫数据写入SQLite数据库 @@ -153,10 +40,10 @@ public class SQLiteUtil { //initDatabase(); String insertSQL = """ - INSERT OR REPLACE INTO lottery_results - (issue,time, result, winner, gd1, gd2, sum1, sum2, glh_result) - VALUES (?,?, ?, ?, ?, ?, ?, ?, ?) - """; + INSERT OR REPLACE INTO lottery_results + (issue,time, result, winner, gd1, gd2, sum1, sum2, glh_result) + VALUES (?,?, ?, ?, ?, ?, ?, ?, ?) + """; Connection conn = null; PreparedStatement pstmt = null; @@ -356,34 +243,144 @@ public class SQLiteUtil { } } + /** - * 查看表结构 + * 添加/更新登录信息 - 如果用户名存在则先删除再插入(更新) */ - public static void showTableStructure() { - String sql = "PRAGMA table_info(lottery_results)"; + public static boolean addOrUpdateLoginInfo(LoginInfoResult loginInfo) { + if (loginInfo == null || loginInfo.getUsername() == null) { + return false; + } - try (Connection conn = getConnection(); - Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(sql)) { + Connection conn = null; + PreparedStatement deleteStmt = null; + PreparedStatement insertStmt = null; - System.out.println("表结构 lottery_results:"); - System.out.println("=========================="); - System.out.printf("%-5s %-15s %-10s %-5s %-5s %-5s%n", - "cid", "name", "type", "notnull", "dflt_value", "pk"); - System.out.println("------------------------------------------------------------"); + try { + conn = getConnection(); + conn.setAutoCommit(false); // 开启事务 - while (rs.next()) { - System.out.printf("%-5d %-15s %-10s %-5d %-10s %-5d%n", - rs.getInt("cid"), - rs.getString("name"), - rs.getString("type"), - rs.getInt("notnull"), - rs.getString("dflt_value"), - rs.getInt("pk")); + String username = loginInfo.getUsername(); + + // 1. 先检查用户是否存在 + boolean exists = existsByUsername(conn, username); + + // 2. 如果存在,先删除 + if (exists) { + String deleteSQL = "DELETE FROM login_info WHERE user_name = ?"; + deleteStmt = conn.prepareStatement(deleteSQL); + deleteStmt.setString(1, username); + int deleted = deleteStmt.executeUpdate(); + System.out.println("删除已存在的用户记录: " + username + ", 删除条数: " + deleted); + } + + // 3. 插入新记录 + String insertSQL = """ + INSERT INTO login_info + (user_name, password, login_url, win_num, lose_num, create_time, update_time) + VALUES (?, ?, ?, ?, ?, ?, ?) + """; + + insertStmt = conn.prepareStatement(insertSQL); + insertStmt.setString(1, loginInfo.getUsername()); + insertStmt.setString(2, loginInfo.getPassword()); + insertStmt.setString(3, loginInfo.getLoginUrl()); + insertStmt.setInt(4, loginInfo.getWinNum() != null ? loginInfo.getWinNum() : 0); + insertStmt.setInt(5, loginInfo.getLoseNum() != null ? loginInfo.getLoseNum() : 0); + insertStmt.setTimestamp(6, new Timestamp( + loginInfo.getCreateTime() != null ? + loginInfo.getCreateTime().getTime() : + System.currentTimeMillis() + )); + insertStmt.setTimestamp(7, new Timestamp( + loginInfo.getUpdateTime() != null ? + loginInfo.getUpdateTime().getTime() : + System.currentTimeMillis() + )); + + int inserted = insertStmt.executeUpdate(); + + // 提交事务 + conn.commit(); + + if (inserted > 0) { + System.out.println((exists ? "更新" : "添加") + "登录信息成功: " + username); + return true; + } else { + System.out.println("添加/更新登录信息失败: " + username); + return false; } } catch (SQLException e) { + try { + if (conn != null) conn.rollback(); + } catch (SQLException ex) { + ex.printStackTrace(); + } + System.err.println("添加/更新登录信息失败: " + e.getMessage()); e.printStackTrace(); + return false; + } finally { + closeResources(null, deleteStmt, null); + closeResources(null, insertStmt, null); + closeResources(null, null, conn); } } -} \ No newline at end of file + + /** + * 检查用户名是否存在(使用现有连接) + */ + private static boolean existsByUsername(Connection conn, String username) throws SQLException { + String sql = "SELECT COUNT(*) FROM login_info WHERE user_name = ?"; + + try (PreparedStatement pstmt = conn.prepareStatement(sql)) { + pstmt.setString(1, username); + + try (ResultSet rs = pstmt.executeQuery()) { + return rs.next() && rs.getInt(1) > 0; + } + } + } + + + /** + * 根据ID查询 + */ + public static LoginInfoResult getLoginInfo() { + String sql = "SELECT * FROM login_info "; + + try (Connection conn = getConnection(); + PreparedStatement pstmt = conn.prepareStatement(sql)) { + + try (ResultSet rs = pstmt.executeQuery()) { + if (rs.next()) { + return mapResultSetToLoginInfo(rs); + } + } + + } catch (SQLException e) { + System.err.println("根据ID查询登录信息失败: " + e.getMessage()); + e.printStackTrace(); + } + return null; + } + + + /** + * 将ResultSet映射为LoginInfoResult对象 + */ + private static LoginInfoResult mapResultSetToLoginInfo(ResultSet rs) throws SQLException { + LoginInfoResult loginInfo = new LoginInfoResult(); + + loginInfo.setId(rs.getLong("id")); + loginInfo.setUsername(rs.getString("user_name")); + loginInfo.setPassword(rs.getString("password")); + loginInfo.setLoginUrl(rs.getString("login_url")); + loginInfo.setWinNum(rs.getInt("win_num")); + loginInfo.setLoseNum(rs.getInt("lose_num")); + loginInfo.setCreateTime(rs.getTimestamp("create_time")); + loginInfo.setUpdateTime(rs.getTimestamp("update_time")); + + return loginInfo; + } +} diff --git a/src/main/java/com/tem/bocai/util/TokenCacheService.java b/src/main/java/com/tem/bocai/util/TokenCacheService.java index ecaf7f0..804f225 100644 --- a/src/main/java/com/tem/bocai/util/TokenCacheService.java +++ b/src/main/java/com/tem/bocai/util/TokenCacheService.java @@ -1,10 +1,35 @@ package com.tem.bocai.util; +import com.tem.bocai.service.LoginService; +import net.sourceforge.tess4j.Tesseract; +import net.sourceforge.tess4j.TesseractException; +import org.apache.http.Header; +import org.apache.http.NameValuePair; +import org.apache.http.client.CookieStore; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.cookie.Cookie; +import org.apache.http.impl.client.BasicCookieStore; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.Callable; @Service @@ -13,7 +38,10 @@ public class TokenCacheService { private static final String TOKEN_CACHE_NAME = "tokenCache"; private static final String TOKEN_KEY = "login_token"; private static final String TOKEN_INFO_KEY = "token_info"; - + private static final int MAX_RETRY = 10; + private static final String BASE_URL = "https://4701268539-esh.qdk63ayw8g.com"; + @Autowired + private Tesseract tesseract; @Autowired private CacheManager tokenCacheManager; @@ -35,8 +63,11 @@ public class TokenCacheService { Cache cache = tokenCacheManager.getCache(TOKEN_CACHE_NAME); if (cache != null) { return cache.get(TOKEN_KEY, String.class); + } else { + String token = getTokenSqlite(); + System.out.println("重新登入获取的token==" + token); + return token; } - return null; } /** @@ -89,4 +120,336 @@ public class TokenCacheService { return null; } + + public String getTokenSqlite() { + String token = ""; + for (int attempt = 1; attempt <= MAX_RETRY; attempt++) { + System.out.println("\n=== 第 " + attempt + " 次尝试 ==="); + try { + token = attemptLogin(); + + if (token != null && !token.isEmpty()) { + return token; + } + if (attempt < MAX_RETRY) { + waitForRetry(attempt); + } + } catch (Exception e) { + System.err.println("第 " + attempt + " 次尝试失败: " + e.getMessage()); + e.printStackTrace(); + } + } + return ""; + } + + /** + * 等待重试 + */ + public void waitForRetry(int attempt) throws InterruptedException { + System.out.println("\n等待2秒后进行下一次尝试..."); + Thread.sleep(2000); + } + + /** + * 单次登录尝试 + */ + public String attemptLogin() { + CookieStore cookieStore = new BasicCookieStore(); + try (CloseableHttpClient httpClient = createHttpClient(cookieStore)) { + // 1. 获取验证码 + byte[] imageData = fetchCaptcha(httpClient); + if (imageData == null) { + return null; + } + // 2. OCR识别验证码 + String code = processCaptcha(imageData); + if (code == null || code.length() != 4) { + return null; + } + // 3. 执行登录 + return performLogin(httpClient, cookieStore, code); + + } catch (Exception e) { + System.out.println("登录尝试失败" + e); + } + return null; + } + + + /** + * 创建HttpClient + */ + private CloseableHttpClient createHttpClient(CookieStore cookieStore) { + return HttpClients.custom() + .setDefaultCookieStore(cookieStore) + .build(); + } + + /** + * 获取验证码图片 + */ + private byte[] fetchCaptcha(CloseableHttpClient httpClient) + throws IOException, InterruptedException { + + System.out.println("获取验证码..."); + + // 添加随机延迟 + Thread.sleep(1000 + (long) (Math.random() * 1000)); + + HttpGet getCaptcha = new HttpGet(BASE_URL + "/code"); + setCommonHeaders(getCaptcha); + getCaptcha.setHeader("Referer", BASE_URL + "/login"); + + try (CloseableHttpResponse captchaResponse = httpClient.execute(getCaptcha)) { + int captchaStatus = captchaResponse.getStatusLine().getStatusCode(); + System.out.println("验证码响应状态码: " + captchaStatus); + + if (captchaStatus == 200) { + return EntityUtils.toByteArray(captchaResponse.getEntity()); + } else if (captchaStatus == 429) { + System.out.println("获取验证码被限速,等待后重试..."); + Thread.sleep(3000); + } else { + System.out.println("获取验证码失败: " + captchaStatus); + } + } + + return null; + } + + /** + * 处理验证码识别 + */ + private String processCaptcha(byte[] imageData) + throws IOException, TesseractException { + + BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageData)); + String rawOcr = tesseract.doOCR(image); + + // 清理验证码 + String code = rawOcr.replaceAll("\\s+", "").trim(); + code = code.replaceAll("[^0-9]", ""); // 只保留数字 + System.out.println("OCR原始结果: " + rawOcr); + System.out.println("清理后验证码: [" + code + "] 长度: " + code.length()); + // 保存图片用于调试 + //saveCaptchaImage(image); + + return code; + } + + /** + * 设置通用请求头 + */ + private void setCommonHeaders(HttpRequestBase request) { + request.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"); + request.setHeader("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8"); + request.setHeader("Accept-Encoding", "gzip, deflate, br"); + request.setHeader("Connection", "keep-alive"); + request.setHeader("Upgrade-Insecure-Requests", "1"); + } + /** + * 保存验证码图片 + */ +/* private void saveCaptchaImage(BufferedImage image) throws IOException { + String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); + File output = new File("captcha_" + timestamp + ".png"); + ImageIO.write(image, "png", output); + System.out.println("验证码图片已保存到: " + output.getAbsolutePath()); + }*/ + + /** + * 执行登录请求 + */ + private String performLogin(CloseableHttpClient httpClient, + CookieStore cookieStore, + String code) throws IOException, InterruptedException { + + System.out.println("执行登录..."); + // 等待一下再发送登录请求 + Thread.sleep(1500 + (long) (Math.random() * 1000)); + + HttpPost loginPost = createLoginRequest(code); + + try (CloseableHttpResponse loginResponse = httpClient.execute(loginPost)) { + return processLoginResponse(loginResponse, cookieStore); + } + } + + /** + * 创建登录请求 + */ + private HttpPost createLoginRequest(String code) throws UnsupportedEncodingException { + HttpPost loginPost = new HttpPost(BASE_URL + "/login"); + + // 设置请求头 + setCommonHeaders(loginPost); + loginPost.setHeader("Referer", BASE_URL + "/login"); + loginPost.setHeader("Origin", BASE_URL); + loginPost.setHeader("Accept", "application/json, text/plain, */*"); + + // 构建登录参数 + List params = new ArrayList<>(); + params.add(new BasicNameValuePair("type", "1")); + params.add(new BasicNameValuePair("account", "pmk1")); + params.add(new BasicNameValuePair("password", "Asd123123")); + params.add(new BasicNameValuePair("code", code)); + + loginPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8")); + + // 禁用自动重定向 + RequestConfig requestConfig = RequestConfig.custom() + .setRedirectsEnabled(false) + .build(); + loginPost.setConfig(requestConfig); + + return loginPost; + } + + /** + * 处理登录响应 + */ + private String processLoginResponse(CloseableHttpResponse loginResponse, + CookieStore cookieStore) { + try { + int statusCode = loginResponse.getStatusLine().getStatusCode(); + System.out.println("登录响应状态码: " + statusCode); + + // 处理限速 + if (statusCode == 429) { + handleRateLimit(loginResponse); + return null; + } + + // 打印响应头 + printResponseHeaders(loginResponse); + + // 检查重定向 + if (statusCode == 302) { + if (checkRedirectForError(loginResponse)) { + return null; + } + } + + // 读取响应体 + String tokenFromBody = extractTokenFromResponseBody(loginResponse); + if (tokenFromBody != null) { + return tokenFromBody; + } + + // 从cookies中提取token + return extractTokenFromCookies(cookieStore, statusCode); + } catch (Exception e) { + System.out.println("请求异常" + e); + } + return null; + } + + /** + * 打印响应头 + */ + private void printResponseHeaders(CloseableHttpResponse response) { + System.out.println("响应头:"); + for (Header header : response.getAllHeaders()) { + System.out.println(" " + header.getName() + ": " + header.getValue()); + } + } + + /** + * 检查重定向是否包含错误 + */ + private boolean checkRedirectForError(CloseableHttpResponse response) { + Header locationHeader = response.getFirstHeader("Location"); + if (locationHeader != null) { + String location = locationHeader.getValue(); + System.out.println("重定向到: " + location); + + if (location.contains("e=3")) { + System.out.println("验证码错误 (e=3)"); + return true; + } + } + return false; + } + + /** + * 从cookies中提取token + */ + private String extractTokenFromCookies(CookieStore cookieStore, int statusCode) { + List cookies = cookieStore.getCookies(); + System.out.println("所有cookies (" + cookies.size() + "个):"); + + String token = null; + for (Cookie cookie : cookies) { + System.out.println(" " + cookie.getName() + " = " + cookie.getValue()); + + if ("token".equals(cookie.getName()) || + cookie.getName().toLowerCase().contains("token")) { + token = cookie.getValue(); + } + } + + if (token != null && !token.isEmpty()) { + System.out.println("\n[SUCCESS] Login OK!"); + System.out.println("Token: " + token); + return token; + } else if (statusCode == 200) { + System.out.println("登录返回200但没有找到token,可能需要检查其他认证方式"); + } + + return null; + } + + + /** + * 从响应体中提取token + */ + private String extractTokenFromResponseBody(CloseableHttpResponse response) throws IOException { + if (response.getEntity() != null) { + String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8"); + if (responseBody != null && !responseBody.isEmpty()) { + System.out.println("响应体: " + responseBody); + + // 检查响应体中是否有token(JSON格式) + if (responseBody.contains("\"token\"")) { + // 简单提取token + int start = responseBody.indexOf("\"token\":\""); + if (start != -1) { + start += 9; + int end = responseBody.indexOf("\"", start); + if (end != -1) { + String token = responseBody.substring(start, end); + System.out.println("\n[SUCCESS] 从响应体找到Token!"); + System.out.println("Token: " + token); + return token; + } + } + } + } + // 消耗实体 + EntityUtils.consume(response.getEntity()); + } + return null; + } + + /** + * 处理速率限制 + */ + private void handleRateLimit(CloseableHttpResponse response) throws InterruptedException { + System.out.println("登录请求被限速 (429 Too Many Requests)"); + + Header retryAfterHeader = response.getFirstHeader("Retry-After"); + if (retryAfterHeader != null) { + try { + int retryAfterSeconds = Integer.parseInt(retryAfterHeader.getValue()); + System.out.println("服务器要求等待 " + retryAfterSeconds + " 秒"); + Thread.sleep(retryAfterSeconds * 1000L); + } catch (NumberFormatException e) { + System.out.println("等待5秒后重试"); + Thread.sleep(5000); + } + } else { + System.out.println("等待3秒后重试"); + Thread.sleep(3000); + } + } }