From 0be60bc103bb0845b506cf56146ccf150a84f9e3 Mon Sep 17 00:00:00 2001 From: zhangzijienbplus <17738440858@163.com> Date: Mon, 27 Oct 2025 09:13:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(genmai):=20=E9=9B=86=E6=88=90=E8=B7=9F?= =?UTF-8?q?=E5=8D=96=E7=B2=BE=E7=81=B5=E8=B4=A6=E5=8F=B7=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E8=B7=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增卖精灵账号管理功能,支持多账号切换 - 实现账号增删改查接口与前端交互逻辑 -优化打开跟卖精灵流程,增加账号选择界面 - 添加账号权限限制与订阅升级提醒 - 完善后端账号实体类及数据库映射 - 更新系统控制器以支持指定账号启动功能- 调整HTTP请求路径适配新工具模块路由- 升级客户端版本至2.5.3并优化代码结构 --- electron-vue-template/src/main/main.ts | 3 +- .../src/renderer/api/genmai.ts | 39 +++++ .../src/renderer/api/http.ts | 2 +- .../src/renderer/api/system.ts | 5 +- .../components/amazon/AmazonDashboard.vue | 156 ++++++++++++++---- .../components/common/AccountManager.vue | 117 ++++++++++--- .../components/zebra/ZebraDashboard.vue | 2 +- erp_client_sb/pom.xml | 2 +- .../erp/controller/SystemController.java | 4 +- .../impl/AmazonScrapingServiceImpl.java | 10 +- .../erp/service/impl/GenmaiServiceImpl.java | 119 ++++++------- .../src/main/resources/application.yml | 2 - .../tool/GenmaiAccountController.java | 62 +++++++ .../ruoyi/system/domain/GenmaiAccount.java | 51 ++++++ .../system/mapper/GenmaiAccountMapper.java | 18 ++ .../system/service/IGenmaiAccountService.java | 50 ++++++ .../impl/GenmaiAccountServiceImpl.java | 116 +++++++++++++ .../mapper/system/GenmaiAccountMapper.xml | 94 +++++++++++ 18 files changed, 720 insertions(+), 132 deletions(-) create mode 100644 electron-vue-template/src/renderer/api/genmai.ts create mode 100644 ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/GenmaiAccountController.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/domain/GenmaiAccount.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/mapper/GenmaiAccountMapper.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/service/IGenmaiAccountService.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/GenmaiAccountServiceImpl.java create mode 100644 ruoyi-system/src/main/resources/mapper/system/GenmaiAccountMapper.xml diff --git a/electron-vue-template/src/main/main.ts b/electron-vue-template/src/main/main.ts index 521bdc9..b77ad9d 100644 --- a/electron-vue-template/src/main/main.ts +++ b/electron-vue-template/src/main/main.ts @@ -166,7 +166,6 @@ function startSpringBoot() { `--logging.config=file:${logbackConfigPath}`, `--logging.file.path=${logDir}` ]; - springProcess = spawn(javaPath, springArgs, { cwd: dataDir, detached: false, @@ -224,7 +223,7 @@ function startSpringBoot() { app.quit(); } } -startSpringBoot(); + startSpringBoot(); function stopSpringBoot() { if (!springProcess) return; try { diff --git a/electron-vue-template/src/renderer/api/genmai.ts b/electron-vue-template/src/renderer/api/genmai.ts new file mode 100644 index 0000000..2e93bd1 --- /dev/null +++ b/electron-vue-template/src/renderer/api/genmai.ts @@ -0,0 +1,39 @@ +import { http } from './http' + +export interface GenmaiAccount { + id?: number + name?: string + username: string + password: string + clientUsername?: string + token?: string + tokenExpireAt?: string + status?: number + remark?: string + createTime?: string + updateTime?: string +} + +export const genmaiApi = { + getAccounts(name?: string) { + return http.get('/tool/genmai/accounts', name ? { name } : undefined) + }, + + getAccountLimit(name?: string) { + return http.get('/tool/genmai/account-limit', name ? { name } : undefined) + }, + + saveAccount(body: GenmaiAccount, name?: string) { + const url = name ? `/tool/genmai/accounts?name=${encodeURIComponent(name)}` : '/tool/genmai/accounts' + return http.post(url, body) + }, + + removeAccount(id: number) { + return http.delete(`/tool/genmai/accounts/${id}`) + }, + + validateAndRefresh(id: number) { + return http.post(`/tool/genmai/accounts/${id}/validate`) + } +} + diff --git a/electron-vue-template/src/renderer/api/http.ts b/electron-vue-template/src/renderer/api/http.ts index c9a1eb0..00d5924 100644 --- a/electron-vue-template/src/renderer/api/http.ts +++ b/electron-vue-template/src/renderer/api/http.ts @@ -7,7 +7,7 @@ export const CONFIG = { } as const; function resolveBase(path: string): string { - if (path.startsWith('/monitor/') || path.startsWith('/system/') || path.startsWith('/tool/banma')) { + if (path.startsWith('/monitor/') || path.startsWith('/system/') || path.startsWith('/tool/banma') || path.startsWith('/tool/genmai')) { return CONFIG.RUOYI_BASE; } return CONFIG.CLIENT_BASE; diff --git a/electron-vue-template/src/renderer/api/system.ts b/electron-vue-template/src/renderer/api/system.ts index f805908..bd2a5da 100644 --- a/electron-vue-template/src/renderer/api/system.ts +++ b/electron-vue-template/src/renderer/api/system.ts @@ -1,8 +1,9 @@ import { http } from './http'; export const systemApi = { - openGenmaiSpirit() { - return http.post('/api/system/genmai/open'); + openGenmaiSpirit(accountId?: number | null) { + const url = accountId ? `/api/system/genmai/open?accountId=${accountId}` : '/api/system/genmai/open'; + return http.post(url); }, clearCache() { diff --git a/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue b/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue index 0ff09a9..e1c8a8d 100644 --- a/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue +++ b/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue @@ -3,11 +3,13 @@ import { ref, computed, onMounted, defineAsyncComponent, inject } from 'vue' import { ElMessage, ElMessageBox } from 'element-plus' import { amazonApi } from '../../api/amazon' import { systemApi } from '../../api/system' +import { genmaiApi, type GenmaiAccount } from '../../api/genmai' import { handlePlatformFileExport } from '../../utils/settings' import { getUsernameFromToken } from '../../utils/token' import { useFileDrop } from '../../composables/useFileDrop' const TrialExpiredDialog = defineAsyncComponent(() => import('../common/TrialExpiredDialog.vue')) +const AccountManager = defineAsyncComponent(() => import('../common/AccountManager.vue')) const refreshVipStatus = inject<() => Promise>('refreshVipStatus') @@ -24,6 +26,9 @@ const progressVisible = ref(false) // 进度条是否显示(完成后仍保留 const localProductData = ref([]) // 本地产品数据 const currentAsin = ref('') // 当前处理的ASIN const genmaiLoading = ref(false) // Genmai Spirit加载状态 +const genmaiAccounts = ref([]) // 跟卖精灵账号列表 +const selectedGenmaiAccountId = ref(null) // 选中的跟卖精灵账号ID +const currentTab = ref<'asin' | 'genmai'>('asin') // 当前选中的tab let abortController: AbortController | null = null // 请求取消控制器 // 分页配置 @@ -35,6 +40,10 @@ const amazonUpload = ref(null) const showTrialExpiredDialog = ref(false) const trialExpiredType = ref<'device' | 'account' | 'both' | 'subscribe'>('account') +// 账号管理弹框 +const showAccountManager = ref(false) +const accountManagerRef = ref(null) + const vipStatus = inject('vipStatus') // 计算属性 - 当前页数据 @@ -257,25 +266,25 @@ function stopFetch() { } async function openGenmaiSpirit() { - try { - await ElMessageBox.confirm('打开跟卖精灵会关闭所有谷歌浏览器进程,是否继续?', '提示', { - confirmButtonText: '确定', - cancelButtonText: '取消', - type: 'warning' - }) - genmaiLoading.value = true - try { - await systemApi.openGenmaiSpirit() - showMessage('跟卖精灵已打开', 'success') - } catch (error: any) { - const errorMsg = error?.msg || error?.message || '打开跟卖精灵失败' - showMessage(errorMsg, 'error') - } finally { - genmaiLoading.value = false - } - } catch { - // 用户取消 + if (!genmaiAccounts.value.length) { + showAccountManager.value = true + return } + genmaiLoading.value = true + try { + await systemApi.openGenmaiSpirit(selectedGenmaiAccountId.value) + showMessage('跟卖精灵已打开', 'success') + } finally { + genmaiLoading.value = false + } +} + +async function loadGenmaiAccounts() { + try { + const res = await genmaiApi.getAccounts(getUsernameFromToken()) + genmaiAccounts.value = (res as any)?.data ?? [] + if (genmaiAccounts.value[0]) selectedGenmaiAccountId.value = genmaiAccounts.value[0].id + } catch {} } // 分页处理 @@ -310,14 +319,12 @@ function downloadAmazonTemplate() { URL.revokeObjectURL(url) } -// 组件挂载时获取最新数据 onMounted(async () => { try { const resp = await amazonApi.getLatestProducts() localProductData.value = resp.data?.products || [] - } catch { - // 静默处理初始化失败 - } + } catch {} + await loadGenmaiAccounts() }) @@ -329,18 +336,79 @@ onMounted(async () => {