From 6273f35f53f095e4fd2d874f6ad2791b50cfd9d2 Mon Sep 17 00:00:00 2001 From: wangli Date: Wed, 28 Jan 2026 00:37:55 +0800 Subject: [PATCH] init --- .../biwin/xianyu/goofish/BrowserConstant.java | 100 ++++++++++++++ .../{service => }/api/GoofishAbstractApi.java | 2 +- .../goofish/{service => }/api/GoofishApi.java | 2 +- .../api => api/impl}/GetAccountApi.java | 3 +- .../goofish/service/BrowserService.java | 93 ++++++++++++- .../goofish/service/GoofishApiService.java | 2 +- .../service/GoofishPwdLoginService.java | 123 ++++++++++++++++++ .../{qrcode => service}/QrLoginService.java | 6 +- .../controller/GoofishAccountController.java | 18 +-- .../src/main/resources/application.yml | 8 +- 10 files changed, 337 insertions(+), 20 deletions(-) create mode 100644 xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/BrowserConstant.java rename xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/{service => }/api/GoofishAbstractApi.java (98%) rename xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/{service => }/api/GoofishApi.java (83%) rename xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/{service/api => api/impl}/GetAccountApi.java (97%) create mode 100644 xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/GoofishPwdLoginService.java rename xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/{qrcode => service}/QrLoginService.java (99%) diff --git a/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/BrowserConstant.java b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/BrowserConstant.java new file mode 100644 index 0000000..6e97799 --- /dev/null +++ b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/BrowserConstant.java @@ -0,0 +1,100 @@ +package top.biwin.xianyu.goofish; + +/** + * TODO + * 常量 + * + * @author wangli + * @since 2026-01-27 23:08 + */ +public interface BrowserConstant { + + String STEALTH_SCRIPT = + "// 隐藏webdriver属性\n" + + "Object.defineProperty(navigator, 'webdriver', {\n" + + " get: () => undefined,\n" + + "});\n" + + "\n" + + "// 隐藏自动化相关属性\n" + + "delete navigator.__proto__.webdriver;\n" + + "delete window.navigator.webdriver;\n" + + "delete window.navigator.__proto__.webdriver;\n" + + "\n" + + "// 模拟真实浏览器环境\n" + + "window.chrome = {\n" + + " runtime: {},\n" + + " loadTimes: function() {},\n" + + " csi: function() {},\n" + + " app: {}\n" + + "};\n" + + "\n" + + "// 覆盖plugins - 随机化\n" + + "const pluginCount = Math.floor(Math.random() * 5) + 3;\n" + + "Object.defineProperty(navigator, 'plugins', {\n" + + " get: () => Array.from({length: pluginCount}, (_, i) => ({\n" + + " name: 'Plugin' + i,\n" + + " description: 'Plugin ' + i\n" + + " })),\n" + + "});\n" + + "\n" + + "// 覆盖languages\n" + + "Object.defineProperty(navigator, 'languages', {\n" + + " get: () => ['zh-CN', 'zh', 'en'],\n" + + "});\n" + + "\n" + + "// 隐藏自动化检测 - 随机化硬件信息\n" + + "Object.defineProperty(navigator, 'hardwareConcurrency', { get: () => [2, 4, 6, 8][Math.floor(Math.random()*4)] });\n" + + "Object.defineProperty(navigator, 'deviceMemory', { get: () => [4, 8, 16][Math.floor(Math.random()*3)] });\n" + + "\n" + + "// 伪装 Date\n" + + "const OriginalDate = Date;\n" + + "Date = function(...args) {\n" + + " if (args.length === 0) {\n" + + " const date = new OriginalDate();\n" + + " const offset = Math.floor(Math.random() * 3) - 1;\n" + + " return new OriginalDate(date.getTime() + offset);\n" + + " }\n" + + " return new OriginalDate(...args);\n" + + "};\n" + + "Date.prototype = OriginalDate.prototype;\n" + + "Date.now = function() {\n" + + " return OriginalDate.now() + Math.floor(Math.random() * 3) - 1;\n" + + "};\n" + + "\n" + + "// 伪装 RTCPeerConnection\n" + + "if (window.RTCPeerConnection) {\n" + + " const originalRTC = window.RTCPeerConnection;\n" + + " window.RTCPeerConnection = function(...args) {\n" + + " const pc = new originalRTC(...args);\n" + + " const originalCreateOffer = pc.createOffer;\n" + + " pc.createOffer = function(...args) {\n" + + " return originalCreateOffer.apply(this, args).then(offer => {\n" + + " offer.sdp = offer.sdp.replace(/a=fingerprint:.*\\r\\n/g, \n" + + " `a=fingerprint:sha-256 ${Array.from({length:64}, ()=>Math.floor(Math.random()*16).toString(16)).join('')}\\r\\n`);\n" + + " return offer;\n" + + " });\n" + + " };\n" + + " return pc;\n" + + " };\n" + + "}\n" + + "\n" + + "// 伪装 Notification 权限\n" + + "Object.defineProperty(Notification, 'permission', {\n" + + " get: function() {\n" + + " return ['default', 'granted', 'denied'][Math.floor(Math.random() * 3)];\n" + + " }\n" + + "});\n" + + "\n" + + "// 隐藏 Playwright 特征\n" + + "delete window.__playwright;\n" + + "delete window.__pw_manual;\n" + + "delete window.__PW_inspect;\n" + + "\n" + + "// 伪装 Permissions API\n" + + "const originalQuery = window.navigator.permissions.query;\n" + + "window.navigator.permissions.query = (parameters) => (\n" + + " parameters.name === 'notifications' ?\n" + + " Promise.resolve({ state: Notification.permission }) :\n" + + " originalQuery(parameters)\n" + + ");\n"; +} diff --git a/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/api/GoofishAbstractApi.java b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/api/GoofishAbstractApi.java similarity index 98% rename from xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/api/GoofishAbstractApi.java rename to xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/api/GoofishAbstractApi.java index 324c28d..03c1ad1 100644 --- a/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/api/GoofishAbstractApi.java +++ b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/api/GoofishAbstractApi.java @@ -1,4 +1,4 @@ -package top.biwin.xianyu.goofish.service.api; +package top.biwin.xianyu.goofish.api; import cn.hutool.crypto.SecureUtil; import org.springframework.beans.factory.annotation.Value; diff --git a/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/api/GoofishApi.java b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/api/GoofishApi.java similarity index 83% rename from xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/api/GoofishApi.java rename to xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/api/GoofishApi.java index 3efa139..b5aa5e9 100644 --- a/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/api/GoofishApi.java +++ b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/api/GoofishApi.java @@ -1,4 +1,4 @@ -package top.biwin.xianyu.goofish.service.api; +package top.biwin.xianyu.goofish.api; /** * TODO diff --git a/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/api/GetAccountApi.java b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/api/impl/GetAccountApi.java similarity index 97% rename from xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/api/GetAccountApi.java rename to xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/api/impl/GetAccountApi.java index 4d6f7ad..24d2f7c 100644 --- a/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/api/GetAccountApi.java +++ b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/api/impl/GetAccountApi.java @@ -1,4 +1,4 @@ -package top.biwin.xianyu.goofish.service.api; +package top.biwin.xianyu.goofish.api.impl; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; @@ -8,6 +8,7 @@ import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import top.biwin.xianyu.goofish.api.GoofishAbstractApi; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; diff --git a/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/BrowserService.java b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/BrowserService.java index 218d40d..dc8616c 100644 --- a/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/BrowserService.java +++ b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/BrowserService.java @@ -12,13 +12,17 @@ import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; /** @@ -30,6 +34,10 @@ import java.util.concurrent.ConcurrentHashMap; @Component @Slf4j public class BrowserService { + @Value("${browser.persistence-data}") + private String persistenceData; + @Value("${browser.location}") + private String browserLocation; @Value("${browser.ua}") private String ua; // 为每个账号维护持久化浏览器上下文(用于Cookie刷新) @@ -116,6 +124,9 @@ public class BrowserService { public Map verifyQrLoginCookies(Map qrCookies, String accountId) { log.info("【QR Login】Verifying cookies for account: {}", accountId); + // TODO 这里可以抽象 newContext ,暂时先放这里 + // newContext 就像打开了一个“无痕/隐身窗口” + // launchPersistentContext() 就像打开了一个带有完整用户配置(Profile)的常规浏览器窗口 try (BrowserContext context = browser.newContext(new Browser.NewContextOptions() .setUserAgent(ua) )) { @@ -173,4 +184,84 @@ public class BrowserService { return new HashMap<>(); // Failed } -} + /** + * + * @param goofishId + * @param showBrowser 对应 headless + * @param slowMo 每个动作间的间隔 + * @param goofishCookieStr + * @return + */ + public BrowserContext loadPersistentContext(String goofishId, Boolean showBrowser, Double slowMo, String goofishCookieStr) { + BrowserContext browserContext = persistentContexts.get(goofishId); + if (Objects.nonNull(browserContext)) { + // TODO 可以尝试验证是否还生效 +// Page testPage = existingContext.newPage(); +// testPage.close(); + return browserContext; + } + try { + String userDataDir = persistenceData + "user_" + goofishId; + Path userDataPath = Paths.get(userDataDir); + Files.createDirectories(userDataPath); + log.debug("【{}-loadPersistentContext】创建浏览器持久化目录: {}, headless model: {}", goofishId, userDataDir, showBrowser); + + List args = new ArrayList<>(); + args.add("--no-sandbox"); + args.add("--disable-setuid-sandbox"); + args.add("--disable-dev-shm-usage"); + args.add("--disable-blink-features=AutomationControlled"); + args.add("--disable-web-security"); + args.add("--disable-features=VizDisplayCompositor"); + args.add("--lang=zh-CN"); + args.add("--start-maximized"); + + BrowserType.LaunchPersistentContextOptions options = new BrowserType.LaunchPersistentContextOptions() + .setHeadless(!showBrowser) + .setSlowMo(Objects.nonNull(slowMo) ? slowMo : 0) + .setArgs(args) + .setUserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0") + .setLocale("zh-CN") + .setAcceptDownloads(false) + .setExecutablePath(Paths.get(browserLocation)) + .setIgnoreHTTPSErrors(true); + + log.debug("【{}-loadPersistentContext】创建持久化浏览器上下文", goofishId); + BrowserContext context = playwright.chromium().launchPersistentContext(userDataPath, options); + + if (StringUtils.hasText(goofishCookieStr)) { + log.debug("【{}-loadPersistentContext】添加指定 Cookies: {}", goofishId, goofishCookieStr); + List playwrightCookies = new ArrayList<>(); + for (String cookiePart : goofishCookieStr.split(";")) { + String[] kv = cookiePart.trim().split("=", 2); + if (kv.length == 2) { + playwrightCookies.add(new Cookie(kv[0], kv[1]).setDomain(".goofish.com").setPath("/")); + } + } + context.addCookies(playwrightCookies); + log.debug("【{}-loadPersistentContext】已设置初始 Cookie: {} 个", goofishId, playwrightCookies); + } + + persistentContexts.put(goofishId, context); + return context; + } catch (Exception e) { + log.error("【{}-loadPersistentContext】创建持久化上下文失败", goofishId, e); + throw new RuntimeException("创建持久化浏览器上下文失败", e); + } + } + + /** + * 该方法一般是在 loadPersistentContext 函数后面调用,设置 cookie + */ + public void addPersistentContextCookie(String goofishId, List cookies) { + BrowserContext browserContext = persistentContexts.get(goofishId); + if (Objects.isNull(browserContext)) { + throw new IllegalStateException("未找到" + goofishId + "的浏览器持久化数据"); + } + if (CollectionUtils.isEmpty(cookies)) { + return; + } + browserContext.addCookies(cookies); + } + +} \ No newline at end of file diff --git a/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/GoofishApiService.java b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/GoofishApiService.java index f756e32..7ccd687 100644 --- a/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/GoofishApiService.java +++ b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/GoofishApiService.java @@ -6,7 +6,7 @@ import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import top.biwin.xianyu.core.repository.GoofishAccountRepository; -import top.biwin.xianyu.goofish.service.api.GoofishApi; +import top.biwin.xianyu.goofish.api.GoofishApi; import javax.annotation.Nullable; import java.util.List; diff --git a/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/GoofishPwdLoginService.java b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/GoofishPwdLoginService.java new file mode 100644 index 0000000..b56c42a --- /dev/null +++ b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/GoofishPwdLoginService.java @@ -0,0 +1,123 @@ +package top.biwin.xianyu.goofish.service; + +import com.microsoft.playwright.BrowserContext; +import com.microsoft.playwright.ElementHandle; +import com.microsoft.playwright.Frame; +import com.microsoft.playwright.Page; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Objects; + +import static top.biwin.xianyu.goofish.BrowserConstant.STEALTH_SCRIPT; + +/** + * TODO + * + * @author wangli + * @since 2026-01-27 22:13 + */ +@Component +@Slf4j +public class GoofishPwdLoginService { + + @Autowired + private BrowserService browserService; + + /** + * + * @param account + * @param password + * @param showBrowser 是否有头,headless + * @param systemUsername 当前系统登录用户 + */ + public void processPasswordLogin(String account, String password, Boolean showBrowser, String systemUsername) { + BrowserContext context = browserService.loadPersistentContext(account, showBrowser, 500D, null); + Page page = context.pages().isEmpty() ? context.newPage() : context.pages().get(0); + page.addInitScript(STEALTH_SCRIPT); + log.debug("【{}】正在导航至登录页... url: http://www.goofish.com/im", account); + page.navigate("https://www.goofish.com/im"); + log.debug("【{}】等待页面加载,查找登录框... url: http://www.goofish.com/im", account); + page.getByText("登录后可以更懂你,推荐你喜欢的商品!"); + Frame loginFrame = findLoginFrame(page, account); + if (Objects.nonNull(loginFrame)) { + // 开始登录 + log.debug("【{}】找到登录框,开始模拟登录", account); + ElementHandle switchLink = loginFrame.querySelector("a.password-login-tab-item"); + + if (switchLink != null && switchLink.isVisible()) { + log.debug("【{}】切换密码登陆框", account); + switchLink.click(new ElementHandle.ClickOptions().setDelay(100)); + log.debug("【{}】开始输入用户名...", account); + loginFrame.fill("#fm-login-id", ""); + loginFrame.type("#fm-login-id", account, new Frame.TypeOptions().setDelay(100)); + log.debug("【{}】开始输入密码...", account); + loginFrame.fill("#fm-login-password", ""); + loginFrame.type("#fm-login-password", password, new Frame.TypeOptions().setDelay(100)); + + loginFrame.waitForTimeout(1000); + // TODO 点击协议,并登录 + ElementHandle agreement = loginFrame.querySelector("#fm-agreement-checkbox"); + if (agreement != null && !agreement.isChecked()) { + log.debug("【{}】正在检查协议复选框,并勾选......", account); + agreement.click(); + } + log.info("【{}】单击登录按钮...", account); + loginFrame.click("button.fm-button.fm-submit.password-login"); + } else { + log.debug("【{}】未找到密码登录框,可能是持久化登录...", account); + } + } else { + // 可能是快捷登录按钮页。等待 10s 后,查看右上角有没有登录信息 + } + + // TODO 处理登录成功后的账号 cookie,并存库,更新persistentData 目录名称 + log.debug("...."); + } + + private Frame findLoginFrame(Page page, String account) { + String[] selectors = { + "#fm-login-id", + "input[name='fm-login-id']", + "input[placeholder*='手机号']", + "input[placeholder*='邮箱']", + ".fm-login-id", + "#J_LoginForm input[type='text']" + }; + + // 1. Check Main Frame First + for (String s : selectors) { + try { + if (page.isVisible(s)) { + log.debug("【{}】在主框架中判断登录是否显示: {}", account, s); + return page.mainFrame(); + } + if (page.querySelector(s) != null) { // Fallback check availability even if not visible yet + log.debug("【{}】在主框架中判断登录是否存在: {}", account, s); + return page.mainFrame(); + } + } catch (Exception e) { + } + } + + // 2. Check All Frames + for (Frame frame : page.frames()) { + for (String s : selectors) { + try { + if (frame.isVisible(s)) { + log.debug("【{}】在 Frame 中判断登录是否显示 ({}): {}", account, frame.url(), s); + return frame; + } + if (frame.querySelector(s) != null) { + log.debug("【{}】在 Frame 中判断登录是否存在({}): {}", account, frame.url(), s); + return frame; + } + } catch (Exception e) { + } + } + } + + return null; + } +} diff --git a/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/qrcode/QrLoginService.java b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/QrLoginService.java similarity index 99% rename from xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/qrcode/QrLoginService.java rename to xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/QrLoginService.java index d3d53ff..eccb9bd 100644 --- a/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/qrcode/QrLoginService.java +++ b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/QrLoginService.java @@ -1,4 +1,4 @@ -package top.biwin.xianyu.goofish.qrcode; +package top.biwin.xianyu.goofish.service; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.zxing.BarcodeFormat; @@ -21,8 +21,6 @@ import org.springframework.stereotype.Component; import top.biwin.xianyu.core.entity.GoofishAccountEntity; import top.biwin.xianyu.core.repository.GoofishAccountRepository; import top.biwin.xianyu.goofish.dto.QrLoginSessionDto; -import top.biwin.xianyu.goofish.service.BrowserService; -import top.biwin.xianyu.goofish.service.GoofishApiService; import top.biwin.xinayu.common.dto.response.GoofishAccountSimpleInfo; import top.biwin.xinayu.common.dto.response.GoofishQrStatusResponse; import top.biwin.xinayu.common.dto.response.QrLoginResponse; @@ -185,7 +183,7 @@ public class QrLoginService { // 6. 清理过期会话 cleanupExpiredSessions(); - // 7. 获取会话状态 + // 7. 调用闲鱼的 query.do 获取会话状态 GoofishQrStatusResponse statusInfo = getSessionStatus(sessionId); log.info("【QR Login】获取扫码状态信息: {}", statusInfo); diff --git a/xianyu-server/src/main/java/top/biwin/xinayu/server/controller/GoofishAccountController.java b/xianyu-server/src/main/java/top/biwin/xinayu/server/controller/GoofishAccountController.java index 1d9a39c..89bbba4 100644 --- a/xianyu-server/src/main/java/top/biwin/xinayu/server/controller/GoofishAccountController.java +++ b/xianyu-server/src/main/java/top/biwin/xinayu/server/controller/GoofishAccountController.java @@ -10,7 +10,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import top.biwin.xianyu.core.entity.GoofishAccountEntity; import top.biwin.xianyu.core.repository.GoofishAccountRepository; -import top.biwin.xianyu.goofish.qrcode.QrLoginService; +import top.biwin.xianyu.goofish.service.GoofishPwdLoginService; +import top.biwin.xianyu.goofish.service.QrLoginService; import top.biwin.xinayu.common.dto.request.GoofishAddCookieRequest; import top.biwin.xinayu.common.dto.request.GoofishPwdLoginRequest; import top.biwin.xinayu.common.dto.response.BaseResponse; @@ -21,7 +22,6 @@ import top.biwin.xinayu.server.util.CurrentUserUtil; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; /** @@ -37,6 +37,8 @@ public class GoofishAccountController { private QrLoginService qrLoginService; @Autowired private GoofishAccountRepository goofishAccountRepository; + @Autowired + private GoofishPwdLoginService goofishPwdLoginService; /** * 获取所有Cookie的详细信息(包括值和状态) @@ -87,14 +89,12 @@ public class GoofishAccountController { .build()); } + // TODO 自动化操作浏览器完成登陆,并写 goofishAccount 表 -// browserService.startPasswordLogin( -// request.getAccount_id(), -// request.getAccount(), -// request.getPassword(), -// request.getShow_browser() != null && request.getShow_browser(), -// userId -// ); + goofishPwdLoginService.processPasswordLogin(request.getAccount(), + request.getPassword(), + request.getShow_browser() != null && request.getShow_browser(), + CurrentUserUtil.getCurrentUsername()); return ResponseEntity.ok(BaseResponse.builder() .success(true) diff --git a/xianyu-server/src/main/resources/application.yml b/xianyu-server/src/main/resources/application.yml index d626f0c..db6ee20 100644 --- a/xianyu-server/src/main/resources/application.yml +++ b/xianyu-server/src/main/resources/application.yml @@ -40,8 +40,7 @@ spring: logging: level: root: INFO - com.xianyu.autoreply: DEBUG - top.biwin.xinayu: DEBUG + top.biwin.xianyu: DEBUG org.springframework.security: DEBUG org.hibernate.SQL: INFO # Ensure Hibernate SQL logging is not set to DEBUG/TRACE org.hibernate.type.descriptor.sql: INFO # Ensure Hibernate parameter logging is not set to DEBUG/TRACE @@ -68,4 +67,9 @@ goofish: appKey: 34839810 browser: + # chrome 浏览器软件安装位置 + location: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome + # 浏览器登录状态数据存储目录 + persistence-data: browser_data/ + # 浏览器 UA ua: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0'