diff --git a/electron-vue-template/src/renderer/api/mark.ts b/electron-vue-template/src/renderer/api/mark.ts index ecfbc0e..8839afe 100644 --- a/electron-vue-template/src/renderer/api/mark.ts +++ b/electron-vue-template/src/renderer/api/mark.ts @@ -16,6 +16,13 @@ export const markApi = { // 品牌商标筛查 brandCheck(brands: string[]) { return http.post<{ code: number, data: { total: number, filtered: number, passed: number, data: any[] }, msg: string }>('/tool/mark/brandCheck', brands) + }, + + // 从Excel提取品牌列表(客户端本地接口) + extractBrands(file: File) { + const formData = new FormData() + formData.append('file', file) + return http.upload<{ code: number, data: { total: number, brands: string[] }, msg: string }>('/api/trademark/extractBrands', formData) } } diff --git a/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue b/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue index ffcc44e..4c8c1dd 100644 --- a/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue +++ b/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue @@ -105,7 +105,7 @@ function handleRetryTask() {
-
- +
@@ -200,8 +200,8 @@ function handleRetryTask() {
ASIN
- -
+ +
- -
+ +
+
+ + +
+ + +
@@ -240,22 +256,27 @@ function handleRetryTask() {
- 筛查失败 - 采集完成 - 采集中 + 采集完成 + 筛查失败 + 已取消 + 采集中 待采集
- 采集完成 + 采集完成 + 筛查失败 + 已取消 采集中 待采集
- 采集完成 + 采集完成 + 筛查失败 + 已取消 采集中 待采集
@@ -267,7 +288,7 @@ function handleRetryTask() {
{{ trademarkPanelRef?.taskProgress?.product?.label || '未注册/TM商标筛查' }}
-
{{ trademarkPanelRef?.taskProgress?.product?.desc || '筛查未注册商标或TM标的产品' }} (已完成)
+
{{ trademarkPanelRef?.taskProgress?.product?.desc || '筛查未注册商标或TM标的产品' }} (已完成)
@@ -279,15 +300,15 @@ function handleRetryTask() {
查询数量 - {{ (trademarkPanelRef?.taskProgress?.product?.total || 0) === 0 ? '-' : trademarkPanelRef.taskProgress.product.total }} + {{ (trademarkPanelRef?.taskProgress?.product?.total || 0) > 100 ? trademarkPanelRef.taskProgress.product.total : '-' }}
未注册/TM标 - {{ (trademarkPanelRef?.taskProgress?.product?.total || 0) > 0 ? (trademarkPanelRef?.taskProgress?.product?.completed || 0) : '-' }} + {{ (trademarkPanelRef?.taskProgress?.product?.total || 0) > 100 ? (trademarkPanelRef?.taskProgress?.product?.completed || 0) : '-' }}
已过滤 - {{ (trademarkPanelRef?.taskProgress?.product?.total || 0) > 0 ? ((trademarkPanelRef?.taskProgress?.product?.total || 0) - (trademarkPanelRef?.taskProgress?.product?.completed || 0)) : '-' }} + {{ (trademarkPanelRef?.taskProgress?.product?.total || 0) > 100 ? ((trademarkPanelRef?.taskProgress?.product?.total || 0) - (trademarkPanelRef?.taskProgress?.product?.completed || 0)) : '-' }}
@@ -296,7 +317,7 @@ function handleRetryTask() {
{{ trademarkPanelRef?.taskProgress?.brand?.label || '品牌商标筛查' }}
-
{{ trademarkPanelRef?.taskProgress?.brand?.desc || '筛查未注册商标的品牌' }} (已完成)
+
{{ trademarkPanelRef?.taskProgress?.brand?.desc || '筛查未注册商标的品牌' }} (已完成)
@@ -312,11 +333,11 @@ function handleRetryTask() {
未注册 - {{ (trademarkPanelRef?.taskProgress?.brand?.total || 0) > 0 ? (trademarkPanelRef?.taskProgress?.brand?.completed || 0) : '-' }} + {{ ((trademarkPanelRef?.taskProgress?.brand?.current || 0) >= (trademarkPanelRef?.taskProgress?.brand?.total || 1)) ? (trademarkPanelRef?.taskProgress?.brand?.completed || 0) : '-' }}
已注册 - {{ (trademarkPanelRef?.taskProgress?.brand?.total || 0) > 0 ? ((trademarkPanelRef?.taskProgress?.brand?.total || 0) - (trademarkPanelRef?.taskProgress?.brand?.completed || 0)) : '-' }} + {{ ((trademarkPanelRef?.taskProgress?.brand?.current || 0) >= (trademarkPanelRef?.taskProgress?.brand?.total || 1)) ? ((trademarkPanelRef?.taskProgress?.brand?.total || 0) - (trademarkPanelRef?.taskProgress?.brand?.completed || 0)) : '-' }}
@@ -342,11 +363,11 @@ function handleRetryTask() {
可跟卖 - {{ (trademarkPanelRef?.taskProgress?.platform?.total || 0) > 0 ? (trademarkPanelRef?.taskProgress?.platform?.completed || 0) : '-' }} + {{ ((trademarkPanelRef?.taskProgress?.platform?.current || 0) >= (trademarkPanelRef?.taskProgress?.platform?.total || 1)) ? (trademarkPanelRef?.taskProgress?.platform?.completed || 0) : '-' }}
已过滤 - {{ (trademarkPanelRef?.taskProgress?.platform?.total || 0) > 0 ? ((trademarkPanelRef?.taskProgress?.platform?.total || 0) - (trademarkPanelRef?.taskProgress?.platform?.completed || 0)) : '-' }} + {{ ((trademarkPanelRef?.taskProgress?.platform?.current || 0) >= (trademarkPanelRef?.taskProgress?.platform?.total || 1)) ? ((trademarkPanelRef?.taskProgress?.platform?.total || 0) - (trademarkPanelRef?.taskProgress?.platform?.completed || 0)) : '-' }}
@@ -355,9 +376,9 @@ function handleRetryTask() { -
+
-
+
setTimeout(resolve, pollInterval)) - if (!trademarkLoading.value) return - - // 尝试获取任务结果 - try { - taskResult = await markApi.getTask() - - if (taskResult.code === 200 || taskResult.code === 0) { - // 检查是否有 download_url,有则说明任务完成 - if (taskResult.data.original?.download_url) { - break - } - } - } catch (err) { - // 继续等待 - console.log('等待任务处理中...', err) + // 启动进度动画(5-95%) + const progressTimer = setInterval(() => { + if (taskData.current < 95) { + taskData.current = Math.min(taskData.current + 3, 95) } - } + }, 500) - if (!trademarkLoading.value) return - - // 步骤3: 检查是否成功获取到结果 - if (!taskResult || (taskResult.code !== 200 && taskResult.code !== 0)) { - throw new Error('获取任务超时或失败,请重试') - } - - if (!taskResult.data.original?.download_url) { - throw new Error('任务处理超时,请稍后重试') - } - - productResult = taskResult - - // 从后端获取真实的统计数据 - taskData.total = taskResult.data.original?.total || 0 - taskData.completed = taskResult.data.filtered.length - taskData.current = taskData.total - - // 映射后端数据到前端格式 - trademarkData.value = taskResult.data.filtered.map((item: any) => ({ - name: item['品牌'] || '', - status: item['商标类型'] || '', - class: '', - owner: '', - expireDate: item['注册时间'] || '', - similarity: 0, - // 保留原始数据供后续使用 - asin: item['ASIN'], - productImage: item['商品主图'] - })) - - // 如果需要品牌筛查,从产品结果中提取品牌列表 - if (needBrandCheck) { - brandList = taskResult.data.filtered - .map((item: any) => item['品牌']) - .filter((brand: string) => brand && brand.trim()) - } - - showMessage(`产品筛查完成,共查询 ${taskData.total} 条,筛查出 ${taskData.completed} 条未注册/TM标`, 'success') - } - - // 品牌商标筛查 - if (needBrandCheck) { - if (!trademarkLoading.value) return - - // 如果没有执行产品筛查,需要先上传文件获取品牌列表 - if (!needProductCheck) { - showMessage('正在上传文件提取品牌列表...', 'info') - - // 调用新建任务接口获取处理后的数据 - const createResult = await markApi.newTask(trademarkFile.value) - - if (createResult.code !== 200 && createResult.code !== 0) { - throw new Error(createResult.msg || '创建任务失败') - } - - // 轮询获取任务结果 + // 轮询检查任务状态 + const pollTask = async () => { const maxWaitTime = 60000 const pollInterval = 3000 const startTime = Date.now() let taskResult: any = null while (Date.now() - startTime < maxWaitTime) { - if (!trademarkLoading.value) return + if (!trademarkLoading.value) { + clearInterval(progressTimer) + return null + } await new Promise(resolve => setTimeout(resolve, pollInterval)) - if (!trademarkLoading.value) return + if (!trademarkLoading.value) { + clearInterval(progressTimer) + return null + } try { taskResult = await markApi.getTask() - if (taskResult.code === 200 || taskResult.code === 0) { if (taskResult.data.original?.download_url) { - break + clearInterval(progressTimer) + return taskResult } } } catch (err) { - console.log('等待任务处理中...', err) + // 继续等待 } } + clearInterval(progressTimer) + return taskResult + } + + try { + productResult = await pollTask() if (!trademarkLoading.value) return - if (!taskResult || (taskResult.code !== 200 && taskResult.code !== 0)) { + if (!productResult || (productResult.code !== 200 && productResult.code !== 0)) { throw new Error('获取任务超时或失败,请重试') } - if (!taskResult.data.original?.download_url) { + if (!productResult.data.original?.download_url) { throw new Error('任务处理超时,请稍后重试') } - // 从结果中提取品牌列表(包括TM和未注册的品牌) - brandList = taskResult.data.filtered + // 设置真实统计数据 + taskData.total = productResult.data.original?.total || 0 + taskData.current = taskData.total + taskData.completed = productResult.data.filtered.length + } finally { + clearInterval(progressTimer) + } + + // 映射后端数据到前端格式 + trademarkData.value = productResult.data.filtered.map((item: any) => ({ + name: item['品牌'] || '', + status: item['商标类型'] || '', + class: '', + owner: '', + expireDate: item['注册时间'] || '', + similarity: 0, + asin: item['ASIN'], + productImage: item['商品主图'] + })) + + // 如果需要品牌筛查,从产品结果中提取品牌列表 + if (needBrandCheck) { + brandList = productResult.data.filtered .map((item: any) => item['品牌']) .filter((brand: string) => brand && brand.trim()) + } + + showMessage(`产品筛查完成,共 ${taskData.total} 条,筛查出 ${taskData.completed} 条`, 'success') + } + + // 品牌商标筛查 + if (needBrandCheck) { + if (!trademarkLoading.value) return + + // 如果没有执行产品筛查,需要先从Excel提取品牌列表 + if (!needProductCheck) { + showMessage('正在从Excel提取品牌列表...', 'info') + const extractResult = await markApi.extractBrands(trademarkFile.value) + if (extractResult.code !== 200 && extractResult.code !== 0) { + throw new Error(extractResult.msg || '提取品牌列表失败') + } + + if (!extractResult.data.brands || extractResult.data.brands.length === 0) { + throw new Error('未能从文件中提取到品牌数据,请确保Excel包含"品牌"列') + } + + brandList = extractResult.data.brands showMessage(`品牌列表提取成功,共 ${brandList.length} 个品牌`, 'success') } @@ -308,15 +288,13 @@ async function startTrademarkQuery() { showMessage(`开始品牌商标筛查,共 ${brandList.length} 个品牌...`, 'info') - // 模拟进度动画(每秒增加20个) - const batchSize = 20 + // 模拟进度动画 brandProgressTimer = setInterval(() => { if (brandData.current < brandList.length * 0.95) { - brandData.current = Math.min(brandData.current + batchSize, brandList.length * 0.95) + brandData.current = Math.min(brandData.current + 20, brandList.length * 0.95) } }, 1000) - // 调用品牌筛查接口(浏览器内并发,速度快) const brandResult = await markApi.brandCheck(brandList) if (brandProgressTimer) clearInterval(brandProgressTimer) @@ -352,29 +330,37 @@ async function startTrademarkQuery() { emit('updateData', trademarkData.value) let summaryMsg = '筛查完成' - if (needProductCheck) { - const taskData = taskProgress.value.product - summaryMsg += `,产品:${taskData.completed}/${taskData.total}` - } - if (needBrandCheck && brandList.length > 0) { - const brandData = taskProgress.value.brand - summaryMsg += `,品牌:${brandData.completed}/${brandData.total}` - } + if (needProductCheck) summaryMsg += `,产品:${taskProgress.value.product.completed}/${taskProgress.value.product.total}` + if (needBrandCheck && brandList.length > 0) summaryMsg += `,品牌:${taskProgress.value.brand.completed}/${taskProgress.value.brand.total}` showMessage(summaryMsg, 'success') } } catch (error: any) { - if (error.message && error.message.includes('网络')) { + const hasProductData = taskProgress.value.product.total > 0 + + // 优化错误信息 - 只显示友好提示 + let msg = error.message || '' + if (msg.includes('网络') || msg.includes('network')) { queryStatus.value = 'networkError' - errorMessage.value = '网络连接失败' + errorMessage.value = '网络不可用,请检查你的网络或代理设置' + } else if (msg.includes('超时') || msg.includes('timeout')) { + queryStatus.value = 'error' + errorMessage.value = '数据库维护中,请稍后重试' + } else if (msg.includes('403') || msg.includes('风控')) { + queryStatus.value = 'error' + errorMessage.value = '网站风控限制,请稍后重试' } else { queryStatus.value = 'error' - errorMessage.value = error.message || '查询失败' + errorMessage.value = '数据库维护中,请稍后重试' + } + + // 仅在第1步失败时清空数据 + if (!hasProductData) { + trademarkData.value = [] + emit('updateData', []) + } else { + emit('updateData', trademarkData.value) } showMessage(errorMessage.value, 'error') - - // 失败时清空数据,不显示侧边栏 - trademarkData.value = [] - emit('updateData', []) } finally { // 清除定时器 if (brandProgressTimer) { @@ -466,10 +452,13 @@ function resetToIdle() { trademarkData.value = [] trademarkFileName.value = '' trademarkFile.value = null + taskProgress.value.product.total = 0 taskProgress.value.product.current = 0 taskProgress.value.product.completed = 0 + taskProgress.value.brand.total = 0 taskProgress.value.brand.current = 0 taskProgress.value.brand.completed = 0 + taskProgress.value.platform.total = 0 taskProgress.value.platform.current = 0 taskProgress.value.platform.completed = 0 } @@ -481,8 +470,10 @@ defineExpose({ queryStatus, statusConfig, taskProgress, + errorMessage, resetToIdle, - stopTrademarkQuery + stopTrademarkQuery, + startTrademarkQuery }) @@ -493,8 +484,8 @@ defineExpose({
1
-
导入“卖家精灵选品表格”
-
在卖家精灵导出文档时,必须要勾选“导出主图”,具体操作请点击查看
+
导入Excel表格
+
产品筛查:需导入卖家精灵选品表格,并勾选"导出主图";品牌筛查:Excel需包含"品牌"列
📤
点击或将文件拖拽到这里上传
@@ -798,6 +789,12 @@ defineExpose({ font-weight: 500; border-radius: 6px; } +.start-btn:disabled { + background: #d9d9d9; + border-color: #d9d9d9; + color: #ffffff; + cursor: not-allowed; +} .stop-btn { background: #f56c6c; border-color: #f56c6c; diff --git a/erp_client_sb/src/main/java/com/tashow/erp/controller/TrademarkController.java b/erp_client_sb/src/main/java/com/tashow/erp/controller/TrademarkController.java index 3660c1f..ae1b4bd 100644 --- a/erp_client_sb/src/main/java/com/tashow/erp/controller/TrademarkController.java +++ b/erp_client_sb/src/main/java/com/tashow/erp/controller/TrademarkController.java @@ -1,19 +1,20 @@ package com.tashow.erp.controller; +import com.tashow.erp.utils.ExcelParseUtil; import com.tashow.erp.utils.JsonData; import com.tashow.erp.utils.LoggerUtil; import com.tashow.erp.utils.TrademarkCheckUtil; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import java.util.*; -import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; - /** * 商标检查控制器 - 极速版(浏览器内并发) */ @RestController @RequestMapping("/api/trademark") +@CrossOrigin public class TrademarkController { private static final Logger logger = LoggerUtil.getLogger(TrademarkController.class); @@ -31,10 +32,7 @@ public class TrademarkController { .map(String::trim) .distinct() .collect(Collectors.toList()); - - logger.info("开始检查 {}个品牌", list.size()); long start = System.currentTimeMillis(); - // 串行查询(不加延迟) List> unregistered = new ArrayList<>(); int checkedCount = 0; @@ -87,4 +85,21 @@ public class TrademarkController { util.closeDriver(); } } + + /** + * 从Excel提取品牌列表 + */ + @PostMapping("/extractBrands") + public JsonData extractBrands(@RequestParam("file") MultipartFile file) { + try { + List brands = ExcelParseUtil.parseColumnByName(file, "品牌"); + if (brands.isEmpty()) return JsonData.buildError("未找到品牌列或品牌数据为空"); + Map result = new HashMap<>(); + result.put("total", brands.size()); + result.put("brands", brands); + return JsonData.buildSuccess(result); + } catch (Exception e) { + return JsonData.buildError("提取失败: " + e.getMessage()); + } + } } diff --git a/erp_client_sb/src/main/java/com/tashow/erp/utils/ExcelParseUtil.java b/erp_client_sb/src/main/java/com/tashow/erp/utils/ExcelParseUtil.java index bc701d8..84b7b90 100644 --- a/erp_client_sb/src/main/java/com/tashow/erp/utils/ExcelParseUtil.java +++ b/erp_client_sb/src/main/java/com/tashow/erp/utils/ExcelParseUtil.java @@ -56,7 +56,6 @@ public class ExcelParseUtil { log.error("解析 Excel 文件失败: {}, 文件名: {}", e.getMessage(), file.getOriginalFilename(), e); } - return result; } @@ -101,4 +100,45 @@ public class ExcelParseUtil { return result; } + + /** + + * 根据列名解析数据(自动适配第1行或第2行为表头) + */ + public static List parseColumnByName(MultipartFile file, String columnName) { + List result = new ArrayList<>(); + try (InputStream in = file.getInputStream()) { + ExcelReader reader = ExcelUtil.getReader(in, 0); + List> rows = reader.read(); + if (rows.isEmpty()) return result; + + // 查找表头行和列索引 + int headerRow = -1, colIdx = -1; + for (int r = 0; r < Math.min(2, rows.size()); r++) { + for (int c = 0; c < rows.get(r).size(); c++) { + String col = rows.get(r).get(c).toString().replaceAll("\\s+", ""); + if (col.equals(columnName)) { + headerRow = r; + colIdx = c; + break; + } + } + if (colIdx != -1) break; + } + + if (colIdx == -1) return result; + + // 从表头下一行开始读数据 + for (int i = headerRow + 1; i < rows.size(); i++) { + List row = rows.get(i); + if (row.size() > colIdx && row.get(colIdx) != null) { + String val = row.get(colIdx).toString().trim(); + if (!val.isEmpty()) result.add(val); + } + } + } catch (Exception e) { + log.error("解析失败", e); + } + return result; + } } \ No newline at end of file diff --git a/erp_client_sb/src/main/java/com/tashow/erp/utils/TrademarkCheckUtil.java b/erp_client_sb/src/main/java/com/tashow/erp/utils/TrademarkCheckUtil.java index d377e06..61ddf40 100644 --- a/erp_client_sb/src/main/java/com/tashow/erp/utils/TrademarkCheckUtil.java +++ b/erp_client_sb/src/main/java/com/tashow/erp/utils/TrademarkCheckUtil.java @@ -6,26 +6,23 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; /** * 商标检查工具 - * 支持批量并发查询(每100次切换代理) + * 检测到403时自动切换代理并重试 */ @Component public class TrademarkCheckUtil { @Autowired private ProxyPool proxyPool; private ChromeDriver driver; - private final AtomicInteger checkCount = new AtomicInteger(0); - private static final int PROXY_SWITCH_THRESHOLD = 100; private synchronized void ensureInit() { if (driver == null) { - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 5; i++) { try { driver = SeleniumUtil.createDriver(false, proxyPool.getProxy()); driver.get("https://tmsearch.uspto.gov/search/search-results"); - Thread.sleep(5000); + Thread.sleep(6000); return; // 成功则返回 } catch (Exception e) { System.err.println("初始化失败(尝试" + (i+1) + "/3): " + e.getMessage()); @@ -42,14 +39,6 @@ public class TrademarkCheckUtil { public synchronized Map batchCheck(List brands) { ensureInit(); - // 每100个切换代理 - if (checkCount.addAndGet(brands.size()) >= PROXY_SWITCH_THRESHOLD) { - try { driver.quit(); } catch (Exception e) {} - driver = null; - checkCount.set(0); - ensureInit(); - } - // 构建批量查询脚本(带错误诊断) String script = """ const brands = arguments[0]; @@ -91,6 +80,27 @@ public class TrademarkCheckUtil { List> results = (List>) ((JavascriptExecutor) driver).executeAsyncScript(script, brands); + // 检测是否有403错误 + boolean has403 = results.stream() + .anyMatch(item -> { + String error = (String) item.get("error"); + return error != null && error.contains("HTTP 403"); + }); + + // 如果有403,切换代理并重试 + if (has403) { + System.err.println("检测到403,切换代理并重试..."); + try { driver.quit(); } catch (Exception e) {} + driver = null; + ensureInit(); + + // 重新执行查询 + @SuppressWarnings("unchecked") + List> retryResults = (List>) + ((JavascriptExecutor) driver).executeAsyncScript(script, brands); + results = retryResults; + } + Map resultMap = new HashMap<>(); for (Map item : results) { String brand = (String) item.get("brand"); @@ -117,7 +127,6 @@ public class TrademarkCheckUtil { if (driver != null) { try { driver.quit(); } catch (Exception e) {} driver = null; - checkCount.set(0); } } diff --git a/erp_client_sb/src/main/resources/application.yml b/erp_client_sb/src/main/resources/application.yml index fa893f6..9360247 100644 --- a/erp_client_sb/src/main/resources/application.yml +++ b/erp_client_sb/src/main/resources/application.yml @@ -10,6 +10,10 @@ javafx: spring: main: lazy-initialization: true + servlet: + multipart: + max-file-size: 50MB + max-request-size: 50MB datasource: url: jdbc:sqlite:./data/erp-cache.db?journal_mode=WAL&synchronous=NORMAL&cache_size=10000&temp_store=memory&busy_timeout=30000 driver-class-name: org.sqlite.JDBC diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/FileController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/FileController.java index 9789b5d..87f497a 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/FileController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/FileController.java @@ -20,9 +20,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Date; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** * @Author liwq * @Date 2025年03月14日 14:38 diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/GenmaiAccountController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/GenmaiAccountController.java index afae6bb..b16508c 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/GenmaiAccountController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/GenmaiAccountController.java @@ -10,19 +10,16 @@ import com.ruoyi.system.domain.ClientAccount; import com.ruoyi.system.mapper.GenmaiAccountMapper; import com.ruoyi.system.mapper.ClientAccountMapper; import com.ruoyi.system.service.IGenmaiAccountService; - @RestController @RequestMapping("/tool/genmai") @Anonymous public class GenmaiAccountController { - @Autowired private IGenmaiAccountService accountService; @Autowired private ClientAccountMapper clientAccountMapper; @Autowired private GenmaiAccountMapper genmaiAccountMapper; - @GetMapping("/accounts") public R listAccounts(String name) { return R.ok(accountService.listSimple(name));