feat(genmai): 集成跟卖精灵账号管理系统跟
- 新增卖精灵账号管理功能,支持多账号切换 - 实现账号增删改查接口与前端交互逻辑 -优化打开跟卖精灵流程,增加账号选择界面 - 添加账号权限限制与订阅升级提醒 - 完善后端账号实体类及数据库映射 - 更新系统控制器以支持指定账号启动功能- 调整HTTP请求路径适配新工具模块路由- 升级客户端版本至2.5.3并优化代码结构
This commit is contained in:
@@ -83,9 +83,9 @@ public class SystemController {
|
||||
}
|
||||
|
||||
@PostMapping("/genmai/open")
|
||||
public JsonData openGenmaiWebsite() {
|
||||
public JsonData openGenmaiWebsite(@RequestParam(required = false) Long accountId) {
|
||||
try {
|
||||
genmaiService.openGenmaiWebsite();
|
||||
genmaiService.openGenmaiWebsite(accountId);
|
||||
return JsonData.buildSuccess("跟卖精灵已打开");
|
||||
} catch (Exception e) {
|
||||
logger.error("打开跟卖精灵失败", e);
|
||||
|
||||
@@ -97,6 +97,11 @@ public class AmazonScrapingServiceImpl implements AmazonScrapingService, PagePro
|
||||
// 第一步:清理1小时前的所有旧数据
|
||||
amazonProductRepository.deleteAllDataBefore(LocalDateTime.now().minusHours(1));
|
||||
|
||||
// 优化:批次内复用代理检测和工具实例
|
||||
RakutenProxyUtil proxyUtil = new RakutenProxyUtil();
|
||||
String sampleUrl = buildAmazonUrl(region, "B00000000");
|
||||
var proxyDownloader = proxyUtil.createProxyDownloader(proxyUtil.detectSystemProxy(sampleUrl));
|
||||
|
||||
// 第二步:处理每个ASIN
|
||||
Map<String, AmazonProductEntity> allProducts = new HashMap<>();
|
||||
for (String asin : asinList.stream().distinct().toList()) {
|
||||
@@ -113,9 +118,8 @@ public class AmazonScrapingServiceImpl implements AmazonScrapingService, PagePro
|
||||
} else {
|
||||
String url = buildAmazonUrl(region, cleanAsin);
|
||||
this.site = configureSiteForRegion(region);
|
||||
RakutenProxyUtil proxyUtil = new RakutenProxyUtil();
|
||||
synchronized (spiderLock) {
|
||||
activeSpider = Spider.create(this).addUrl(url).setDownloader(proxyUtil.createProxyDownloader(proxyUtil.detectSystemProxy(url))).thread(1);
|
||||
activeSpider = Spider.create(this).addUrl(url).setDownloader(proxyDownloader).thread(1);
|
||||
activeSpider.run();
|
||||
activeSpider = null;
|
||||
}
|
||||
@@ -152,7 +156,7 @@ public class AmazonScrapingServiceImpl implements AmazonScrapingService, PagePro
|
||||
boolean isUS = "US".equals(region);
|
||||
return Site.me()
|
||||
.setRetryTimes(3)
|
||||
.setSleepTime(3000 + random.nextInt(3000))
|
||||
.setSleepTime(2000 + random.nextInt(2000))
|
||||
.setTimeOut(20000)
|
||||
.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36")
|
||||
.addHeader("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8")
|
||||
|
||||
@@ -3,93 +3,76 @@ import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.qiniu.util.UrlUtils;
|
||||
import org.openqa.selenium.JavascriptExecutor;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.chrome.ChromeDriver;
|
||||
import org.openqa.selenium.chrome.ChromeOptions;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class GenmaiServiceImpl {
|
||||
@Value("${api.server.base-url}")
|
||||
private String serverApiUrl;
|
||||
@Value("${api.server.paths.getGenmaijlToken}")
|
||||
private String genmaijlToken;
|
||||
@Value("${api.server.paths.updateGenmaijlToken}")
|
||||
private String updateGenmaijlToken;
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
public void openGenmaiWebsite() {
|
||||
try {
|
||||
// 先关闭所有Chrome进程
|
||||
Runtime.getRuntime().exec("taskkill /f /im chrome.exe");
|
||||
Thread.sleep(1000); // 等待进程关闭
|
||||
|
||||
ChromeOptions options = new ChromeOptions();
|
||||
String username = System.getProperty("user.name", "user");
|
||||
String safeUsername;
|
||||
char firstChar = username.charAt(0);
|
||||
char flippedFirstChar = Character.isUpperCase(firstChar) ? Character.toLowerCase(firstChar) : Character.toUpperCase(firstChar);
|
||||
safeUsername = flippedFirstChar + username.substring(1);
|
||||
String chromeUserData = System.getProperty("user.home").replace(username, UrlUtils.urlEncode(safeUsername))
|
||||
+ "\\AppData\\Local\\Google\\Chrome\\User Data";
|
||||
options.addArguments("user-data-dir=" + chromeUserData.toLowerCase());
|
||||
options.addArguments("profile-directory=Default");
|
||||
|
||||
WebDriver driver = new ChromeDriver(options);
|
||||
driver.get("https://www.genmaijl.com/#/profile");
|
||||
JavascriptExecutor js = (JavascriptExecutor) driver;
|
||||
js.executeScript(String.format("localStorage.setItem('token','%s')", checkTokenExpired()));
|
||||
driver.navigate().refresh();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("打开跟卖精灵失败,请确保已安装Chrome浏览器", e);
|
||||
}
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public void openGenmaiWebsite(Long accountId) throws Exception {
|
||||
String token = getAndValidateToken(accountId);
|
||||
Runtime.getRuntime().exec("taskkill /f /im chrome.exe");
|
||||
Thread.sleep(1000);
|
||||
|
||||
ChromeOptions options = new ChromeOptions();
|
||||
String username = System.getProperty("user.name");
|
||||
char firstChar = username.charAt(0);
|
||||
char flipped = Character.isUpperCase(firstChar) ? Character.toLowerCase(firstChar) : Character.toUpperCase(firstChar);
|
||||
String chromeUserData = System.getProperty("user.home")
|
||||
.replace(username, UrlUtils.urlEncode(flipped + username.substring(1)))
|
||||
+ "\\AppData\\Local\\Google\\Chrome\\User Data";
|
||||
options.addArguments("user-data-dir=" + chromeUserData.toLowerCase(), "profile-directory=Default");
|
||||
|
||||
ChromeDriver driver = new ChromeDriver(options);
|
||||
driver.get("https://www.genmaijl.com/#/profile");
|
||||
((JavascriptExecutor) driver).executeScript("localStorage.setItem('token','" + token + "')");
|
||||
driver.navigate().refresh();
|
||||
}
|
||||
/**
|
||||
* 获取token验证token是否过期
|
||||
*/
|
||||
public String checkTokenExpired() {
|
||||
ResponseEntity<String> exchange = restTemplate.exchange(serverApiUrl+genmaijlToken, HttpMethod.GET, null, String.class);
|
||||
String token = exchange.getBody();
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Token", token);
|
||||
HttpEntity<String> entity = new HttpEntity<>(null, headers);
|
||||
try {
|
||||
ResponseEntity<String> response = restTemplate.exchange("https://www.genmaijl.com/manage/user/balance", HttpMethod.GET, entity, String.class);
|
||||
String body = response.getBody();
|
||||
JsonNode root = objectMapper.readTree(body);
|
||||
return root.get("code").asInt() == 0 ? token : login();
|
||||
} catch (Exception e) {
|
||||
return login();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private String getAndValidateToken(Long accountId) {
|
||||
Map<String, Object> resp = restTemplate.getForObject(serverApiUrl + "/tool/genmai/accounts", Map.class);
|
||||
List<Map<String, Object>> list = (List<Map<String, Object>>) resp.get("data");
|
||||
|
||||
Map<String, Object> account = accountId != null
|
||||
? list.stream().filter(m -> accountId.equals(((Number) m.get("id")).longValue())).findFirst().orElse(list.get(0))
|
||||
: list.get(0);
|
||||
String token = (String) account.get("token");
|
||||
Long id = ((Number) account.get("id")).longValue();
|
||||
// 验证token是否有效
|
||||
if (!validateToken(token)) {
|
||||
// token无效,调用服务器刷新
|
||||
restTemplate.postForObject(serverApiUrl + "/tool/genmai/accounts/" + id + "/validate", null, Map.class);
|
||||
// 重新获取token
|
||||
resp = restTemplate.getForObject(serverApiUrl + "/tool/genmai/accounts", Map.class);
|
||||
list = (List<Map<String, Object>>) resp.get("data");
|
||||
account = list.stream().filter(m -> id.equals(((Number) m.get("id")).longValue())).findFirst().orElse(null);
|
||||
token = (String) account.get("token");
|
||||
}
|
||||
return token;
|
||||
}
|
||||
/**
|
||||
* 登录
|
||||
*/
|
||||
public String login() {
|
||||
|
||||
private boolean validateToken(String token) {
|
||||
try {
|
||||
Map<String, String> requestBody = new HashMap<>();
|
||||
requestBody.put("nickname", "changzhu-4");
|
||||
requestBody.put("password", "123456QWe@");
|
||||
HttpEntity<Map<String, String>> entity = new HttpEntity<>(requestBody);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.TEXT_PLAIN);
|
||||
ResponseEntity<String> response = restTemplate.exchange("https://www.genmaijl.com/manage/user/subLogin", HttpMethod.POST, entity, String.class);
|
||||
String body = response.getBody();
|
||||
JsonNode root = objectMapper.readTree(body);
|
||||
requestBody.clear();
|
||||
String token = root.get("result").asText();
|
||||
HttpEntity<String> updateEntity = new HttpEntity<>(token, headers);
|
||||
ResponseEntity<String> exchange = restTemplate.exchange(serverApiUrl + updateGenmaijlToken, HttpMethod.POST, updateEntity, String.class);
|
||||
System.out.println(exchange.getBody());
|
||||
return token;
|
||||
headers.set("Token", token);
|
||||
ResponseEntity<String> response = restTemplate.exchange(
|
||||
"https://www.genmaijl.com/manage/user/balance",
|
||||
HttpMethod.GET, new HttpEntity<>(headers), String.class);
|
||||
JsonNode root = objectMapper.readTree(response.getBody());
|
||||
return root.get("code").asInt() == 0;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("跟卖精灵登录失败", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,8 +53,6 @@ api:
|
||||
data: "/monitor/client/api/data"
|
||||
alibaba1688: "/monitor/client/api/alibaba1688"
|
||||
version: "/system/version/check"
|
||||
getGenmaijlToken: "/getToken"
|
||||
updateGenmaijlToken: "/saveToken"
|
||||
# 项目信息配置
|
||||
project:
|
||||
version: @project.version@
|
||||
|
||||
Reference in New Issue
Block a user