diff --git a/xianyu-core/src/main/java/top/biwin/xianyu/core/entity/GoofishAccountEntity.java b/xianyu-core/src/main/java/top/biwin/xianyu/core/entity/GoofishAccountEntity.java
index 60abc91..3dd4940 100644
--- a/xianyu-core/src/main/java/top/biwin/xianyu/core/entity/GoofishAccountEntity.java
+++ b/xianyu-core/src/main/java/top/biwin/xianyu/core/entity/GoofishAccountEntity.java
@@ -52,7 +52,7 @@ public class GoofishAccountEntity {
private String username;
@ColumnDefault("''")
- private String nickName;
+ private String nickname;
@ColumnDefault("''")
private String password;
diff --git a/xianyu-goofish/pom.xml b/xianyu-goofish/pom.xml
index 02b5d64..685427a 100644
--- a/xianyu-goofish/pom.xml
+++ b/xianyu-goofish/pom.xml
@@ -37,9 +37,17 @@
com.google.zxing
javase
+
+ com.google.guava
+ guava
+
org.projectlombok
lombok
+
+ cn.hutool
+ hutool-all
+
\ No newline at end of file
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/qrcode/QrLoginService.java
index c8c9184..d3d53ff 100644
--- a/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/qrcode/QrLoginService.java
+++ b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/qrcode/QrLoginService.java
@@ -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 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);
}
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 16c6f2d..218d40d 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
@@ -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
}
}
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
new file mode 100644
index 0000000..f756e32
--- /dev/null
+++ b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/GoofishApiService.java
@@ -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> 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"));
+ }
+
+
+}
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/service/api/GetAccountApi.java
new file mode 100644
index 0000000..4d6f7ad
--- /dev/null
+++ b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/api/GetAccountApi.java
@@ -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 {
+ @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");
+ }
+
+ }
+ }
+}
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/service/api/GoofishAbstractApi.java
new file mode 100644
index 0000000..324c28d
--- /dev/null
+++ b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/api/GoofishAbstractApi.java
@@ -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 implements GoofishApi {
+ @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 buildQueryParams(String cookieStr, String dataStr) {
+ Map 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);
+ }
+}
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/service/api/GoofishApi.java
new file mode 100644
index 0000000..3efa139
--- /dev/null
+++ b/xianyu-goofish/src/main/java/top/biwin/xianyu/goofish/service/api/GoofishApi.java
@@ -0,0 +1,18 @@
+package top.biwin.xianyu.goofish.service.api;
+
+/**
+ * TODO
+ *
+ * @author wangli
+ * @since 2026-01-25 17:16
+ */
+public interface GoofishApi {
+ String getName();
+
+ String getApi();
+
+ String getVersion();
+
+ T call(String goofishId, String cookie, String dataStr);
+
+}
diff --git a/xianyu-server/src/main/resources/application.yml b/xianyu-server/src/main/resources/application.yml
index e70d938..d626f0c 100644
--- a/xianyu-server/src/main/resources/application.yml
+++ b/xianyu-server/src/main/resources/application.yml
@@ -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'