feat(genmai): 集成跟卖精灵账号管理系统跟

- 新增卖精灵账号管理功能,支持多账号切换
- 实现账号增删改查接口与前端交互逻辑
-优化打开跟卖精灵流程,增加账号选择界面
- 添加账号权限限制与订阅升级提醒
- 完善后端账号实体类及数据库映射
- 更新系统控制器以支持指定账号启动功能- 调整HTTP请求路径适配新工具模块路由- 升级客户端版本至2.5.3并优化代码结构
This commit is contained in:
2025-10-27 09:13:00 +08:00
parent 35c9fc205a
commit 0be60bc103
18 changed files with 720 additions and 132 deletions

View File

@@ -10,7 +10,7 @@
</parent>
<groupId>com.tashow.erp</groupId>
<artifactId>erp_client_sb</artifactId>
<version>2.5.2</version>
<version>2.5.3</version>
<name>erp_client_sb</name>
<description>erp客户端</description>
<properties>

View File

@@ -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);

View File

@@ -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")

View File

@@ -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;
}
}
}

View File

@@ -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@