refactor(client):优化设备管理与登录逻辑

- 移除冗余的日志记录器声明
- 简化设备心跳接口,合并注册与更新逻辑
- 调整设备数量限制检查逻辑,提高代码可读性
- 修改默认设备数量限制从3台调整为1台- 更新客户端登出提示文案- 固定启动窗口尺寸并移除延迟启动逻辑
- 调整设备移除时的消息提示内容
This commit is contained in:
2025-10-17 16:14:43 +08:00
parent d9f91b77e3
commit 0c85aa5677
5 changed files with 41 additions and 117 deletions

View File

@@ -199,7 +199,7 @@ function startSpringBoot() {
}
}
// startSpringBoot();
startSpringBoot();
function stopSpringBoot() {
if (!springProcess) return;
@@ -269,10 +269,9 @@ app.whenReady().then(() => {
createWindow();
createTray(mainWindow);
const {width: sw, height: sh} = screen.getPrimaryDisplay().workAreaSize;
splashWindow = new BrowserWindow({
width: Math.min(Math.floor(sw * 0.8), 1800),
height: Math.min(Math.floor(sh * 0.8), 1200),
width: 1200,
height: 675,
frame: false,
transparent: false,
resizable: false,
@@ -296,9 +295,9 @@ app.whenReady().then(() => {
splashWindow.loadFile(splashPath);
}
setTimeout(() => {
openAppIfNotOpened();
}, 2000);
// setTimeout(() => {
// openAppIfNotOpened();
// }, 2000);
app.on('activate', () => {
if (mainWindow && !mainWindow.isDestroyed()) {

View File

@@ -185,16 +185,13 @@ async function handleLoginSuccess(data: { token: string; permissions?: string; e
os: navigator.platform
})
SSEManager.connect()
// 根据不同场景显示提示
const accountExpired = vipExpireTime.value && new Date() > vipExpireTime.value
const deviceExpired = deviceTrialExpired.value
const isPaid = accountType.value === 'paid'
if (isPaid) {
// 场景5: 付费用户
ElMessage.success('登录成功')
} else if (deviceExpired && accountExpired) {
if (deviceExpired && accountExpired) {
// 场景4: 试用已到期,请订阅
trialExpiredType.value = 'both'
showTrialExpiredDialog.value = true
@@ -206,9 +203,6 @@ async function handleLoginSuccess(data: { token: string; permissions?: string; e
// 场景2: 设备试用已到期,请更换设备或订阅
trialExpiredType.value = 'device'
showTrialExpiredDialog.value = true
} else {
// 场景1: 允许使用
ElMessage.success('登录成功')
}
} catch (e: any) {
isAuthenticated.value = false
@@ -234,7 +228,7 @@ function clearLocalAuth() {
async function logout() {
try {
const deviceId = getClientIdFromToken()
if (deviceId) await deviceApi.offline({ deviceId, username: currentUsername.value })
if (deviceId) await deviceApi.remove({ deviceId, username: currentUsername.value })
} catch (error) {
console.warn('离线通知失败:', error)
}
@@ -253,7 +247,6 @@ async function handleUserClick() {
cancelButtonText: '取消'
})
await logout()
ElMessage.success('已退出登录')
} catch {}
}
@@ -366,7 +359,7 @@ const SSEManager = {
break
case 'DEVICE_REMOVED':
clearLocalAuth()
ElMessage.warning('您的设备已被移除,请重新登录')
ElMessage.warning('会话已失效,请重新登录')
break
case 'FORCE_LOGOUT':
logout()

View File

@@ -27,7 +27,6 @@ import java.util.concurrent.ConcurrentHashMap;
*/
@Service
public class AmazonScrapingServiceImpl implements IAmazonScrapingService, PageProcessor {
private static final Logger logger = LoggerFactory.getLogger(AmazonScrapingServiceImpl.class);
@Autowired
private AmazonProductRepository amazonProductRepository;
@Autowired
@@ -102,7 +101,7 @@ public class AmazonScrapingServiceImpl implements IAmazonScrapingService, PagePr
@Override
public List<AmazonProductEntity> batchGetProductInfo(List<String> asinList, String batchId, String region) {
String sessionId = (batchId != null) ? batchId : "SINGLE_" + UUID.randomUUID();
LocalDateTime batchTime = LocalDateTime.now(); // 统一的批次时间
LocalDateTime batchTime = LocalDateTime.now();
// 第一步清理1小时前的所有旧数据
amazonProductRepository.deleteAllDataBefore(LocalDateTime.now().minusHours(1));

View File

@@ -24,40 +24,6 @@ public class ClientDeviceController {
private ClientAccountMapper clientAccountMapper;
@Autowired
private SseHubService sseHubService;
private static final int DEFAULT_LIMIT = 3;
/**
* 获取账号的设备数量限制
*
* @param username 用户名
* @return 设备数量限制,如果账号不存在或未配置则返回默认值
*/
private int getDeviceLimit(String username) {
if (username == null || username.isEmpty()) {
return DEFAULT_LIMIT;
}
ClientAccount account = clientAccountMapper.selectClientAccountByUsername(username);
if (account == null || account.getDeviceLimit() == null) {
return DEFAULT_LIMIT;
}
return account.getDeviceLimit();
}
/**
* 检查设备数量限制
*
* @param username 用户名
* @param currentDeviceId 当前设备ID检查时排除此设备
* @throws RuntimeException 如果设备数量已达上限
*/
private void checkDeviceLimit(String username, String currentDeviceId) {
int deviceLimit = getDeviceLimit(username);
List<ClientDevice> userDevices = clientDeviceMapper.selectByUsername(username);
if (userDevices.size() >= deviceLimit) {
throw new RuntimeException("设备数量已达上限(" + deviceLimit + "个),请先移除其他设备");
}
}
/**
* 查询设备配额与已使用数量
@@ -72,7 +38,8 @@ public class ClientDeviceController {
for (ClientDevice d : all) {
if (!"removed".equals(d.getStatus())) used++;
}
int limit = getDeviceLimit(username);
ClientAccount account = clientAccountMapper.selectClientAccountByUsername(username);
int limit = (account != null && account.getDeviceLimit() != null) ? account.getDeviceLimit() : 3;
Map<String, Object> map = new HashMap<>();
map.put("limit", limit);
map.put("used", used);
@@ -102,14 +69,21 @@ public class ClientDeviceController {
String deviceId = device.getDeviceId();
String os = device.getOs();
String deviceName = username + "@" + ip + " (" + os + ")";
// 检查设备数量限制
ClientAccount account = clientAccountMapper.selectClientAccountByUsername(username);
int deviceLimit = (account != null && account.getDeviceLimit() != null) ? account.getDeviceLimit() : 3;
List<ClientDevice> userDevices = clientDeviceMapper.selectByUsername(username);
int userDevice = userDevices.size();
boolean deviceExists = userDevices.stream().anyMatch(d -> deviceId.equals(d.getDeviceId()));
if (deviceExists) userDevice--;
if (userDevice >= deviceLimit) {
return AjaxResult.error("设备数量已达上限(" + deviceLimit + "个),请先移除其他设备");
}
ClientDevice exists = clientDeviceMapper.selectByDeviceIdAndUsername(deviceId, username);
if (exists == null) {
// 检查设备数量限制
try {
checkDeviceLimit(username, deviceId);
} catch (RuntimeException e) {
return AjaxResult.error(e.getMessage());
}
device.setIp(ip);
device.setStatus("online");
device.setLastActiveAt(new java.util.Date());
@@ -117,6 +91,7 @@ public class ClientDeviceController {
device.setName(deviceName);
clientDeviceMapper.insert(device);
} else {
exists.setName(deviceName);
exists.setOs(os);
exists.setStatus("online");
@@ -188,48 +163,6 @@ public class ClientDeviceController {
return AjaxResult.success();
}
/**
* 设备心跳
* 若设备未注册则按注册逻辑插入;已注册则更新在线状态和设备信息
* 所有情况都检查设备数量限制,被移除设备允许重新注册(需检查配额)
*/
@PostMapping("/heartbeat")
public AjaxResult heartbeat(@RequestBody ClientDevice device, HttpServletRequest request) {
String ip = IpUtils.getIpAddr(request);
String username = device.getUsername();
String deviceId = device.getDeviceId();
String os = device.getOs();
String deviceName = username + "@" + ip + " (" + os + ")";
ClientDevice exists = clientDeviceMapper.selectByDeviceIdAndUsername(deviceId, username);
if (exists == null) {
// 新设备注册
device.setIp(ip);
device.setStatus("online");
device.setLastActiveAt(new java.util.Date());
device.setTrialExpireTime(new java.util.Date(System.currentTimeMillis() + 3 * 24L * 60 * 60 * 1000));
device.setName(deviceName);
clientDeviceMapper.insert(device);
} else if ("removed".equals(exists.getStatus())) {
// 被移除设备重新激活
exists.setName(deviceName);
exists.setOs(os);
exists.setStatus("online");
exists.setIp(ip);
exists.setLocation(device.getLocation());
exists.setLastActiveAt(new java.util.Date());
clientDeviceMapper.updateByDeviceIdAndUsername(exists);
} else {
// 已存在设备更新
exists.setStatus("online");
exists.setIp(ip);
exists.setLastActiveAt(new java.util.Date());
exists.setName(deviceName);
clientDeviceMapper.updateByDeviceIdAndUsername(exists);
}
return AjaxResult.success();
}
}

View File

@@ -96,8 +96,8 @@
</el-table-column>
<el-table-column label="过期时间" align="center" prop="expireTime" width="130">
<template slot-scope="scope">
<el-tag
:type="getRemainingDays(scope.row.expireTime).type"
<el-tag
:type="getRemainingDays(scope.row.expireTime).type"
size="small"
>
{{ getRemainingDays(scope.row.expireTime).text }}
@@ -186,14 +186,14 @@
</div>
</el-form-item>
<el-form-item label="设备数量限制" prop="deviceLimit">
<el-input-number
v-model="form.deviceLimit"
:min="1"
:max="20"
<el-input-number
v-model="form.deviceLimit"
:min="1"
:max="20"
placeholder="请输入设备数量限制"
style="width: 100%;"
></el-input-number>
<span style="color: #909399; font-size: 12px;">允许同时登录的设备数量默认3</span>
<span style="color: #909399; font-size: 12px;">允许同时登录的设备数量默认1</span>
</el-form-item>
<el-form-item label="功能权限" prop="permissions">
<div class="permission-config">
@@ -232,8 +232,8 @@
</el-table-column>
<el-table-column label="设备试用期" prop="trialExpireTime" width="130" align="center">
<template slot-scope="scope">
<el-tag
:type="getRemainingDays(scope.row.trialExpireTime).type"
<el-tag
:type="getRemainingDays(scope.row.trialExpireTime).type"
size="small"
>
{{ getRemainingDays(scope.row.trialExpireTime).text }}
@@ -472,7 +472,7 @@ export default {
}
// 执行更新
promises.push(updateAccount(this.form));
Promise.all(promises).then(() => {
const msg = this.form.renewDays ? "修改并续费成功" : "修改成功";
this.$modal.msgSuccess(msg);
@@ -635,7 +635,7 @@ export default {
/** 计算续费后的新到期时间 */
calculateNewExpireTime() {
if (!this.form.renewDays) return '';
let baseDate;
if (this.form.expireTime && new Date(this.form.expireTime) > new Date()) {
// 未过期,从到期时间延长
@@ -644,7 +644,7 @@ export default {
// 已过期或无到期时间,从当前时间开始
baseDate = new Date();
}
baseDate.setDate(baseDate.getDate() + this.form.renewDays);
return baseDate.toLocaleString('zh-CN', {
year: 'numeric',
@@ -660,12 +660,12 @@ export default {
if (!expireTime) {
return { text: '未设置', type: 'info' };
}
const now = new Date();
const expireDate = new Date(expireTime);
const diffTime = expireDate - now;
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
if (diffDays < 0) {
return { text: '已过期', type: 'danger' };
} else if (diffDays <= 3) {