init
This commit is contained in:
parent
a6eedd9001
commit
e7c1288ab2
17
API.md
17
API.md
@ -1,17 +0,0 @@
|
||||
# 获取会员名
|
||||
curl 'https://h5api.m.goofish.com/h5/mtop.idle.web.user.page.account/1.0/?jsv=2.7.2&appKey=34839810&t=1769270729440&sign=02c196fa1bf3d9797d63374dbd942843&v=1.0&type=originaljson&accountSite=xianyu&dataType=json&timeout=20000&api=mtop.idle.web.user.page.account&sessionOption=AutoLoginOnly&spm_cnt=a21ybx.account.0.0&spm_pre=a21ybx.personal.menu.6.1b866ac226jKae&log_id=1b866ac226jKae' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'accept-language: zh-CN,zh;q=0.9' \
|
||||
-H 'content-type: application/x-www-form-urlencoded' \
|
||||
-b 'cna=9P3pIdIP/CsCASSqLXsBph0X; t=81cea1923c0f7c1c6acd493e8fb98440; cookie2=1865e191ee74187929f741a5b527cb43; mtop_partitioned_detect=1; _m_h5_tk=634d547c63abefec9bd902b069faa5bb_1769276924644; _m_h5_tk_enc=7b76e180c49784d8fc49f098b2ac3967; xlly_s=1; _samesite_flag_=true; _tb_token_=53eda6bad563a; tracknick=%E5%8D%8A%E5%A4%8F%E8%90%BD%E5%9C%B0; unb=2045669855; sdkSilent=1769354710900; sgcookie=E100MpVcyQgT5YDPe3ISBbg8t7xrPjLAJMVTGTLnZHR1TPlLwGYotQYxGlZrUE%2FzSToFWstgiPsiQtbqRyDuqfyd8I64zsZ%2BOIsTKDMKFEAeuiQ%3D; csg=019ee131; havana_lgc2_77=eyJoaWQiOjIwNDU2Njk4NTUsInNnIjoiNjg0OWZmY2E1OTQ5YjVlNjk1MjZmMDhlMWYwNDNhNWEiLCJzaXRlIjo3NywidG9rZW4iOiIxOXZjMkZtWFMtSFFRenJfRnJ1dVpUZyJ9; _hvn_lgc_=77; havana_lgc_exp=1771861120777; tfstk=gPEKdSYz0OppePS1MHbGZDty-r6GoN2UtWyXqbcHP5FTh-RnPkm3wUF3U705TDq867VzxXD3EY9UXSKkx67UF3noVsfcmi2eL0o5iFsIYYcEURG5I2TyIYBnVsfcjFvs86m7K32g7Rws_YHWR3NSffMx13T7NXg61xHqVbN7NCTsEvKBP4gQCOHr1biSV7i1BYcsN0i7NRTRgNhyRjx8-Opp4gDK63t7XA1rGJhiQArtp2hj5fK7IlHKJjwQc_51pYUbD2PfsecQkzVxnWj6vSeQ9rgbAC1t4R4YwxEChEh8TRZn5k6kuAozRriQ2sdQCqE_xDzOMncb78Z-5J5vuA47nk48t1-Stre_6qqMsnm_vWEIWmsrSoqYdROmMYYCBOLyz2MaZQZCf3oTiyktiODHzUuO7AhcB_8yz2iKBjXMRU8rWrC..' \
|
||||
-H 'origin: https://www.goofish.com' \
|
||||
-H 'priority: u=1, i' \
|
||||
-H 'referer: https://www.goofish.com/' \
|
||||
-H 'sec-ch-ua: "Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"' \
|
||||
-H 'sec-ch-ua-mobile: ?0' \
|
||||
-H 'sec-ch-ua-platform: "macOS"' \
|
||||
-H 'sec-fetch-dest: empty' \
|
||||
-H 'sec-fetch-mode: cors' \
|
||||
-H 'sec-fetch-site: same-site' \
|
||||
-H 'user-agent: 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' \
|
||||
--data-raw 'data=%7B%7D'
|
||||
@ -0,0 +1,17 @@
|
||||
package top.biwin.xinayu.common.dto.request;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author wangli
|
||||
* @since 2026-02-03 23:19
|
||||
*/
|
||||
@Data
|
||||
public class FetchRemoteAllProductRequest {
|
||||
|
||||
@JsonProperty("goofish_id")
|
||||
private String goofishId;
|
||||
}
|
||||
@ -3,6 +3,7 @@ package top.biwin.xinayu.common.dto.response;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
@ -12,6 +13,7 @@ import lombok.experimental.SuperBuilder;
|
||||
* @author wangli
|
||||
* @since 2026-01-22 23:50
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@SuperBuilder
|
||||
public class CheckDefaultPwdResponse extends BaseResponse {
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
package top.biwin.xinayu.common.dto.response;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author wangli
|
||||
* @since 2026-02-03 23:21
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@SuperBuilder
|
||||
public class FetchRemoteAllProductResponse extends BaseResponse {
|
||||
|
||||
private Integer totalCount;
|
||||
private Integer totalPages;
|
||||
private Integer savedCount;
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package top.biwin.xinayu.common.dto.response;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author wangli
|
||||
* @since 2026-02-03 23:34
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class GetRemoteProductApiVo{
|
||||
private Integer totalCount;
|
||||
private Integer totalPages;
|
||||
private Integer totalSaved;
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
package top.biwin.xianyu.core.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.ColumnDefault;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "products")
|
||||
public class ProductEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "goofish_id", nullable = false)
|
||||
@JsonProperty("goofish_id")
|
||||
private String goofishId;
|
||||
|
||||
@Column(name = "goofish_product_id", nullable = false)
|
||||
@JsonProperty("goofish_product_id")
|
||||
private String goofishProductId;
|
||||
|
||||
@Column(name = "product_title")
|
||||
@JsonProperty("product_title")
|
||||
private String productTitle;
|
||||
|
||||
@Column(name = "product_description", columnDefinition = "TEXT")
|
||||
@JsonProperty("product_description")
|
||||
private String productDescription;
|
||||
|
||||
@Column(name = "product_category")
|
||||
@JsonProperty("product_category")
|
||||
private String productCategory;
|
||||
|
||||
@Column(name = "product_price")
|
||||
@JsonProperty("product_price")
|
||||
private String productPrice;
|
||||
|
||||
@Column(name = "product_detail", columnDefinition = "TEXT")
|
||||
@JsonProperty("product_detail")
|
||||
private String productDetail;
|
||||
|
||||
@Column(name = "is_multi_spec")
|
||||
@ColumnDefault("false")
|
||||
@JsonProperty("is_multi_spec")
|
||||
private Boolean isMultiSpec = false;
|
||||
|
||||
@Column(name = "multi_quantity_delivery")
|
||||
@ColumnDefault("false")
|
||||
@JsonProperty("multi_quantity_delivery")
|
||||
private Boolean multiQuantityDelivery = false;
|
||||
|
||||
@Column(name = "user_id")
|
||||
@JsonProperty("user_id")
|
||||
private Long userId;
|
||||
|
||||
@CreationTimestamp
|
||||
@Column(name = "created_at", updatable = false)
|
||||
@JsonProperty("created_at")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@UpdateTimestamp
|
||||
@Column(name = "updated_at")
|
||||
@JsonProperty("updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@ -4,12 +4,13 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import top.biwin.xianyu.core.entity.GoofishAccountEntity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface GoofishAccountRepository extends JpaRepository<GoofishAccountEntity, String> {
|
||||
|
||||
Optional<GoofishAccountEntity> findByUserId(Long UserId);
|
||||
List<GoofishAccountEntity> findByUserId(Long UserId);
|
||||
|
||||
long countByEnabled(Boolean enabled);
|
||||
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
package top.biwin.xianyu.core.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import top.biwin.xianyu.core.entity.ProductEntity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface ProductRepository extends JpaRepository<ProductEntity, Long> {
|
||||
List<ProductEntity> findByUserId(Long userId);
|
||||
|
||||
List<ProductEntity> findByGoofishId(String goofishId);
|
||||
}
|
||||
@ -2,6 +2,7 @@ package top.biwin.xianyu.goofish.api;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -20,10 +21,10 @@ public abstract class GoofishAbstractApi<T> implements GoofishApi<T> {
|
||||
private String apiHostUrl;
|
||||
|
||||
protected final String buildApiUrl() {
|
||||
return apiHostUrl + getApi() + "/" + getVersion() + "/?";
|
||||
return apiHostUrl + getApi() + "/" + getVersion() + "/?";
|
||||
}
|
||||
|
||||
protected Map<String, String> buildQueryParams(String cookieStr, String dataStr) {
|
||||
protected Map<String, String> buildQueryParams(String cookieStr, String dataStr, Map<String, String> overrideMap) {
|
||||
Map<String, String> api_query_params = new HashMap<>();
|
||||
api_query_params.put("jsv", "2.7.2");
|
||||
api_query_params.put("appKey", appKey);
|
||||
@ -38,9 +39,17 @@ public abstract class GoofishAbstractApi<T> implements GoofishApi<T> {
|
||||
api_query_params.put("api", "");
|
||||
api_query_params.put("sessionOption", "AutoLoginOnly");
|
||||
api_query_params.put("spm_cnt", "a21ybx.account.0.0");
|
||||
|
||||
if (!CollectionUtils.isEmpty(overrideMap)) {
|
||||
api_query_params.putAll(overrideMap);
|
||||
}
|
||||
return api_query_params;
|
||||
}
|
||||
|
||||
protected Map<String, String> buildQueryParams(String cookieStr, String dataStr) {
|
||||
return buildQueryParams(cookieStr, dataStr, null);
|
||||
}
|
||||
|
||||
private String getApiToken(String cookieStr) {
|
||||
if (!StringUtils.hasText(cookieStr)) {
|
||||
throw new IllegalArgumentException("缺少 Cookie 数据");
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package top.biwin.xianyu.goofish.api;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
@ -13,6 +15,6 @@ public interface GoofishApi<T> {
|
||||
|
||||
String getVersion();
|
||||
|
||||
T call(String goofishId, String cookieStr, String dataStr);
|
||||
T call(String goofishId, String cookieStr, Map<String,Object> dataMap);
|
||||
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import top.biwin.xianyu.goofish.api.GoofishAbstractApi;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 获取闲鱼的账号
|
||||
@ -38,7 +39,8 @@ public class GetAccountApi extends GoofishAbstractApi<String> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String call(String goofishId, String cookieStr, String dataStr) {
|
||||
public String call(String goofishId, String cookieStr, Map<String,Object> data) {
|
||||
String dataStr = JSONUtil.toJsonStr(data);
|
||||
String apiUrl = buildApiUrl() + HttpUtil.toParams(buildQueryParams(cookieStr, dataStr));
|
||||
log.debug("【{}】获取账号名 ApiUrl: {}", goofishId, apiUrl);
|
||||
log.debug("【{}】获取账号名时使用的 Cookie 为: {}", goofishId, cookieStr);
|
||||
|
||||
@ -0,0 +1,149 @@
|
||||
package top.biwin.xianyu.goofish.api.impl;
|
||||
|
||||
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 top.biwin.xianyu.goofish.api.GoofishAbstractApi;
|
||||
import top.biwin.xinayu.common.dto.response.GetRemoteProductApiVo;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author wangli
|
||||
* @since 2026-02-03 23:28
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class GetAllItemsApi extends GoofishAbstractApi<GetRemoteProductApiVo> {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "getAllItems";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getApi() {
|
||||
return "mtop.idle.web.xyh.item.list";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return "1.0";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param goofishId
|
||||
* @param cookieStr
|
||||
* @param data {"needGroupInfo":false,"pageNumber":1,"userId":"450737657","pageSize":20,"groupName":"在售","groupId":66679630,"defaultGroup":true}
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public GetRemoteProductApiVo call(String goofishId, String cookieStr, Map<String, Object> data) {
|
||||
String dataStr = JSONUtil.toJsonStr(data);
|
||||
String apiUrl = buildApiUrl() + HttpUtil.toParams(buildQueryParams(cookieStr, dataStr, selfMap()));
|
||||
log.debug("【{}】获取闲鱼商品数据 ApiUrl: {}", goofishId, apiUrl);
|
||||
log.debug("【{}】获取闲鱼商品数据时使用的 Cookie 为: {}", goofishId, cookieStr);
|
||||
|
||||
GetRemoteProductApiVo.GetRemoteProductApiVoBuilder builder = GetRemoteProductApiVo.builder();
|
||||
try (HttpResponse response = HttpRequest.post(apiUrl)
|
||||
.header("Cookie", cookieStr)
|
||||
.header("content-type", "application/x-www-form-urlencoded")
|
||||
.header("priority", "u=1, i")
|
||||
.header("dnt", "1")
|
||||
.header("user-agent", "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")
|
||||
.body("data=" + URLEncoder.encode(dataStr, StandardCharsets.UTF_8))
|
||||
.execute()) {
|
||||
String body = response.body();
|
||||
log.info("【{}】获取闲鱼商品数据时,服务端返回的完整响应为: {}", 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")) {
|
||||
JSONObject itemsData = json.getJSONObject("data");
|
||||
JSONArray cardList = itemsData.getJSONArray("cardList");
|
||||
|
||||
// 解析商品信息
|
||||
java.util.List<Map<String, Object>> itemsList = new java.util.ArrayList<>();
|
||||
if (cardList != null) {
|
||||
for (int i = 0; i < cardList.size(); i++) {
|
||||
JSONObject card = cardList.getJSONObject(i);
|
||||
JSONObject cardData = card.getJSONObject("cardData");
|
||||
if (cardData != null) {
|
||||
Map<String, Object> itemInfo = new HashMap<>();
|
||||
itemInfo.put("id", cardData.getStr("id"));
|
||||
itemInfo.put("title", cardData.getStr("title"));
|
||||
|
||||
JSONObject priceInfo = cardData.getJSONObject("priceInfo");
|
||||
if (priceInfo != null) {
|
||||
itemInfo.put("price", priceInfo.getStr("price"));
|
||||
String priceText = (priceInfo.getStr("preText") != null ? priceInfo.getStr("preText") : "") +
|
||||
(priceInfo.getStr("price") != null ? priceInfo.getStr("price") : "");
|
||||
itemInfo.put("price_text", priceText);
|
||||
} else {
|
||||
itemInfo.put("price", "");
|
||||
itemInfo.put("price_text", "");
|
||||
}
|
||||
|
||||
itemInfo.put("category_id", cardData.getStr("categoryId"));
|
||||
itemInfo.put("auction_type", cardData.getStr("auctionType"));
|
||||
itemInfo.put("item_status", cardData.getInt("itemStatus"));
|
||||
itemInfo.put("detail_url", cardData.getStr("detailUrl"));
|
||||
itemInfo.put("pic_info", cardData.getJSONObject("picInfo"));
|
||||
itemInfo.put("detail_params", cardData.getJSONObject("detailParams"));
|
||||
itemInfo.put("track_params", cardData.getJSONObject("trackParams"));
|
||||
itemInfo.put("item_label_data", cardData.getJSONObject("itemLabelDataVO"));
|
||||
itemInfo.put("card_type", card.getInt("cardType"));
|
||||
|
||||
itemsList.add(itemInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("【{}】成功获取到 {} 个商品", goofishId, itemsList.size());
|
||||
// 打印商品详细信息到控制台
|
||||
System.out.println("\n" + "=".repeat(80));
|
||||
System.out.println(String.format("📦 账号 %s 的商品列表 (第%d页,%d 个商品)", goofishId, data.get("pageNumber"), itemsList.size()));
|
||||
System.out.println("=".repeat(80));
|
||||
|
||||
for (int i = 0; i < itemsList.size(); i++) {
|
||||
Map<String, Object> item = itemsList.get(i);
|
||||
System.out.println(String.format("\n🔸 商品 %d:", i + 1));
|
||||
System.out.println(String.format(" 商品ID: %s", item.get("id")));
|
||||
System.out.println(String.format(" 商品标题: %s", item.get("title")));
|
||||
System.out.println(String.format(" 价格: %s", item.get("price_text")));
|
||||
System.out.println(String.format(" 分类ID: %s", item.get("category_id")));
|
||||
System.out.println(String.format(" 商品状态: %s", item.get("item_status")));
|
||||
System.out.println(String.format(" 拍卖类型: %s", item.get("auction_type")));
|
||||
System.out.println(String.format(" 详情链接: %s", item.get("detail_url")));
|
||||
}
|
||||
|
||||
System.out.println("\n" + "=".repeat(80));
|
||||
System.out.println("✅ 商品列表获取完成");
|
||||
System.out.println("=".repeat(80));
|
||||
|
||||
// TODO 保存近数据库
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("获取闲鱼商品数据异常: {}", e.getMessage(), e);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Map<String, String> selfMap() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("spm_cnt", "a21ybx.personal.0.0");
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@ -12,6 +12,7 @@ import top.biwin.xianyu.goofish.api.GoofishAbstractApi;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
@ -38,7 +39,8 @@ public class GetDisplayNameApi extends GoofishAbstractApi<String> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String call(String goofishId, String cookieStr, String dataStr) {
|
||||
public String call(String goofishId, String cookieStr, Map<String,Object> data) {
|
||||
String dataStr = JSONUtil.toJsonStr(data);
|
||||
String apiUrl = buildApiUrl() + HttpUtil.toParams(buildQueryParams(cookieStr, dataStr));
|
||||
log.debug("【{}】获取闲鱼昵称 ApiUrl: {}", goofishId, apiUrl);
|
||||
log.debug("【{}】获取闲鱼昵称时使用的 Cookie 为: {}", goofishId, cookieStr);
|
||||
|
||||
@ -12,6 +12,7 @@ import top.biwin.xianyu.goofish.api.GoofishAbstractApi;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
@ -19,8 +20,8 @@ import java.nio.charset.StandardCharsets;
|
||||
* @author wangli
|
||||
* @since 2026-01-29 21:41
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
@Component
|
||||
public class GetUserIdApi extends GoofishAbstractApi<Long> {
|
||||
@Override
|
||||
public String getName() {
|
||||
@ -38,7 +39,8 @@ public class GetUserIdApi extends GoofishAbstractApi<Long> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long call(String goofishId, String cookieStr, String dataStr) {
|
||||
public Long call(String goofishId, String cookieStr, Map<String,Object> data) {
|
||||
String dataStr = JSONUtil.toJsonStr(data);
|
||||
String apiUrl = buildApiUrl() + HttpUtil.toParams(buildQueryParams(cookieStr, dataStr));
|
||||
log.debug("【{}】获取闲鱼用户 ID ApiUrl: {}", goofishId, apiUrl);
|
||||
log.debug("【{}】获取闲鱼用户 ID 时使用的 Cookie 为: {}", goofishId, cookieStr);
|
||||
|
||||
@ -12,6 +12,7 @@ import top.biwin.xianyu.goofish.api.GoofishAbstractApi;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
@ -38,7 +39,8 @@ public class GetWsTokenApi extends GoofishAbstractApi<String> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String call(String goofishId, String cookieStr, String dataStr) {
|
||||
public String call(String goofishId, String cookieStr, Map<String,Object> data) {
|
||||
String dataStr = JSONUtil.toJsonStr(data);
|
||||
String apiUrl = buildApiUrl() + HttpUtil.toParams(buildQueryParams(cookieStr, dataStr));
|
||||
log.debug("【{}】获取闲鱼 WebSocket Token ApiUrl: {}", goofishId, apiUrl);
|
||||
log.debug("【{}】获取闲鱼 WebSocket Token 时使用的 Cookie 为: {}", goofishId, cookieStr);
|
||||
|
||||
@ -5,12 +5,16 @@ 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.entity.GoofishAccountEntity;
|
||||
import top.biwin.xianyu.core.repository.GoofishAccountRepository;
|
||||
import top.biwin.xianyu.goofish.api.GoofishApi;
|
||||
import top.biwin.xianyu.goofish.websocket.WebSocketConfiguration;
|
||||
import top.biwin.xinayu.common.dto.response.GetRemoteProductApiVo;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@ -37,11 +41,9 @@ public class GoofishApiService {
|
||||
public String getAccount(String goofishId, @Nullable String cookieStr) {
|
||||
GoofishApi<?> goofishApi = getApi("getAccount");
|
||||
if (!StringUtils.hasText(cookieStr)) {
|
||||
cookieStr = goofishAccountRepository.findById(goofishId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("无法获取闲鱼用户名,缺少 Cookie 信息"))
|
||||
.getCookie();
|
||||
cookieStr = goofishAccountRepository.findById(goofishId).orElseThrow(() -> new IllegalArgumentException("无法获取闲鱼账号相关信息")).getCookie();
|
||||
}
|
||||
return (String) goofishApi.call(goofishId, cookieStr, "{}");
|
||||
return (String) goofishApi.call(goofishId, cookieStr, new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,31 +56,47 @@ public class GoofishApiService {
|
||||
public Long getUserId(String goofishId, @Nullable String cookieStr) {
|
||||
GoofishApi<?> goofishApi = getApi("getUserId");
|
||||
if (!StringUtils.hasText(cookieStr)) {
|
||||
cookieStr = goofishAccountRepository.findById(goofishId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("无法获取闲鱼用户 ID,缺少 Cookie 信息"))
|
||||
.getCookie();
|
||||
cookieStr = goofishAccountRepository.findById(goofishId).orElseThrow(() -> new IllegalArgumentException("无法获取闲鱼用户 ID,缺少 Cookie 信息")).getCookie();
|
||||
}
|
||||
return (Long) goofishApi.call(goofishId, cookieStr, "{}");
|
||||
return (Long) goofishApi.call(goofishId, cookieStr, new HashMap<>());
|
||||
}
|
||||
|
||||
public String getNickName(String goofishId, @Nullable String cookieStr) {
|
||||
GoofishApi<?> goofishApi = getApi("getDisplayName");
|
||||
if (!StringUtils.hasText(cookieStr)) {
|
||||
cookieStr = goofishAccountRepository.findById(goofishId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("无法获取闲鱼用户 ID,缺少 Cookie 信息"))
|
||||
.getCookie();
|
||||
cookieStr = goofishAccountRepository.findById(goofishId).orElseThrow(() -> new IllegalArgumentException("无法获取闲鱼用户 ID,缺少 Cookie 信息")).getCookie();
|
||||
}
|
||||
return (String) goofishApi.call(goofishId, cookieStr, "{}");
|
||||
return (String) goofishApi.call(goofishId, cookieStr, new HashMap<>());
|
||||
}
|
||||
|
||||
public String getWsToken(String goofishId, @Nullable String cookieStr, String deviceId) {
|
||||
GoofishApi<?> goofishApi = getApi("getWsToken");
|
||||
if (!StringUtils.hasText(cookieStr)) {
|
||||
cookieStr = goofishAccountRepository.findById(goofishId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("无法获取闲鱼用户 ID,缺少 Cookie 信息"))
|
||||
.getCookie();
|
||||
cookieStr = goofishAccountRepository.findById(goofishId).orElseThrow(() -> new IllegalArgumentException("无法获取闲鱼账号相关信息")).getCookie();
|
||||
}
|
||||
return (String) goofishApi.call(goofishId, cookieStr, "{\"appKey\":\"" + webSocketConfiguration.getAppKey() + "\", \"deviceId\":\"" + deviceId + "\"}");
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("appKey", webSocketConfiguration.getAppKey());
|
||||
data.put("deviceId", deviceId);
|
||||
return (String) goofishApi.call(goofishId, cookieStr, data);
|
||||
}
|
||||
|
||||
public GetRemoteProductApiVo getAllItems(String goofishId, @Nullable String cookieStr) {
|
||||
GoofishApi<?> goofishApi = getApi("getAllItems");
|
||||
|
||||
GoofishAccountEntity account = goofishAccountRepository.findById(goofishId).orElseThrow(() -> new IllegalArgumentException("无法获取闲鱼账号相关信息"));
|
||||
if (!StringUtils.hasText(cookieStr)) {
|
||||
cookieStr = account.getCookie();
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("needGroupInfo", false);
|
||||
data.put("pageNumber", 1);
|
||||
data.put("userId", account.getId());
|
||||
data.put("pageSize", 20);
|
||||
data.put("groupName", "在售");
|
||||
data.put("groupId", 66679630);
|
||||
data.put("defaultGroup", true);
|
||||
|
||||
return (GetRemoteProductApiVo) goofishApi.call(goofishId, cookieStr, data);
|
||||
}
|
||||
|
||||
private GoofishApi getApi(String apiName) {
|
||||
|
||||
@ -14,6 +14,7 @@ import org.springframework.stereotype.Component;
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "goofish.websocket")
|
||||
public class WebSocketConfiguration {
|
||||
private Boolean autoConnectOnStartup;
|
||||
private String appKey;
|
||||
private String wsUrl;
|
||||
private Integer tokenRefreshInterval;
|
||||
|
||||
@ -15,7 +15,6 @@ 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;
|
||||
|
||||
/**
|
||||
@ -40,8 +39,14 @@ public class WebSocketStarter {
|
||||
@Autowired
|
||||
@Qualifier("goofishAccountWebSocketExecutor")
|
||||
private ScheduledExecutorService goofishAccountWebSocketExecutor;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
Boolean autoConnectOnStartup = webSocketConfiguration.getAutoConnectOnStartup();
|
||||
if (!Objects.equals(Boolean.TRUE, autoConnectOnStartup)) {
|
||||
log.info("ignore goofish account connect websocket...");
|
||||
return;
|
||||
}
|
||||
log.info("Start all goofish account websocket ...");
|
||||
List<GoofishAccountEntity> accounts = goofishAccountRepository.findAll();
|
||||
accounts.stream()
|
||||
|
||||
@ -20,7 +20,7 @@ public class ThreadPoolConfig {
|
||||
// 创建定时任务线程池
|
||||
return Executors.newScheduledThreadPool(5, r -> {
|
||||
Thread t = new Thread(r);
|
||||
t.setName("GoofishAccountWebSocket-" + t.getId());
|
||||
t.setName("GAWebSocket-" + t.getId());
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
|
||||
@ -53,14 +53,13 @@ public class GoofishAccountController {
|
||||
* 获取所有Cookie的详细信息(包括值和状态)
|
||||
* 对应Python的 get_cookies_details 接口
|
||||
*/
|
||||
@GetMapping("/details")
|
||||
@GetMapping("/accounts")
|
||||
public ResponseEntity<List<GoofishAccountResponse>> getAllGoofishAccountDetails() {
|
||||
List<GoofishAccountEntity> goofishAccountEntities = new ArrayList<>();
|
||||
if (CurrentUserUtil.isSuperAdmin()) {
|
||||
goofishAccountEntities.addAll(goofishAccountRepository.findAll());
|
||||
} else {
|
||||
goofishAccountRepository.findByUserId(CurrentUserUtil.getCurrentUserId())
|
||||
.ifPresent(goofishAccountEntities::add);
|
||||
goofishAccountEntities.addAll(goofishAccountRepository.findByUserId(CurrentUserUtil.getCurrentUserId()));
|
||||
}
|
||||
|
||||
// 构建详细信息响应
|
||||
@ -111,7 +110,7 @@ public class GoofishAccountController {
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/cookies")
|
||||
@PostMapping("/login-by-cookie")
|
||||
public ResponseEntity<BaseResponse> loginByCookie(@RequestBody GoofishAddCookieRequest request) {
|
||||
Long goofishUserId = goofishApiService.getUserId(request.getId(), request.getValue());
|
||||
log.info("尝试利用 cookie 获取闲鱼用户 ID:{}", goofishUserId > 0L ? goofishUserId : "Cookie 失效");
|
||||
@ -138,7 +137,7 @@ public class GoofishAccountController {
|
||||
.build());
|
||||
}
|
||||
|
||||
@DeleteMapping("/cookies/{id}")
|
||||
@DeleteMapping("/account/{id}")
|
||||
public void deleteAccount(@PathVariable("id") String goofishId) {
|
||||
|
||||
GoofishAccountEntity entity;
|
||||
@ -156,7 +155,7 @@ public class GoofishAccountController {
|
||||
goofishAccountRepository.delete(entity);
|
||||
}
|
||||
|
||||
@PutMapping("/cookies/{id}/remark")
|
||||
@PutMapping("/account/{id}/remark")
|
||||
public ResponseEntity<GoofishAccountResponse> updateAccountRemark(@PathVariable("id") String goofishId, @RequestBody AccountUpdateRequest request) {
|
||||
GoofishAccountEntity account = goofishAccountRepository.findById(goofishId).orElseThrow(() -> new IllegalStateException("账号不存在"));
|
||||
if (!CurrentUserUtil.isSuperAdmin()) {
|
||||
@ -180,7 +179,7 @@ public class GoofishAccountController {
|
||||
|
||||
}
|
||||
|
||||
@PutMapping("/cookies/{id}/status")
|
||||
@PutMapping("/account/{id}/status")
|
||||
public ResponseEntity<GoofishAccountResponse> updateAccountStatus(@PathVariable("id") String goofishId, @RequestBody AccountUpdateRequest request) {
|
||||
GoofishAccountEntity account = goofishAccountRepository.findById(goofishId).orElseThrow(() -> new IllegalStateException("账号不存在"));
|
||||
if (!CurrentUserUtil.isSuperAdmin()) {
|
||||
|
||||
@ -0,0 +1,64 @@
|
||||
package top.biwin.xinayu.server.controller;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import top.biwin.xianyu.core.entity.ProductEntity;
|
||||
import top.biwin.xianyu.core.repository.ProductRepository;
|
||||
import top.biwin.xinayu.common.dto.request.FetchRemoteAllProductRequest;
|
||||
import top.biwin.xinayu.common.dto.response.FetchRemoteAllProductResponse;
|
||||
import top.biwin.xinayu.server.util.CurrentUserUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author wangli
|
||||
* @since 2026-02-03 22:42
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping()
|
||||
public class ProductController {
|
||||
@Autowired
|
||||
private ProductRepository productRepository;
|
||||
|
||||
@GetMapping("/products")
|
||||
public ResponseEntity<List<ProductEntity>> getAllProducts() {
|
||||
List<ProductEntity> productEntities = new ArrayList<>();
|
||||
if (CurrentUserUtil.isSuperAdmin()) {
|
||||
productEntities.addAll(productRepository.findAll());
|
||||
} else {
|
||||
productEntities.addAll(productRepository.findByUserId(CurrentUserUtil.getCurrentUserId()));
|
||||
}
|
||||
return ResponseEntity.ok(productEntities);
|
||||
}
|
||||
|
||||
@GetMapping("/products/account/{goofishId}")
|
||||
public ResponseEntity<List<ProductEntity>> getProductByGoofishId(@PathVariable String goofishId) {
|
||||
return ResponseEntity.ok(productRepository.findByGoofishId(goofishId));
|
||||
}
|
||||
|
||||
@PostMapping("/products/get-all-form-account")
|
||||
public ResponseEntity<FetchRemoteAllProductResponse> getAllProductFormXianyu(@RequestBody FetchRemoteAllProductRequest request) {
|
||||
FetchRemoteAllProductResponse.FetchRemoteAllProductResponseBuilder<?, ?> builder = FetchRemoteAllProductResponse.builder();
|
||||
if (!StringUtils.hasText(request.getGoofishId())) {
|
||||
return ResponseEntity.ok(builder
|
||||
.success(false)
|
||||
.message("缺少 goofish_id 参数")
|
||||
.build());
|
||||
}
|
||||
log.info("触发商品同步任务,goofishId: {}", request.getGoofishId());
|
||||
|
||||
return ResponseEntity.ok(FetchRemoteAllProductResponse.builder().build());
|
||||
}
|
||||
}
|
||||
@ -10,7 +10,7 @@ server:
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: xianyu-free
|
||||
name: xianyu-freedom
|
||||
|
||||
datasource:
|
||||
driver-class-name: org.sqlite.JDBC
|
||||
@ -66,6 +66,7 @@ goofish:
|
||||
hostUrl: https://h5api.m.goofish.com/h5/
|
||||
appKey: 34839810
|
||||
websocket:
|
||||
autoConnectOnStartup: false
|
||||
appKey: 444e9908a51d1cb236a27862abc769c9
|
||||
wsUrl: wss://wss-goofish.dingtalk.com/
|
||||
tokenRefreshInterval: 72000
|
||||
|
||||
Loading…
Reference in New Issue
Block a user