init
This commit is contained in:
parent
936a511b79
commit
b6c2f7407b
@ -52,7 +52,7 @@ public class GoofishAccountEntity {
|
||||
private String username;
|
||||
|
||||
@ColumnDefault("''")
|
||||
private String nickName;
|
||||
private String nickname;
|
||||
|
||||
@ColumnDefault("''")
|
||||
private String password;
|
||||
|
||||
@ -37,9 +37,17 @@
|
||||
<groupId>com.google.zxing</groupId>
|
||||
<artifactId>javase</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -22,6 +22,7 @@ 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;
|
||||
@ -49,6 +50,8 @@ public class QrLoginService {
|
||||
@Autowired
|
||||
private BrowserService browserService;
|
||||
@Autowired
|
||||
private GoofishApiService goofishApiService;
|
||||
@Autowired
|
||||
private OkHttpClient okHttpClient;
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
@ -91,7 +94,6 @@ public class QrLoginService {
|
||||
log.info("【QR Login】Got login params for session: {}", sessionId);
|
||||
|
||||
// 3. Generate QR Code Data
|
||||
// Construct URL: https://passport.goofish.com/newlogin/qrcode/generate.do
|
||||
HttpUrl.Builder urlBuilder = HttpUrl.parse("https://passport.goofish.com/newlogin/qrcode/generate.do").newBuilder();
|
||||
for (Map.Entry<String, String> entry : loginParams.entrySet()) {
|
||||
urlBuilder.addQueryParameter(entry.getKey(), entry.getValue());
|
||||
@ -702,7 +704,7 @@ public class QrLoginService {
|
||||
accountId
|
||||
);
|
||||
|
||||
if (verifiedCookies != null && !verifiedCookies.isEmpty()) {
|
||||
if (!verifiedCookies.isEmpty()) {
|
||||
log.info("【QR Login】浏览器验证成功,获取到真实Cookie,数量: {}", verifiedCookies.size());
|
||||
|
||||
// 将Cookie转换为字符串
|
||||
@ -724,8 +726,8 @@ public class QrLoginService {
|
||||
goofishAccount.setCookie(finalCookieStr);
|
||||
|
||||
if (isNewAccount) {
|
||||
goofishAccount.setNickName(verifiedCookies.getOrDefault("nickName", ""));
|
||||
goofishAccount.setUsername("TB_" + unb);
|
||||
goofishAccount.setNickname(verifiedCookies.getOrDefault("nickName", ""));
|
||||
goofishAccount.setUsername(goofishApiService.getAccount(accountId, finalCookieStr));
|
||||
goofishAccount.setPassword("QR_LOGIN");
|
||||
goofishAccount.setUserId(userId);
|
||||
}
|
||||
|
||||
@ -170,7 +170,7 @@ public class BrowserService {
|
||||
log.error("【QR Login】Error creating browser context for verification", e);
|
||||
}
|
||||
|
||||
return null; // Failed
|
||||
return new HashMap<>(); // Failed
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
package top.biwin.xianyu.goofish.service;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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 javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author wangli
|
||||
* @since 2026-01-25 15:59
|
||||
*/
|
||||
@Component
|
||||
@Data
|
||||
public class GoofishApiService {
|
||||
@Autowired
|
||||
private List<GoofishApi<?>> apis;
|
||||
@Autowired
|
||||
private GoofishAccountRepository goofishAccountRepository;
|
||||
|
||||
/**
|
||||
* 获取账号
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getAccount(String goofishId, @Nullable String cookieStr) {
|
||||
GoofishApi<?> goofishApi = getApi("getAccount");
|
||||
if (!StringUtils.hasText(cookieStr)) {
|
||||
cookieStr = goofishAccountRepository.getReferenceById(goofishId).getCookie();
|
||||
}
|
||||
return (String) goofishApi.call(goofishId, cookieStr, "{}");
|
||||
}
|
||||
|
||||
private GoofishApi getApi(String apiName) {
|
||||
if (CollectionUtils.isEmpty(apis)) {
|
||||
throw new IllegalStateException("未初始化闲鱼 API");
|
||||
}
|
||||
return apis.stream().filter(i -> Objects.equals(i.getName(), apiName)).findFirst().orElseThrow(() -> new IllegalStateException("未找到指定 API"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,100 @@
|
||||
package top.biwin.xianyu.goofish.service.api;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 获取闲鱼的账号
|
||||
*
|
||||
* @author wangli
|
||||
* @since 2026-01-25 17:20
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class GetAccountApi extends GoofishAbstractApi<String> {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "getAccount";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getApi() {
|
||||
return "mtop.idle.web.user.page.account";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return "1.0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String call(String goofishId, String cookieStr, String dataStr) {
|
||||
String apiUrl = buildApiUrl() + HttpUtil.toParams(buildQueryParams(cookieStr, dataStr));
|
||||
|
||||
try (HttpResponse response = HttpRequest.post(apiUrl)
|
||||
.header("Cookie", cookieStr)
|
||||
.body("data=" + URLEncoder.encode(dataStr, StandardCharsets.UTF_8))
|
||||
.execute()) {
|
||||
String body = response.body();
|
||||
log.info("【{}】Auto confirm response: {}", goofishId, body);
|
||||
|
||||
JSONObject json = JSONUtil.parseObj(body);
|
||||
if (json.containsKey("ret")) {
|
||||
JSONArray ret = json.getJSONArray("ret");
|
||||
if (ret != null && !ret.isEmpty() && ret.getStr(0).startsWith("SUCCESS")) {
|
||||
return json.getJSONObject("data").getJSONObject("module").getJSONObject("base").getStr("displayNick");
|
||||
}
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("获取闲鱼账号异常:{}", e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
String jsonStr = "{\n" +
|
||||
" \"api\": \"mtop.idle.web.user.page.account\",\n" +
|
||||
" \"data\": {\n" +
|
||||
" \"module\": {\n" +
|
||||
" \"certify\": {\n" +
|
||||
" \"realPersonCertified\": true,\n" +
|
||||
" \"alipayCertified\": true,\n" +
|
||||
" \"xianyuCertified\": true\n" +
|
||||
" },\n" +
|
||||
" \"base\": {\n" +
|
||||
" \"displayNick\": \"半夏落地\"\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"needDecryptKeys\": [\n" +
|
||||
" \"baseInfo.encryptedUserId\"\n" +
|
||||
" ]\n" +
|
||||
" },\n" +
|
||||
" \"ret\": [\n" +
|
||||
" \"SUCCESS::调用成功\"\n" +
|
||||
" ],\n" +
|
||||
" \"traceId\": \"2150444f17693348617105274e29cb\",\n" +
|
||||
" \"v\": \"1.0\"\n" +
|
||||
"}";
|
||||
|
||||
|
||||
JSONObject json = JSONUtil.parseObj(jsonStr);
|
||||
if (json.containsKey("ret")) {
|
||||
JSONArray ret = json.getJSONArray("ret");
|
||||
if (ret != null && !ret.isEmpty() && ret.getStr(0).startsWith("SUCCESS")) {
|
||||
json.getJSONObject("data").getJSONObject("module").getJSONObject("base").getStr("displayNick");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
package top.biwin.xianyu.goofish.service.api;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author wangli
|
||||
* @since 2026-01-25 17:16
|
||||
*/
|
||||
public abstract class GoofishAbstractApi<T> implements GoofishApi<T> {
|
||||
@Value("${goofish.api.appKey:34839810}")
|
||||
private String appKey;
|
||||
@Value("${goofish.api.hostUrl:https://h5api.m.goofish.com/h5/}")
|
||||
private String apiHostUrl;
|
||||
|
||||
protected final String buildApiUrl() {
|
||||
return apiHostUrl + getApi() + "/" + getVersion() + "/";
|
||||
}
|
||||
|
||||
protected Map<String, String> buildQueryParams(String cookieStr, String dataStr) {
|
||||
Map<String, String> api_query_params = new HashMap<>();
|
||||
api_query_params.put("jsv", "2.7.2");
|
||||
api_query_params.put("appKey", appKey);
|
||||
String t = String.valueOf(System.currentTimeMillis());
|
||||
api_query_params.put("t", t);
|
||||
api_query_params.put("sign", generateSign(t, getApiToken(cookieStr), dataStr));
|
||||
api_query_params.put("v", "1.0");
|
||||
api_query_params.put("type", "originaljson");
|
||||
api_query_params.put("accountSite", "xianyu");
|
||||
api_query_params.put("dataType", "json");
|
||||
api_query_params.put("timeout", "20000");
|
||||
api_query_params.put("api", "");
|
||||
api_query_params.put("sessionOption", "AutoLoginOnly");
|
||||
api_query_params.put("spm_cnt", "a21ybx.account.0.0");
|
||||
return api_query_params;
|
||||
}
|
||||
|
||||
private String getApiToken(String cookieStr) {
|
||||
if (!StringUtils.hasText(cookieStr)) {
|
||||
throw new IllegalArgumentException("缺少 Cookie 数据");
|
||||
}
|
||||
String token = "";
|
||||
String[] parts = cookieStr.split(";");
|
||||
for (String part : parts) {
|
||||
String[] kv = part.trim().split("=");
|
||||
if (kv.length > 0 && "_m_h5_tk".equals(kv[0])) {
|
||||
if (kv.length > 1) {
|
||||
token = kv[1].split("_")[0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
private String generateSign(String millisTimeStampStr, String token, String data) {
|
||||
String src = token + "&" + millisTimeStampStr + "&" + appKey + "&" + data;
|
||||
return SecureUtil.md5(src);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package top.biwin.xianyu.goofish.service.api;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author wangli
|
||||
* @since 2026-01-25 17:16
|
||||
*/
|
||||
public interface GoofishApi<T> {
|
||||
String getName();
|
||||
|
||||
String getApi();
|
||||
|
||||
String getVersion();
|
||||
|
||||
T call(String goofishId, String cookie, String dataStr);
|
||||
|
||||
}
|
||||
@ -62,5 +62,10 @@ jwt:
|
||||
# 7 天 = 604,800,000 毫秒
|
||||
refresh-token-expiration: 604800000
|
||||
|
||||
goofish:
|
||||
api:
|
||||
hostUrl: https://h5api.m.goofish.com/h5/
|
||||
appKey: 34839810
|
||||
|
||||
browser:
|
||||
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'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user