diff --git a/electron-vue-template/src/renderer/App.vue b/electron-vue-template/src/renderer/App.vue index 02bfefb..4ceb0ac 100644 --- a/electron-vue-template/src/renderer/App.vue +++ b/electron-vue-template/src/renderer/App.vue @@ -40,7 +40,7 @@ const navigationHistory = ref(['rakuten']) const currentHistoryIndex = ref(0) // 应用状态 -const activeMenu = ref('rakuten') +const activeMenu = ref(localStorage.getItem('active-menu') || 'rakuten') const isAuthenticated = ref(false) const showAuthDialog = ref(false) const showRegDialog = ref(false) @@ -190,6 +190,7 @@ function handleMenuSelect(key: string) { } activeMenu.value = key + localStorage.setItem('active-menu', key) addToHistory(key) } @@ -778,7 +779,7 @@ onUnmounted(() => { width: 180px; min-width: 180px; flex-shrink: 0; - background: #ffffff; + background: #F0F0F0; border-right: 1px solid #e8eaec; padding: 16px 12px; box-sizing: border-box; diff --git a/electron-vue-template/src/renderer/api/http.ts b/electron-vue-template/src/renderer/api/http.ts index 00d5924..a80d284 100644 --- a/electron-vue-template/src/renderer/api/http.ts +++ b/electron-vue-template/src/renderer/api/http.ts @@ -33,20 +33,29 @@ async function getToken(): Promise { async function request(path: string, options: RequestInit & { signal?: AbortSignal }): Promise { const token = await getToken(); - const res = await fetch(`${resolveBase(path)}${path}`, { - credentials: 'omit', - cache: 'no-store', - ...options, - headers: { - 'Content-Type': 'application/json;charset=UTF-8', - ...(token ? { 'Authorization': `Bearer ${token}` } : {}), - ...options.headers - } - }); + let res: Response; + + try { + res = await fetch(`${resolveBase(path)}${path}`, { + credentials: 'omit', + cache: 'no-store', + ...options, + headers: { + 'Content-Type': 'application/json;charset=UTF-8', + ...(token ? { 'Authorization': `Bearer ${token}` } : {}), + ...options.headers + } + }); + } catch (e) { + throw new Error('无法连接服务器,请检查网络后重试'); + } if (!res.ok) { + if (res.status >= 500) { + throw new Error('无法连接服务器,请检查网络后重试'); + } const text = await res.text().catch(() => ''); - throw new Error(text || `HTTP ${res.status}`); + throw new Error(text || '无法连接服务器,请检查网络后重试'); } const contentType = res.headers.get('content-type') || ''; @@ -81,18 +90,27 @@ export const http = { async upload(path: string, form: FormData, signal?: AbortSignal) { const token = await getToken(); - const res = await fetch(`${resolveBase(path)}${path}`, { - method: 'POST', - body: form, - credentials: 'omit', - cache: 'no-store', - headers: token ? { 'Authorization': `Bearer ${token}` } : {}, - signal - }); + let res: Response; + + try { + res = await fetch(`${resolveBase(path)}${path}`, { + method: 'POST', + body: form, + credentials: 'omit', + cache: 'no-store', + headers: token ? { 'Authorization': `Bearer ${token}` } : {}, + signal + }); + } catch (e) { + throw new Error('无法连接服务器,请检查网络后重试'); + } if (!res.ok) { + if (res.status >= 500) { + throw new Error('无法连接服务器,请检查网络后重试'); + } const text = await res.text().catch(() => ''); - throw new Error(text || `HTTP ${res.status}`); + throw new Error(text || '无法连接服务器,请检查网络后重试'); } const contentType = res.headers.get('content-type') || ''; diff --git a/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue b/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue index e1c8a8d..4e29a76 100644 --- a/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue +++ b/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue @@ -170,7 +170,6 @@ async function batchGetProductInfo(asinList: string[]) { // 处理完成状态更新 progressPercentage.value = 100 currentAsin.value = '处理完成' - selectedFileName.value = '' @@ -178,7 +177,6 @@ async function batchGetProductInfo(asinList: string[]) { if (error.name !== 'AbortError') { showMessage(error.message || '批量获取产品信息失败', 'error') currentAsin.value = '处理失败' - selectedFileName.value = '' } } finally { tableLoading.value = false @@ -261,7 +259,6 @@ function stopFetch() { abortController = null loading.value = false currentAsin.value = '已停止' - selectedFileName.value = '' showMessage('已停止获取产品数据', 'info') } @@ -273,8 +270,10 @@ async function openGenmaiSpirit() { genmaiLoading.value = true try { await systemApi.openGenmaiSpirit(selectedGenmaiAccountId.value) - showMessage('跟卖精灵已打开', 'success') - } finally { + showMessage('跟卖精灵正在启动,请稍候...', 'success') + setTimeout(() => { genmaiLoading.value = false }, 3000) + } catch (error: any) { + showMessage(error.message || '启动失败', 'error') genmaiLoading.value = false } } @@ -366,7 +365,7 @@ onMounted(async () => { > - avatar + avatar {{ acc.name || acc.username }} ✔️ @@ -391,7 +390,7 @@ onMounted(async () => {
2
启动服务
-
请确保设备已安装Chrome浏览器,否则服务将无法启动。打开跟卖精灵将关闭Chrome浏览器进程。
+
请确保设备已安装Chrome浏览器,否则服务将无法启动。打开跟卖精灵将关闭Chrome浏览器进程。首次启动需初始化浏览器驱动,可能需要1-3分钟。
{ @click="openGenmaiSpirit" > 启动服务 - ⟳ 启动中... + 启动中...
@@ -680,6 +679,7 @@ onMounted(async () => { .price { color: #e6a23c; font-weight: 600; } .table-loading { position: absolute; inset: 0; background: rgba(255, 255, 255, 0.95); display: flex; flex-direction: column; justify-content: center; align-items: center; font-size: 14px; color: #606266; } .spinner { font-size: 24px; animation: spin 1s linear infinite; margin-bottom: 8px; } +.action-buttons .spinner { font-size: 14px; margin-bottom: 0; display: inline-block; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .pagination-fixed { flex-shrink: 0; padding: 8px 12px 0 12px; background: #fff; display: flex; justify-content: flex-end; } .pagination-fixed :deep(.el-pager li.is-active) { border: 1px solid #1677FF; border-radius: 4px; color: #1677FF; background: #fff; } diff --git a/electron-vue-template/src/renderer/components/common/AccountManager.vue b/electron-vue-template/src/renderer/components/common/AccountManager.vue index 8a4ced2..41735b1 100644 --- a/electron-vue-template/src/renderer/components/common/AccountManager.vue +++ b/electron-vue-template/src/renderer/components/common/AccountManager.vue @@ -136,7 +136,7 @@ export default defineComponent({ name: 'AccountManager' })
{{ formatDate(a) }} diff --git a/electron-vue-template/src/renderer/components/common/SettingsDialog.vue b/electron-vue-template/src/renderer/components/common/SettingsDialog.vue index e2511d8..e7fc503 100644 --- a/electron-vue-template/src/renderer/components/common/SettingsDialog.vue +++ b/electron-vue-template/src/renderer/components/common/SettingsDialog.vue @@ -485,7 +485,7 @@ onMounted(() => {
- 如果自动安装,将仅会安装到新版本后,但需手动点击"重启升级"后才可升级 + 如关闭自动安装,你仍会收到新版本提示,但需手动点击"重启升级"后才可升级
diff --git a/electron-vue-template/src/renderer/components/common/UpdateDialog.vue b/electron-vue-template/src/renderer/components/common/UpdateDialog.vue index 42ca9e6..1281bcf 100644 --- a/electron-vue-template/src/renderer/components/common/UpdateDialog.vue +++ b/electron-vue-template/src/renderer/components/common/UpdateDialog.vue @@ -6,7 +6,7 @@
- App Icon + App Icon

新版本的"{{ appName }}"已经发布

@@ -41,7 +41,7 @@
- App Icon + App Icon
@@ -64,7 +64,7 @@
- App Icon + App Icon
diff --git a/electron-vue-template/src/renderer/components/layout/NavigationBar.vue b/electron-vue-template/src/renderer/components/layout/NavigationBar.vue index 7d31518..6b1f1d6 100644 --- a/electron-vue-template/src/renderer/components/layout/NavigationBar.vue +++ b/electron-vue-template/src/renderer/components/layout/NavigationBar.vue @@ -32,6 +32,14 @@ const displayUsername = computed(() => { return props.isAuthenticated ? props.currentUsername : '未登录' }) +const menuItems = [ + { command: 'check-update', label: computed(() => `检查更新 v${props.currentVersion}`), class: 'menu-item' }, + { command: 'account-manager', label: '我的电商账号', class: 'menu-item' }, + { command: 'device', label: '我的设备', class: 'menu-item' }, + { command: 'settings', label: '设置', class: 'menu-item' }, + { command: 'logout', label: '退出', class: 'menu-item logout-item', showIf: () => props.isAuthenticated } +] + async function handleMinimize() { await (window as any).electronAPI.windowMinimize() } @@ -45,24 +53,17 @@ async function handleClose() { await (window as any).electronAPI.windowClose() } +const commandMap: Record = { + logout: 'logout', + device: 'open-device', + settings: 'open-settings', + 'account-manager': 'open-account-manager', + 'check-update': 'check-update' +} + function handleCommand(command: string) { - switch (command) { - case 'logout': - emit('logout') - break - case 'device': - emit('open-device') - break - case 'settings': - emit('open-settings') - break - case 'account-manager': - emit('open-account-manager') - break - case 'check-update': - emit('check-update') - break - } + const emitName = commandMap[command] + if (emitName) emit(emitName as any) } onMounted(async () => { @@ -100,20 +101,13 @@ onMounted(async () => { {{ displayUsername }} - - 检查更新 v{{ currentVersion }} - - - 我的电商账号 - - - 我的设备 - - - 设置 - - - 退出 + + {{ typeof item.label === 'string' ? item.label : item.label.value }} diff --git a/electron-vue-template/src/renderer/components/rakuten/RakutenDashboard.vue b/electron-vue-template/src/renderer/components/rakuten/RakutenDashboard.vue index b3e8299..3738932 100644 --- a/electron-vue-template/src/renderer/components/rakuten/RakutenDashboard.vue +++ b/electron-vue-template/src/renderer/components/rakuten/RakutenDashboard.vue @@ -265,15 +265,10 @@ async function handleStartSearch() { } allProducts.value = products - pendingFile.value = null - selectedFileName.value = '' } catch (e: any) { if (e.name !== 'AbortError') { statusType.value = 'error' statusMessage.value = '解析失败,请重试' - // 失败后清空文件信息,让用户重新上传 - pendingFile.value = null - selectedFileName.value = '' } } finally { loading.value = false @@ -309,9 +304,6 @@ function stopTask() { statusMessage.value = '任务已停止' // 保留进度条和当前进度 allProducts.value = allProducts.value.map(p => ({...p, searching1688: false})) - // 清空文件信息,让用户重新上传 - pendingFile.value = null - selectedFileName.value = '' } async function startBatch1688Search(products: any[]) { @@ -499,7 +491,7 @@ onMounted(loadLatest)
网站地区
-
请选择目标网站地区,如:日本区。
+
仅支持乐天市场日本区商品查询,后续将开放更多乐天网站地区,敬请期待。
{{ opt.flag }}{{ opt.label }} diff --git a/electron-vue-template/src/renderer/components/zebra/ZebraDashboard.vue b/electron-vue-template/src/renderer/components/zebra/ZebraDashboard.vue index e40c9ff..5d18d8a 100644 --- a/electron-vue-template/src/renderer/components/zebra/ZebraDashboard.vue +++ b/electron-vue-template/src/renderer/components/zebra/ZebraDashboard.vue @@ -384,7 +384,7 @@ async function removeCurrentAccount() { > - avatar + avatar {{ a.name || a.username }} ✔️ diff --git a/electron-vue-template/src/renderer/public/icon/icon1.png b/electron-vue-template/src/renderer/public/icon/icon1.png new file mode 100644 index 0000000..d007c7e Binary files /dev/null and b/electron-vue-template/src/renderer/public/icon/icon1.png differ diff --git a/electron-vue-template/src/renderer/public/image/user.png b/electron-vue-template/src/renderer/public/image/user.png new file mode 100644 index 0000000..6dc0831 Binary files /dev/null and b/electron-vue-template/src/renderer/public/image/user.png differ diff --git a/erp_client_sb/pom.xml b/erp_client_sb/pom.xml index 4997948..ffdaa17 100644 --- a/erp_client_sb/pom.xml +++ b/erp_client_sb/pom.xml @@ -10,7 +10,7 @@ com.tashow.erp erp_client_sb - 2.5.5 + 2.5.6 erp_client_sb erp客户端 diff --git a/erp_client_sb/src/main/java/com/tashow/erp/config/AsyncConfig.java b/erp_client_sb/src/main/java/com/tashow/erp/config/AsyncConfig.java new file mode 100644 index 0000000..c6b5b9c --- /dev/null +++ b/erp_client_sb/src/main/java/com/tashow/erp/config/AsyncConfig.java @@ -0,0 +1,9 @@ +package com.tashow.erp.config; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; + +@Configuration +@EnableAsync +public class AsyncConfig { +} + diff --git a/erp_client_sb/src/main/java/com/tashow/erp/controller/SystemController.java b/erp_client_sb/src/main/java/com/tashow/erp/controller/SystemController.java index c0d7d8f..f3cc18a 100644 --- a/erp_client_sb/src/main/java/com/tashow/erp/controller/SystemController.java +++ b/erp_client_sb/src/main/java/com/tashow/erp/controller/SystemController.java @@ -84,14 +84,9 @@ public class SystemController { @PostMapping("/genmai/open") public JsonData openGenmaiWebsite(@RequestParam(required = false) Long accountId, HttpServletRequest request) { - try { - String username = com.tashow.erp.utils.JwtUtil.getUsernameFromRequest(request); - genmaiService.openGenmaiWebsite(accountId, username); - return JsonData.buildSuccess("跟卖精灵已打开"); - } catch (Exception e) { - logger.error("打开跟卖精灵失败", e); - return JsonData.buildError(e.getMessage() != null ? e.getMessage() : "打开跟卖精灵失败"); - } + String username = com.tashow.erp.utils.JwtUtil.getUsernameFromRequest(request); + genmaiService.openGenmaiWebsite(accountId, username); + return JsonData.buildSuccess("跟卖精灵正在启动"); } @GetMapping("/proxy/image") diff --git a/erp_client_sb/src/main/java/com/tashow/erp/service/impl/ChromeDriverPreloader.java b/erp_client_sb/src/main/java/com/tashow/erp/service/impl/ChromeDriverPreloader.java new file mode 100644 index 0000000..1eb9f7f --- /dev/null +++ b/erp_client_sb/src/main/java/com/tashow/erp/service/impl/ChromeDriverPreloader.java @@ -0,0 +1,21 @@ +package com.tashow.erp.service.impl; +import jakarta.annotation.PostConstruct; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; +import org.springframework.stereotype.Component; + +@Component +public class ChromeDriverPreloader { + @PostConstruct + public void preloadDriver() { + new Thread(() -> { + try { + ChromeOptions options = new ChromeOptions(); + options.addArguments("--headless", "--disable-gpu"); + ChromeDriver driver = new ChromeDriver(options); + driver.quit(); + } catch (Exception ignored) {} + }).start(); + } +} + diff --git a/erp_client_sb/src/main/java/com/tashow/erp/service/impl/GenmaiServiceImpl.java b/erp_client_sb/src/main/java/com/tashow/erp/service/impl/GenmaiServiceImpl.java index 2956b14..5154ced 100644 --- a/erp_client_sb/src/main/java/com/tashow/erp/service/impl/GenmaiServiceImpl.java +++ b/erp_client_sb/src/main/java/com/tashow/erp/service/impl/GenmaiServiceImpl.java @@ -7,6 +7,7 @@ import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.*; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.List; @@ -19,24 +20,27 @@ public class GenmaiServiceImpl { private final RestTemplate restTemplate = new RestTemplate(); private final ObjectMapper objectMapper = new ObjectMapper(); - 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 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(systemUser, UrlUtils.urlEncode(flipped + systemUser.substring(1))) - + "\\AppData\\Local\\Google\\Chrome\\User Data"; - options.addArguments("user-data-dir=" + chromeUserData.toLowerCase(), "profile-directory=Default"); - - ChromeDriver driver = new ChromeDriver(options); - driver.get("https://www.genmaijl.com/#/profile"); - ((JavascriptExecutor) driver).executeScript("localStorage.setItem('token','" + token + "')"); - driver.navigate().refresh(); + @Async + public void openGenmaiWebsite(Long accountId, String username) { + try { + String token = getAndValidateToken(accountId, username); + Runtime.getRuntime().exec("taskkill /f /im chrome.exe"); + Thread.sleep(1000); + + ChromeOptions options = new ChromeOptions(); + 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(systemUser, UrlUtils.urlEncode(flipped + systemUser.substring(1))) + + "\\AppData\\Local\\Google\\Chrome\\User Data"; + options.addArguments("user-data-dir=" + chromeUserData.toLowerCase(), "profile-directory=Default"); + + ChromeDriver driver = new ChromeDriver(options); + driver.get("https://www.genmaijl.com/#/profile"); + ((JavascriptExecutor) driver).executeScript("localStorage.setItem('token','" + token + "')"); + driver.navigate().refresh(); + } catch (Exception ignored) {} } @SuppressWarnings("unchecked") diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/service/impl/ClientAccountServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/web/service/impl/ClientAccountServiceImpl.java index 29f8f9a..0094886 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/service/impl/ClientAccountServiceImpl.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/service/impl/ClientAccountServiceImpl.java @@ -9,7 +9,6 @@ import com.ruoyi.system.mapper.BanmaAccountMapper; import com.ruoyi.system.mapper.ClientFeedbackMapper; import com.ruoyi.system.mapper.ClientMonitorMapper; import com.ruoyi.system.mapper.ClientAccountDeviceMapper; -import com.ruoyi.system.mapper.RefreshTokenMapper; import com.ruoyi.web.service.IClientAccountService; /** @@ -30,8 +29,6 @@ public class ClientAccountServiceImpl implements IClientAccountService private ClientMonitorMapper clientMonitorMapper; @Autowired private ClientAccountDeviceMapper clientAccountDeviceMapper; - @Autowired - private RefreshTokenMapper refreshTokenMapper; /** * 查询客户端账号 @@ -80,7 +77,7 @@ public class ClientAccountServiceImpl implements IClientAccountService /** * 批量删除客户端账号 - * 级联删除所有关联数据:斑马账号、反馈、错误报告、设备绑定、刷新令牌 + * 级联删除所有关联数据:斑马账号、反馈、错误报告、设备绑定 */ @Override public int deleteClientAccountByIds(Long[] ids) @@ -100,7 +97,6 @@ public class ClientAccountServiceImpl implements IClientAccountService // 根据accountId删除关联数据 clientAccountDeviceMapper.deleteByAccountId(id); - refreshTokenMapper.deleteByAccountId(id); } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/RefreshToken.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/RefreshToken.java deleted file mode 100644 index e2b02a4..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/RefreshToken.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.ruoyi.system.domain; - -import java.util.Date; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.ruoyi.common.core.domain.BaseEntity; - -/** - * 刷新令牌对象 refresh_token - */ -public class RefreshToken extends BaseEntity { - private static final long serialVersionUID = 1L; - - /** ID */ - private Long id; - - /** 账号ID */ - private Long accountId; - - /** 设备ID */ - private String deviceId; - - /** 刷新令牌 */ - private String token; - - /** 到期时间 */ - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private Date expireTime; - - /** 是否已撤销 */ - private String revoked; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public Long getAccountId() { - return accountId; - } - - public void setAccountId(Long accountId) { - this.accountId = accountId; - } - - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getToken() { - return token; - } - - public void setToken(String token) { - this.token = token; - } - - public Date getExpireTime() { - return expireTime; - } - - public void setExpireTime(Date expireTime) { - this.expireTime = expireTime; - } - - public String getRevoked() { - return revoked; - } - - public void setRevoked(String revoked) { - this.revoked = revoked; - } -} \ No newline at end of file diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/RefreshTokenMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/RefreshTokenMapper.java deleted file mode 100644 index d288bb6..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/RefreshTokenMapper.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.ruoyi.system.mapper; - -import com.ruoyi.system.domain.RefreshToken; -import java.util.List; - -/** - * 刷新令牌数据层 - */ -public interface RefreshTokenMapper { - - /** - * 保存刷新令牌 - */ - int insertRefreshToken(RefreshToken refreshToken); - - /** - * 根据令牌查找 - */ - RefreshToken selectByToken(String token); - - /** - * 撤销账号的所有令牌 - */ - int revokeByAccountId(Long accountId); - - /** - * 撤销设备的所有令牌 - */ - int revokeByDeviceId(String deviceId); - - /** - * 删除过期令牌 - */ - int deleteExpiredTokens(); - - /** - * 更新令牌状态 - */ - int updateRefreshToken(RefreshToken refreshToken); - - /** - * 根据账号ID删除令牌 - */ - int deleteByAccountId(Long accountId); -} \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/RefreshTokenMapper.xml b/ruoyi-system/src/main/resources/mapper/system/RefreshTokenMapper.xml deleted file mode 100644 index cdfebd8..0000000 --- a/ruoyi-system/src/main/resources/mapper/system/RefreshTokenMapper.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - insert into refresh_token - - account_id, - device_id, - token, - expire_time, - revoked, - create_time - - - #{accountId}, - #{deviceId}, - #{token}, - #{expireTime}, - #{revoked}, - sysdate() - - - - - - - update refresh_token set revoked = '1', update_time = sysdate() - where account_id = #{accountId} and revoked = '0' - - - - update refresh_token set revoked = '1', update_time = sysdate() - where device_id = #{deviceId} and revoked = '0' - - - - delete from refresh_token - where expire_time < sysdate() or revoked = '1' - - - - update refresh_token - - revoked = #{revoked}, - update_time = sysdate() - - where id = #{id} - - - - delete from refresh_token where account_id = #{accountId} - - - \ No newline at end of file