feat(erp): 实现按用户地区隔离的最新产品查询逻辑
- 修改AmazonController以支持按用户和区域筛选最新会话数据 - 更新AmazonProductRepository中的findLatestProducts方法,增加region参数实现数据隔离 -优化AmazonScrapingServiceImpl的数据处理流程,增强缓存清理机制 - 调整GenmaiServiceImpl的token验证逻辑并改进Chrome启动配置- 升级系统版本至2.5.5并完善相关依赖管理 - 改进前端设置对话框中关于缓存清理描述的信息准确性 -重构SystemController接口,移除不必要的用户名参数传递- 强化GenmaiAccountController和服务层的安全校验逻辑
This commit is contained in:
@@ -178,7 +178,7 @@ async function resetAllSettings() {
|
||||
async function handleClearCache() {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
'确定要清理客户端缓存吗?将清除所有缓存数据、更新文件及相关状态(不影响登录状态)',
|
||||
'确定要清理客户端缓存吗?将清除本地设备的所有缓存数据、更新文件及相关状态(不影响登录状态)',
|
||||
'确认清理',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
@@ -503,7 +503,7 @@ onMounted(() => {
|
||||
|
||||
<div class="setting-item">
|
||||
<div class="setting-row">
|
||||
<span class="cache-desc">清理应用缓存数据,不影响登录状态</span>
|
||||
<span class="cache-desc">清理本地设备的所有缓存数据,不影响登录状态</span>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</parent>
|
||||
<groupId>com.tashow.erp</groupId>
|
||||
<artifactId>erp_client_sb</artifactId>
|
||||
<version>2.5.3</version>
|
||||
<version>2.5.5</version>
|
||||
<name>erp_client_sb</name>
|
||||
<description>erp客户端</description>
|
||||
<properties>
|
||||
|
||||
@@ -42,7 +42,17 @@ public class AmazonController {
|
||||
@GetMapping("/products/latest")
|
||||
public JsonData getLatestProducts(HttpServletRequest request) {
|
||||
String username = JwtUtil.getUsernameFromRequest(request);
|
||||
List<AmazonProductEntity> products = amazonProductRepository.findLatestProducts(username);
|
||||
List<String> recentSessions = amazonProductRepository.findRecentSessionIds(org.springframework.data.domain.PageRequest.of(0, 1));
|
||||
String latestSession = recentSessions.stream()
|
||||
.filter(sid -> sid != null && sid.startsWith(username + "#"))
|
||||
.findFirst()
|
||||
.orElse("");
|
||||
|
||||
if (latestSession.isEmpty()) {
|
||||
return JsonData.buildSuccess(Map.of("products", List.of(), "total", 0));
|
||||
}
|
||||
|
||||
List<AmazonProductEntity> products = amazonProductRepository.findBySessionIdOrderByCreatedAtDesc(latestSession);
|
||||
return JsonData.buildSuccess(Map.of("products", products, "total", products.size()));
|
||||
}
|
||||
|
||||
|
||||
@@ -83,9 +83,10 @@ public class SystemController {
|
||||
}
|
||||
|
||||
@PostMapping("/genmai/open")
|
||||
public JsonData openGenmaiWebsite(@RequestParam(required = false) Long accountId) {
|
||||
public JsonData openGenmaiWebsite(@RequestParam(required = false) Long accountId, HttpServletRequest request) {
|
||||
try {
|
||||
genmaiService.openGenmaiWebsite(accountId);
|
||||
String username = com.tashow.erp.utils.JwtUtil.getUsernameFromRequest(request);
|
||||
genmaiService.openGenmaiWebsite(accountId, username);
|
||||
return JsonData.buildSuccess("跟卖精灵已打开");
|
||||
} catch (Exception e) {
|
||||
logger.error("打开跟卖精灵失败", e);
|
||||
@@ -130,9 +131,8 @@ public class SystemController {
|
||||
}
|
||||
|
||||
@PostMapping("/cache/clear")
|
||||
public JsonData clearCache(HttpServletRequest request) {
|
||||
String username = com.tashow.erp.utils.JwtUtil.getUsernameFromRequest(request);
|
||||
cacheService.clearCache(username);
|
||||
public JsonData clearCache() {
|
||||
cacheService.clearCache();
|
||||
return JsonData.buildSuccess("缓存清理成功");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,10 +64,10 @@ public interface AmazonProductRepository extends JpaRepository<AmazonProductEnti
|
||||
List<AmazonProductEntity> findLatestProducts();
|
||||
|
||||
/**
|
||||
* 获取指定用户最新会话的产品数据(按用户隔离)
|
||||
* 获取指定用户最新会话的产品数据(按用户和地区隔离)
|
||||
*/
|
||||
@Query(value = "SELECT * FROM amazon_products WHERE session_id = (SELECT session_id FROM amazon_products WHERE session_id LIKE :username || '#%' GROUP BY session_id ORDER BY session_id DESC LIMIT 1) ORDER BY updated_at ", nativeQuery = true)
|
||||
List<AmazonProductEntity> findLatestProducts(@Param("username") String username);
|
||||
@Query(value = "SELECT * FROM amazon_products WHERE session_id = (SELECT session_id FROM amazon_products WHERE session_id LIKE :username || '#%' AND region = :region GROUP BY session_id ORDER BY session_id DESC LIMIT 1) ORDER BY updated_at ", nativeQuery = true)
|
||||
List<AmazonProductEntity> findLatestProducts(@Param("username") String username, @Param("region") String region);
|
||||
|
||||
/**
|
||||
* 删除指定ASIN在指定时间后的数据(用于清理12小时内重复)
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package com.tashow.erp.service;
|
||||
|
||||
import com.tashow.erp.entity.AuthTokenEntity;
|
||||
import com.tashow.erp.entity.RakutenProductEntity;
|
||||
import com.tashow.erp.entity.AmazonProductEntity;
|
||||
import com.tashow.erp.entity.BanmaOrderEntity;
|
||||
import com.tashow.erp.repository.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -11,7 +8,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
@@ -49,39 +45,26 @@ public class CacheService {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void clearCache(String username) {
|
||||
if (username == null || username.isEmpty()) {
|
||||
logger.warn("尝试清理缓存但未提供用户名,操作已跳过");
|
||||
return;
|
||||
}
|
||||
public void clearCache() {
|
||||
|
||||
|
||||
logger.info("开始清理用户缓存: {}", username);
|
||||
// 清理所有产品数据
|
||||
rakutenProductRepository.deleteAll();
|
||||
|
||||
|
||||
// 清理当前用户的 Rakuten 产品数据
|
||||
List<RakutenProductEntity> rakutenProducts = rakutenProductRepository.findAll();
|
||||
List<RakutenProductEntity> userRakutenProducts = rakutenProducts.stream()
|
||||
.filter(p -> p.getSessionId() != null && p.getSessionId().startsWith(username + "#"))
|
||||
.toList();
|
||||
rakutenProductRepository.deleteAll(userRakutenProducts);
|
||||
logger.info("已清理 {} 条 Rakuten 产品数据", userRakutenProducts.size());
|
||||
amazonProductRepository.deleteAll();
|
||||
|
||||
|
||||
// 清理当前用户的 Amazon 产品数据
|
||||
List<AmazonProductEntity> amazonProducts = amazonProductRepository.findAll();
|
||||
List<AmazonProductEntity> userAmazonProducts = amazonProducts.stream()
|
||||
.filter(p -> p.getSessionId() != null && p.getSessionId().startsWith(username + "#"))
|
||||
.toList();
|
||||
amazonProductRepository.deleteAll(userAmazonProducts);
|
||||
logger.info("已清理 {} 条 Amazon 产品数据", userAmazonProducts.size());
|
||||
alibaba1688ProductRepository.deleteAll();
|
||||
|
||||
|
||||
// 清理当前用户的 Banma 订单数据
|
||||
List<BanmaOrderEntity> banmaOrders = banmaOrderRepository.findAll();
|
||||
List<BanmaOrderEntity> userBanmaOrders = banmaOrders.stream()
|
||||
.filter(o -> o.getSessionId() != null && o.getSessionId().startsWith(username + "#"))
|
||||
.toList();
|
||||
banmaOrderRepository.deleteAll(userBanmaOrders);
|
||||
logger.info("已清理 {} 条 Banma 订单数据", userBanmaOrders.size());
|
||||
|
||||
logger.info("用户 {} 的缓存清理完成", username);
|
||||
// 清理所有订单数据
|
||||
banmaOrderRepository.deleteAll();
|
||||
|
||||
zebraOrderRepository.deleteAll();
|
||||
// 清理通用缓存和更新状态
|
||||
cacheDataRepository.deleteAll();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ public class AmazonScrapingServiceImpl implements AmazonScrapingService, PagePro
|
||||
if (isEmpty(seller)) {
|
||||
seller = html.xpath("//span[@class='a-size-small offer-display-feature-text-message']/text()").toString();
|
||||
}
|
||||
|
||||
// 关键数据为空时重试
|
||||
if (isEmpty(price) && isEmpty(seller)) {
|
||||
throw new RuntimeException("Retry this page");
|
||||
@@ -94,6 +93,8 @@ public class AmazonScrapingServiceImpl implements AmazonScrapingService, PagePro
|
||||
String sessionId = (batchId != null) ? batchId : "SINGLE_" + UUID.randomUUID();
|
||||
LocalDateTime batchTime = LocalDateTime.now();
|
||||
|
||||
resultCache.clear();
|
||||
|
||||
// 第一步:清理1小时前的所有旧数据
|
||||
amazonProductRepository.deleteAllDataBefore(LocalDateTime.now().minusHours(1));
|
||||
|
||||
@@ -110,7 +111,12 @@ public class AmazonScrapingServiceImpl implements AmazonScrapingService, PagePro
|
||||
|
||||
Optional<AmazonProductEntity> cached = amazonProductRepository.findByAsinAndRegion(cleanAsin, region);
|
||||
if (cached.isPresent() && !isEmpty(cached.get().getPrice()) && !isEmpty(cached.get().getSeller())) {
|
||||
AmazonProductEntity entity = cached.get();
|
||||
AmazonProductEntity cachedEntity = cached.get();
|
||||
AmazonProductEntity entity = new AmazonProductEntity();
|
||||
entity.setAsin(cachedEntity.getAsin());
|
||||
entity.setRegion(cachedEntity.getRegion());
|
||||
entity.setPrice(cachedEntity.getPrice());
|
||||
entity.setSeller(cachedEntity.getSeller());
|
||||
entity.setSessionId(sessionId);
|
||||
entity.setUpdatedAt(LocalDateTime.now());
|
||||
amazonProductRepository.save(entity);
|
||||
|
||||
@@ -19,17 +19,17 @@ public class GenmaiServiceImpl {
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public void openGenmaiWebsite(Long accountId) throws Exception {
|
||||
String token = getAndValidateToken(accountId);
|
||||
public void openGenmaiWebsite(Long accountId, String username) throws Exception {
|
||||
String token = getAndValidateToken(accountId, username);
|
||||
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);
|
||||
String systemUser = System.getProperty("user.name");
|
||||
char firstChar = systemUser.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)))
|
||||
.replace(systemUser, UrlUtils.urlEncode(flipped + systemUser.substring(1)))
|
||||
+ "\\AppData\\Local\\Google\\Chrome\\User Data";
|
||||
options.addArguments("user-data-dir=" + chromeUserData.toLowerCase(), "profile-directory=Default");
|
||||
|
||||
@@ -40,8 +40,9 @@ public class GenmaiServiceImpl {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private String getAndValidateToken(Long accountId) {
|
||||
Map<String, Object> resp = restTemplate.getForObject(serverApiUrl + "/tool/genmai/accounts", Map.class);
|
||||
private String getAndValidateToken(Long accountId, String username) {
|
||||
String url = serverApiUrl + "/tool/genmai/accounts?name=" + username;
|
||||
Map<String, Object> resp = restTemplate.getForObject(url, Map.class);
|
||||
List<Map<String, Object>> list = (List<Map<String, Object>>) resp.get("data");
|
||||
|
||||
Map<String, Object> account = accountId != null
|
||||
@@ -49,12 +50,10 @@ public class GenmaiServiceImpl {
|
||||
: 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);
|
||||
resp = restTemplate.getForObject(url, 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");
|
||||
@@ -69,8 +68,7 @@ public class GenmaiServiceImpl {
|
||||
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;
|
||||
return objectMapper.readTree(response.getBody()).get("code").asInt() == 0;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ public class GenmaiAccountController {
|
||||
public R<?> saveAccount(@RequestBody GenmaiAccount body, String name) {
|
||||
if (body.getId() == null) {
|
||||
String token = accountService.validateAndGetToken(body.getUsername(), body.getPassword());
|
||||
if (token == null) throw new RuntimeException("账号密码验证失败,请检查后重试");
|
||||
body.setToken(token);
|
||||
body.setTokenExpireAt(new Date(System.currentTimeMillis() + 30L * 24 * 60 * 60 * 1000));
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.Map;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
@@ -76,10 +77,11 @@ public class GenmaiAccountServiceImpl implements IGenmaiAccountService {
|
||||
@Override
|
||||
public boolean validateToken(String token) {
|
||||
try {
|
||||
HttpEntity<String> entity = new HttpEntity<>(null, null);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Token", token);
|
||||
ResponseEntity<String> response = restTemplate.exchange(
|
||||
"https://www.genmaijl.com/manage/user/balance",
|
||||
HttpMethod.GET, entity, String.class);
|
||||
HttpMethod.GET, new HttpEntity<>(headers), String.class);
|
||||
return objectMapper.readTree(response.getBody()).get("code").asInt() == 0;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user