init
This commit is contained in:
parent
0b75cc6ac2
commit
a6eedd9001
@ -4,6 +4,7 @@ import com.microsoft.playwright.Browser;
|
||||
import com.microsoft.playwright.BrowserContext;
|
||||
import com.microsoft.playwright.BrowserType;
|
||||
import com.microsoft.playwright.ElementHandle;
|
||||
import com.microsoft.playwright.Frame;
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.microsoft.playwright.Playwright;
|
||||
import com.microsoft.playwright.options.Cookie;
|
||||
@ -28,9 +29,11 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static top.biwin.xianyu.goofish.BrowserConstant.STEALTH_SCRIPT;
|
||||
import static top.biwin.xianyu.goofish.util.CookieUtils.buildCookieStr;
|
||||
import static top.biwin.xianyu.goofish.util.SliderUtils.attemptSolveSlider;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
@ -310,7 +313,6 @@ public class BrowserService {
|
||||
|
||||
public String refreshGoofishAccountCookie(String goofishId, Boolean showBrowser, Double slowMo, String goofishCookieStr) {
|
||||
BrowserContext browserContext = loadPersistentContext(goofishId, showBrowser, slowMo, goofishCookieStr);
|
||||
addPersistentContextCookie(goofishId, goofishCookieStr);
|
||||
|
||||
// TODO 刷新账号信息,并返回新的 cookie
|
||||
Page page = browserContext.pages().isEmpty() ? browserContext.newPage() : browserContext.pages().get(0);
|
||||
@ -320,14 +322,85 @@ public class BrowserService {
|
||||
log.debug("【{}】等待页面加载,查找登录框... url: http://www.goofish.com/im", goofishId);
|
||||
// 确保页面加载完成
|
||||
page.getByText("登录后可以更懂你,推荐你喜欢的商品!");
|
||||
|
||||
String cookieStr = buildCookieStr(browserContext);
|
||||
Long goofishUserId = goofishApiService.getUserId(goofishId, cookieStr);
|
||||
browserContext.close();
|
||||
if (goofishUserId > 0L) {
|
||||
return cookieStr;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用账号密码自动登录
|
||||
*/
|
||||
public boolean autoLoginUsingAccountAndPassword(BrowserContext context, String account, String password, Boolean showBrowser) {
|
||||
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);
|
||||
// 点击协议,并登录
|
||||
ElementHandle agreement = loginFrame.querySelector("#fm-agreement-checkbox");
|
||||
if (agreement != null && !agreement.isChecked()) {
|
||||
log.debug("【{}】正在查找协议复选框,并勾选......", account);
|
||||
agreement.click();
|
||||
}
|
||||
log.debug("【{}】单击登录按钮...", account);
|
||||
loginFrame.click("button.fm-button.fm-submit.password-login");
|
||||
page.waitForTimeout(3000D);
|
||||
|
||||
// 这里点击后,可能并不能成功,会出现滑块
|
||||
if (Objects.nonNull(findLoginFrame(page, account))) {
|
||||
// 内部应该要有重试处理滑块的逻辑
|
||||
boolean resolveResult = attemptSolveSlider(loginFrame, account, new AtomicInteger(3));
|
||||
log.debug("【{}】滑块处理结果: {}!", account, resolveResult ? "成功" : "失败");
|
||||
if(resolveResult) {
|
||||
// 这里可能已经登录成功,等待选择是否持久化登录
|
||||
loginFrame.waitForTimeout(10000);
|
||||
log.debug("【{}】验证是否登录成功...", account);
|
||||
Frame lastCheckFrame = findLoginFrame(page, account);
|
||||
if(Objects.nonNull(lastCheckFrame)) {
|
||||
log.error("【{}】登录失败,仍存在登录页面...", account);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
log.error("【{}】账号密码登录失败", account);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
log.debug("【{}】未找到密码登录框,可能是持久化登录...", account);
|
||||
}
|
||||
} else {
|
||||
// 可能是快捷登录按钮页。等待 10s 后,查看右上角有没有登录信息
|
||||
page.waitForTimeout(10000);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void closePersistentContext(String goofishId) {
|
||||
BrowserContext browserContext = persistentContexts.get(goofishId);
|
||||
if (Objects.isNull(browserContext)) {
|
||||
@ -351,4 +424,50 @@ public class BrowserService {
|
||||
browserContext.addCookies(cookies);
|
||||
}
|
||||
|
||||
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) {
|
||||
log.error("查找登录框异常:{}", e.getMessage(), 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) {
|
||||
log.error("查找登录框异常:{}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1,25 +1,15 @@
|
||||
package top.biwin.xianyu.goofish.service;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.microsoft.playwright.BrowserContext;
|
||||
import com.microsoft.playwright.ElementHandle;
|
||||
import com.microsoft.playwright.Frame;
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.microsoft.playwright.options.Cookie;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.biwin.xianyu.core.entity.GoofishAccountEntity;
|
||||
import top.biwin.xianyu.core.repository.GoofishAccountRepository;
|
||||
import top.biwin.xianyu.goofish.util.CookieUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static top.biwin.xianyu.goofish.BrowserConstant.STEALTH_SCRIPT;
|
||||
import static top.biwin.xianyu.goofish.util.CookieUtils.buildCookieStr;
|
||||
import static top.biwin.xianyu.goofish.util.SliderUtils.attemptSolveSlider;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
@ -42,71 +32,16 @@ public class GoofishPwdLoginService {
|
||||
*
|
||||
* @param account
|
||||
* @param password
|
||||
* @param showBrowser 是否有头,headless
|
||||
* @param systemUsername 当前系统登录用户
|
||||
* @param showBrowser 是否有头,headless
|
||||
* @param systemUserId 当前系统登录用户
|
||||
*/
|
||||
public void processPasswordLogin(String account, String password, Boolean showBrowser, Long systemUsername) {
|
||||
public void processPasswordLogin(String account, String password, Boolean showBrowser, Long systemUserId) {
|
||||
BrowserContext context = browserService.loadPersistentContext(account, showBrowser, 1000D, 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);
|
||||
// 点击协议,并登录
|
||||
ElementHandle agreement = loginFrame.querySelector("#fm-agreement-checkbox");
|
||||
if (agreement != null && !agreement.isChecked()) {
|
||||
log.debug("【{}】正在查找协议复选框,并勾选......", account);
|
||||
agreement.click();
|
||||
}
|
||||
log.debug("【{}】单击登录按钮...", account);
|
||||
loginFrame.click("button.fm-button.fm-submit.password-login");
|
||||
|
||||
// 这里点击后,可能并不能成功,会出现滑块
|
||||
if (Objects.nonNull(findLoginFrame(page, account))) {
|
||||
// 内部应该要有重试处理滑块的逻辑
|
||||
boolean resolveResult = attemptSolveSlider(loginFrame, account, new AtomicInteger(3));
|
||||
log.debug("【{}】滑块结果: {}!", account, resolveResult ? "成功" : "失败");
|
||||
if(resolveResult) {
|
||||
// 这里可能已经登录成功,等待选择是否持久化登录
|
||||
loginFrame.waitForTimeout(10000);
|
||||
log.debug("【{}】验证是否登录成功...", account);
|
||||
Frame lastCheckFrame = findLoginFrame(page, account);
|
||||
if(Objects.nonNull(lastCheckFrame)) {
|
||||
log.error("【{}】登录失败,仍存在登录页面...", account);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
log.error("【{}】账号密码登录失败", account);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
log.debug("【{}】未找到密码登录框,可能是持久化登录...", account);
|
||||
}
|
||||
} else {
|
||||
// 可能是快捷登录按钮页。等待 10s 后,查看右上角有没有登录信息
|
||||
page.waitForTimeout(10000);
|
||||
log.debug("【{}】开始启动浏览器自动化操作", account);
|
||||
boolean loginResult = browserService.autoLoginUsingAccountAndPassword(context, account, password, showBrowser);
|
||||
if (!loginResult) {
|
||||
log.error("【{}】浏览器自动化操作失败", account);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理登录成功后的账号 cookie,并存库,更新persistentData 目录名称
|
||||
@ -117,7 +52,7 @@ public class GoofishPwdLoginService {
|
||||
GoofishAccountEntity entity = new GoofishAccountEntity();
|
||||
entity.setId(String.valueOf(goofishUserId));
|
||||
entity.setCookie(cookieStr);
|
||||
entity.setUserId(systemUsername);
|
||||
entity.setUserId(systemUserId);
|
||||
entity.setAutoConfirm(1);
|
||||
entity.setUsername(account);
|
||||
entity.setNickname(goofishApiService.getNickName(account, cookieStr));
|
||||
@ -133,51 +68,4 @@ public class GoofishPwdLoginService {
|
||||
}
|
||||
|
||||
|
||||
|
||||
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) {
|
||||
log.error("查找登录框异常:{}", e.getMessage(), 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) {
|
||||
log.error("查找登录框异常:{}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,8 +47,8 @@ public final class CookieUtils {
|
||||
if (StrUtil.isBlank(cookiesStr)) {
|
||||
return cookieMap;
|
||||
}
|
||||
|
||||
String[] parts = cookiesStr.split("; ");
|
||||
cookiesStr = cookiesStr.replace(" ", "");
|
||||
String[] parts = cookiesStr.split(";");
|
||||
for (String part : parts) {
|
||||
if (part.contains("=")) {
|
||||
String[] kv = part.split("=", 2);
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package top.biwin.xianyu.goofish.websocket;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSON;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
@ -32,7 +31,6 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.Semaphore;
|
||||
@ -42,6 +40,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static top.biwin.xianyu.goofish.util.CookieUtils.buildCookieStr;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
@ -91,7 +91,6 @@ public class GoofishAccountWebsocket extends TextWebSocketHandler {
|
||||
BrowserService browserService,
|
||||
GoofishApiService goofishApiService,
|
||||
WebSocketConfiguration webSocketConfiguration,
|
||||
// WebSocketContainer webSocketContainer,
|
||||
@Qualifier("goofishAccountWebSocketExecutor") ScheduledExecutorService scheduledExecutorService) {
|
||||
super();
|
||||
this.goofishId = goofishId;
|
||||
@ -136,14 +135,30 @@ public class GoofishAccountWebsocket extends TextWebSocketHandler {
|
||||
this.userId = account.getUserId();
|
||||
|
||||
if (StrUtil.isBlank(cookiesStr)) {
|
||||
log.error("【{}】Cookie值为空", goofishId);
|
||||
log.error("【{}】Cookie值为空, 请先用到账号中心重新添加账号或手动更新 cookie", goofishId);
|
||||
return false;
|
||||
}
|
||||
|
||||
log.debug("【{}】验证 Cookie 是否有效", goofishId);
|
||||
String accountName = goofishApiService.getAccount(account.getId(), cookiesStr);
|
||||
if (!StringUtils.hasText(accountName)) {
|
||||
// 说明 cookie 失效,尝试重新登录
|
||||
this.cookiesStr = browserService.refreshGoofishAccountCookie(account.getId(), account.getShowBrowser() == 1, 1000D, cookiesStr);
|
||||
// 说明 cookie 失效,尝试先用持久化浏览器数据刷新页面获取新的 cookie
|
||||
log.debug("【{}】Cookie 已失效,尝试使用持久化浏览器数据获取新的 Cookie", goofishId);
|
||||
this.cookiesStr = browserService.refreshGoofishAccountCookie(account.getId(), account.getShowBrowser() == 1, 1000D, null);
|
||||
}
|
||||
if (!StringUtils.hasText(cookiesStr)) {
|
||||
log.debug("【{}】Cookie 未获取到,再尝试使用基础账号用户名密码进行自动登录获取新的 Cookie", goofishId);
|
||||
// TODO 重新尝试使用 account 表的基础数据自动登录
|
||||
BrowserContext browserContext = browserService.loadPersistentContext(account.getId(), account.getShowBrowser() == 1, 1000D, null);
|
||||
browserService.autoLoginUsingAccountAndPassword(browserContext, account.getUsername(), account.getPassword(), account.getShowBrowser() == 1);
|
||||
this.cookiesStr = buildCookieStr(browserContext);
|
||||
browserContext.close();
|
||||
Long goofishUserId = goofishApiService.getUserId(goofishId, cookiesStr);
|
||||
if (goofishUserId > 0L) {
|
||||
log.debug("【{}】Cookie 刷新成功", goofishId);
|
||||
} else {
|
||||
log.error("【{}】Cookie 刷新失败", goofishId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 解析Cookie
|
||||
@ -398,6 +413,7 @@ public class GoofishAccountWebsocket extends TextWebSocketHandler {
|
||||
|
||||
try {
|
||||
currentToken = goofishApiService.getWsToken(goofishId, cookiesStr, deviceId);
|
||||
this.lastTokenRefreshTime.set(System.currentTimeMillis());
|
||||
log.info("【{}】Token刷新调用完成,currentToken={}", goofishId, currentToken != null ? "已获取" : "未获取");
|
||||
} catch (Exception e) {
|
||||
log.error("【{}】Token刷新过程出错: {}", goofishId, e.getMessage(), e);
|
||||
@ -423,7 +439,7 @@ public class GoofishAccountWebsocket extends TextWebSocketHandler {
|
||||
regHeaders.put("cache-header", "app-key token ua wv");
|
||||
regHeaders.put("app-key", webSocketConfiguration.getAppKey());
|
||||
regHeaders.put("token", currentToken);
|
||||
regHeaders.put("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");
|
||||
regHeaders.put("ua", webSocketConfiguration.getUa());
|
||||
regHeaders.put("dt", "j");
|
||||
regHeaders.put("wv", "im:3,au:3,sy:6");
|
||||
regHeaders.put("sync", "0,0;0;0;");
|
||||
@ -443,6 +459,24 @@ public class GoofishAccountWebsocket extends TextWebSocketHandler {
|
||||
log.info("【{}】等待1秒...", goofishId);
|
||||
Thread.sleep(1000);
|
||||
|
||||
log.info("【{}】准备发送 /getState 消息...", goofishId);
|
||||
JSONObject getState = new JSONObject();
|
||||
getState.put("lwp", "/r/SyncStatus/getState");
|
||||
JSONObject stateHeader = new JSONObject();
|
||||
stateHeader.put("mid", XianyuUtils.generateMid());
|
||||
getState.put("headers", stateHeader);
|
||||
JSONArray stateBody = new JSONArray();
|
||||
JSONObject bodyTopic = new JSONObject();
|
||||
bodyTopic.put("topic", "sync");
|
||||
stateBody.put(bodyTopic);
|
||||
getState.put("body", stateBody);
|
||||
try {
|
||||
session.sendMessage(new TextMessage(getState.toString()));
|
||||
log.info("【{}】✅ /getState 消息已发送", goofishId);
|
||||
} catch (Exception e) {
|
||||
log.error("【{}】❌ 发送 /getState 消息失败: {}", goofishId, e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
// 发送 /ackDiff 消息
|
||||
log.info("【{}】准备发送 /ackDiff 消息...", goofishId);
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
package top.biwin.xianyu.goofish.websocket;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
@ -10,7 +11,8 @@ import org.springframework.context.annotation.Configuration;
|
||||
* @since 2026-02-01 10:01
|
||||
*/
|
||||
@Data
|
||||
@Configuration(proxyBeanMethods = false, value = "goofish.websocket")
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "goofish.websocket")
|
||||
public class WebSocketConfiguration {
|
||||
private String appKey;
|
||||
private String wsUrl;
|
||||
@ -18,4 +20,5 @@ public class WebSocketConfiguration {
|
||||
private Integer heartbeatInterval;
|
||||
private Integer cleanupInterval;
|
||||
private Integer cookieRefreshInterval;
|
||||
private String ua;
|
||||
}
|
||||
|
||||
@ -0,0 +1,80 @@
|
||||
package top.biwin.xianyu.goofish.websocket;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.biwin.xianyu.core.entity.GoofishAccountEntity;
|
||||
import top.biwin.xianyu.core.repository.GoofishAccountRepository;
|
||||
import top.biwin.xianyu.goofish.service.BrowserService;
|
||||
import top.biwin.xianyu.goofish.service.GoofishApiService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author wangli
|
||||
* @since 2026-02-02 22:00
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class WebSocketStarter {
|
||||
private final Map<String, GoofishAccountWebsocket> sockets = new ConcurrentHashMap<>();
|
||||
|
||||
@Autowired
|
||||
private GoofishAccountRepository goofishAccountRepository;
|
||||
@Autowired
|
||||
private BrowserService browserService;
|
||||
@Autowired
|
||||
private GoofishApiService goofishApiService;
|
||||
@Autowired
|
||||
private WebSocketConfiguration webSocketConfiguration;
|
||||
@Autowired
|
||||
@Qualifier("goofishAccountWebSocketExecutor")
|
||||
private ScheduledExecutorService goofishAccountWebSocketExecutor;
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
log.info("Start all goofish account websocket ...");
|
||||
List<GoofishAccountEntity> accounts = goofishAccountRepository.findAll();
|
||||
accounts.stream()
|
||||
.filter(i -> Objects.equals(i.getEnabled(), Boolean.TRUE))
|
||||
.forEach(i -> {
|
||||
startWebSocket(i.getId());
|
||||
});
|
||||
}
|
||||
|
||||
public void startWebSocket(String goofishId) {
|
||||
if (sockets.containsKey(goofishId)) {
|
||||
log.warn("【{}】WebSocket already running", goofishId);
|
||||
return;
|
||||
}
|
||||
GoofishAccountWebsocket websocket = new GoofishAccountWebsocket(goofishId,
|
||||
goofishAccountRepository,
|
||||
browserService,
|
||||
goofishApiService,
|
||||
webSocketConfiguration,
|
||||
goofishAccountWebSocketExecutor);
|
||||
sockets.put(goofishId, websocket);
|
||||
websocket.start();
|
||||
}
|
||||
|
||||
public void closeWebSocket(String goofishId) {
|
||||
getWebSocket(goofishId)
|
||||
.ifPresent(socket -> {
|
||||
socket.stop();
|
||||
sockets.remove(goofishId);
|
||||
});
|
||||
}
|
||||
|
||||
public Optional<GoofishAccountWebsocket> getWebSocket(String goofishId) {
|
||||
return Optional.ofNullable(sockets.getOrDefault(goofishId, null));
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@ package top.biwin.xinayu.server;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
|
||||
/**
|
||||
@ -18,6 +19,7 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
@SpringBootApplication(scanBasePackages = "top.biwin")
|
||||
@EntityScan(basePackages = "top.biwin.xianyu.core.entity")
|
||||
@EnableJpaRepositories(basePackages = "top.biwin.xianyu.core.repository")
|
||||
//@EnableConfigurationProperties
|
||||
public class XianyuFreedomApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(XianyuFreedomApplication.class, args);
|
||||
|
||||
@ -69,7 +69,10 @@ goofish:
|
||||
appKey: 444e9908a51d1cb236a27862abc769c9
|
||||
wsUrl: wss://wss-goofish.dingtalk.com/
|
||||
tokenRefreshInterval: 72000
|
||||
heartbeatInterval: 30
|
||||
cookieRefreshInterval: 1200
|
||||
heartbeatInterval: 15
|
||||
ua: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0 DingTalk(2.2.0) OS(Mac OS/10.15.7) Browser(Edge/144.0.0.0) DingWeb/2.2.0 IMPaaS DingWeb/2.2.0'
|
||||
cleanupInterval: 300
|
||||
|
||||
assistant:
|
||||
static:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user