feat(electron-vue-template):重构认证与设备管理模块
- 统一token存取逻辑,封装getToken/setToken/removeToken方法 -优化设备ID获取逻辑,调整API路径 - 完善设备管理接口类型定义,增强类型安全 - 调整SSE连接逻辑,使用统一配置管理- 重构HTTP客户端,集中管理后端服务配置 - 更新认证相关API接口,完善请求/响应类型 - 优化设备列表展示逻辑,移除冗余字段 - 调整图片代理路径,统一API前缀 - 完善用户反馈列表展示功能,增强交互体验 - 移除冗余的错误处理逻辑,简化代码结构
This commit is contained in:
@@ -130,9 +130,8 @@ public class ClientAccountController extends BaseController {
|
||||
public AjaxResult login(@RequestBody Map<String, String> loginData) {
|
||||
String username = loginData.get("username");
|
||||
String password = loginData.get("password");
|
||||
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
|
||||
return AjaxResult.error("用户名和密码不能为空");
|
||||
}
|
||||
String clientId = loginData.get("clientId");
|
||||
|
||||
ClientAccount account = clientAccountService.selectClientAccountByUsername(username);
|
||||
if (account == null || !passwordEncoder.matches(password, account.getPassword())) {
|
||||
return AjaxResult.error("用户名或密码错误");
|
||||
@@ -142,24 +141,32 @@ public class ClientAccountController extends BaseController {
|
||||
}
|
||||
|
||||
// 检查设备数量限制
|
||||
String clientId = loginData.get("clientId");
|
||||
int deviceLimit = account.getDeviceLimit();
|
||||
List<ClientDevice> userDevices = clientDeviceMapper.selectByUsername(username);
|
||||
int userDevice = userDevices.size();
|
||||
boolean exists = userDevices.stream()
|
||||
.anyMatch(d -> clientId.equals(d.getDeviceId()));
|
||||
if(exists)userDevice--;
|
||||
int userDevice = userDevices.size();
|
||||
boolean exists = userDevices.stream().anyMatch(d -> clientId.equals(d.getDeviceId()));
|
||||
if (exists) userDevice--;
|
||||
if (userDevice >= deviceLimit) {
|
||||
return AjaxResult.error("设备数量已达上限(" + deviceLimit + "个),请先移除其他设备");
|
||||
}
|
||||
|
||||
String accessToken = Jwts.builder().setHeaderParam("kid", jwtRsaKeyService.getKeyId()).setSubject(username).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + JWT_EXPIRATION)).claim("accountId", account.getId()).claim("username", username).claim("clientId", clientId).signWith(SignatureAlgorithm.RS256, jwtRsaKeyService.getPrivateKey()).compact();
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("accessToken", accessToken);
|
||||
result.put("permissions", account.getPermissions());
|
||||
result.put("accountName", account.getAccountName());
|
||||
result.put("expireTime", account.getExpireTime());
|
||||
return AjaxResult.success("登录成功", result);
|
||||
String token = Jwts.builder()
|
||||
.setHeaderParam("kid", jwtRsaKeyService.getKeyId())
|
||||
.setSubject(username)
|
||||
.setIssuedAt(new Date())
|
||||
.setExpiration(new Date(System.currentTimeMillis() + JWT_EXPIRATION))
|
||||
.claim("accountId", account.getId())
|
||||
.claim("username", username)
|
||||
.claim("clientId", clientId)
|
||||
.signWith(SignatureAlgorithm.RS256, jwtRsaKeyService.getPrivateKey())
|
||||
.compact();
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("token", token);
|
||||
data.put("permissions", account.getPermissions());
|
||||
data.put("accountName", account.getAccountName());
|
||||
data.put("expireTime", account.getExpireTime());
|
||||
return AjaxResult.success(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -169,29 +176,23 @@ public class ClientAccountController extends BaseController {
|
||||
@PostMapping("/verify")
|
||||
public AjaxResult verifyToken(@RequestBody Map<String, String> data) {
|
||||
String token = data.get("token");
|
||||
if (StringUtils.isEmpty(token)) {
|
||||
return AjaxResult.error("token不能为空");
|
||||
}
|
||||
Map<String, Object> claims = Jwts.parser().setSigningKey(jwtRsaKeyService.getPublicKey()).parseClaimsJws(token).getBody();
|
||||
Map<String, Object> claims = Jwts.parser()
|
||||
.setSigningKey(jwtRsaKeyService.getPublicKey())
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
String username = (String) claims.get("sub");
|
||||
|
||||
ClientAccount account = clientAccountService.selectClientAccountByUsername(username);
|
||||
if (account == null || !"0".equals(account.getStatus())) {
|
||||
return AjaxResult.error("token无效");
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("username", username);
|
||||
result.put("permissions", account.getPermissions());
|
||||
result.put("accountName", account.getAccountName());
|
||||
result.put("expireTime", account.getExpireTime());
|
||||
// 计算VIP状态
|
||||
if (account.getExpireTime() != null) {
|
||||
boolean isExpired = account.getExpireTime().before(new Date());
|
||||
result.put("isVip", !isExpired);
|
||||
} else {
|
||||
result.put("isVip", false);
|
||||
}
|
||||
|
||||
return AjaxResult.success("验证成功", result);
|
||||
return AjaxResult.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,6 +224,7 @@ public class ClientAccountController extends BaseController {
|
||||
String username = registerData.get("username");
|
||||
String password = registerData.get("password");
|
||||
String deviceId = registerData.get("deviceId");
|
||||
|
||||
ClientAccount clientAccount = new ClientAccount();
|
||||
clientAccount.setUsername(username);
|
||||
clientAccount.setAccountName(username);
|
||||
@@ -231,22 +233,12 @@ public class ClientAccountController extends BaseController {
|
||||
clientAccount.setPermissions("{\"amazon\":true,\"rakuten\":true,\"zebra\":true}");
|
||||
clientAccount.setPassword(passwordEncoder.encode(password));
|
||||
|
||||
// 检查设备ID是否已注册过(赠送VIP逻辑)
|
||||
boolean isNewDevice = true;
|
||||
if (!StringUtils.isEmpty(deviceId)) {
|
||||
ClientDevice existingDevice = clientDeviceMapper.selectByDeviceId(deviceId);
|
||||
isNewDevice = (existingDevice == null);
|
||||
}
|
||||
int vipDays;
|
||||
if (isNewDevice) {
|
||||
vipDays = 3;
|
||||
} else {
|
||||
vipDays = 0; // 立即过期,需要续费
|
||||
}
|
||||
|
||||
// 新设备赠送3天VIP
|
||||
ClientDevice existingDevice = clientDeviceMapper.selectByDeviceId(deviceId);
|
||||
int vipDays = (existingDevice == null) ? 3 : 0;
|
||||
|
||||
if (vipDays > 0) {
|
||||
Date expireDate = new Date(System.currentTimeMillis() + vipDays * 24L * 60 * 60 * 1000);
|
||||
clientAccount.setExpireTime(expireDate);
|
||||
clientAccount.setExpireTime(new Date(System.currentTimeMillis() + vipDays * 24L * 60 * 60 * 1000));
|
||||
} else {
|
||||
clientAccount.setExpireTime(new Date());
|
||||
}
|
||||
@@ -256,14 +248,22 @@ public class ClientAccountController extends BaseController {
|
||||
return AjaxResult.error("注册失败");
|
||||
}
|
||||
|
||||
String accessToken = Jwts.builder().setHeaderParam("kid", jwtRsaKeyService.getKeyId()).setSubject(clientAccount.getUsername()).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + JWT_EXPIRATION)).claim("accountId", clientAccount.getId()).claim("clientId", deviceId).signWith(SignatureAlgorithm.RS256, jwtRsaKeyService.getPrivateKey()).compact();
|
||||
String token = Jwts.builder()
|
||||
.setHeaderParam("kid", jwtRsaKeyService.getKeyId())
|
||||
.setSubject(clientAccount.getUsername())
|
||||
.setIssuedAt(new Date())
|
||||
.setExpiration(new Date(System.currentTimeMillis() + JWT_EXPIRATION))
|
||||
.claim("accountId", clientAccount.getId())
|
||||
.claim("clientId", deviceId)
|
||||
.signWith(SignatureAlgorithm.RS256, jwtRsaKeyService.getPrivateKey())
|
||||
.compact();
|
||||
|
||||
Map<String, Object> dataMap = new HashMap<>();
|
||||
dataMap.put("accessToken", accessToken);
|
||||
dataMap.put("permissions", clientAccount.getPermissions());
|
||||
dataMap.put("accountName", clientAccount.getAccountName());
|
||||
dataMap.put("expireTime", clientAccount.getExpireTime());
|
||||
return AjaxResult.success("注册成功", dataMap);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("token", token);
|
||||
data.put("permissions", clientAccount.getPermissions());
|
||||
data.put("accountName", clientAccount.getAccountName());
|
||||
data.put("expireTime", clientAccount.getExpireTime());
|
||||
return AjaxResult.success(data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -271,9 +271,6 @@ public class ClientAccountController extends BaseController {
|
||||
*/
|
||||
@GetMapping("/check-username")
|
||||
public AjaxResult checkUsername(@RequestParam("username") String username) {
|
||||
if (StringUtils.isEmpty(username)) {
|
||||
return AjaxResult.error("用户名不能为空");
|
||||
}
|
||||
ClientAccount account = clientAccountService.selectClientAccountByUsername(username);
|
||||
return AjaxResult.success(account == null);
|
||||
}
|
||||
@@ -306,14 +303,13 @@ public class ClientAccountController extends BaseController {
|
||||
account.setUpdateBy(getUsername());
|
||||
clientAccountService.updateClientAccount(account);
|
||||
|
||||
// 通过SSE推送续费通知给该账号的所有在线设备
|
||||
try {
|
||||
sseHubService.sendEventToAllDevices(account.getUsername(), "VIP_RENEWED", "{\"expireTime\":\"" + newExpireTime + "\"}");
|
||||
} catch (Exception e) {
|
||||
// SSE推送失败不影响续费操作
|
||||
}
|
||||
// 推送续费通知
|
||||
sseHubService.sendEventToAllDevices(account.getUsername(), "VIP_RENEWED",
|
||||
"{\"expireTime\":\"" + newExpireTime + "\"}");
|
||||
|
||||
return AjaxResult.success("续费成功,新的过期时间:" + newExpireTime);
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("expireTime", newExpireTime);
|
||||
return AjaxResult.success(result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
package com.ruoyi.web.controller.monitor;
|
||||
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.system.domain.ClientFeedback;
|
||||
import com.ruoyi.web.service.IClientFeedbackService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 客户端反馈控制器
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/monitor/feedback")
|
||||
public class ClientFeedbackController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private IClientFeedbackService feedbackService;
|
||||
|
||||
@Value("${feedback.log.path:C:/ProgramData/erp-logs/feedback/}")
|
||||
private String feedbackLogPath;
|
||||
|
||||
/**
|
||||
* 提交用户反馈(客户端调用)
|
||||
*/
|
||||
@Anonymous
|
||||
@PostMapping("/submit")
|
||||
public AjaxResult submitFeedback(
|
||||
@RequestParam("username") String username,
|
||||
@RequestParam("deviceId") String deviceId,
|
||||
@RequestParam("feedbackContent") String feedbackContent,
|
||||
@RequestParam(value = "logDate", required = false) String logDate,
|
||||
@RequestParam(value = "logFile", required = false) MultipartFile logFile) {
|
||||
|
||||
try {
|
||||
ClientFeedback feedback = new ClientFeedback();
|
||||
feedback.setUsername(username);
|
||||
feedback.setDeviceId(deviceId);
|
||||
feedback.setFeedbackContent(feedbackContent);
|
||||
feedback.setStatus("pending");
|
||||
|
||||
// 处理日志文件上传
|
||||
if (logFile != null && !logFile.isEmpty()) {
|
||||
String logFilePath = saveLogFile(username, deviceId, logFile);
|
||||
feedback.setLogFilePath(logFilePath);
|
||||
|
||||
// 解析日志日期
|
||||
if (logDate != null && !logDate.isEmpty()) {
|
||||
try {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
||||
feedback.setLogDate(sdf.parse(logDate));
|
||||
} catch (Exception e) {
|
||||
logger.warn("解析日志日期失败: {}", logDate, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
feedbackService.insertFeedback(feedback);
|
||||
return AjaxResult.success("success");
|
||||
} catch (Exception e) {
|
||||
logger.error("提交反馈失败", e);
|
||||
return AjaxResult.error("反馈提交失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询反馈列表(管理端调用)
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('monitor:feedback:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(ClientFeedback feedback) {
|
||||
startPage();
|
||||
List<ClientFeedback> list = feedbackService.selectFeedbackList(feedback);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取反馈详情
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('monitor:feedback:query')")
|
||||
@GetMapping("/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") Long id) {
|
||||
return AjaxResult.success(feedbackService.selectFeedbackById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取反馈附带的日志内容(用于在线查看)
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('monitor:feedback:query')")
|
||||
@GetMapping("/log/content/{id}")
|
||||
public AjaxResult getLogContent(@PathVariable("id") Long id) {
|
||||
try {
|
||||
ClientFeedback feedback = feedbackService.selectFeedbackById(id);
|
||||
if (feedback == null) {
|
||||
return AjaxResult.error("反馈记录不存在");
|
||||
}
|
||||
|
||||
String logFilePath = feedback.getLogFilePath();
|
||||
if (logFilePath == null || logFilePath.isEmpty()) {
|
||||
return AjaxResult.error("该反馈未附带日志文件");
|
||||
}
|
||||
|
||||
File logFile = new File(logFilePath);
|
||||
if (!logFile.exists()) {
|
||||
return AjaxResult.error("日志文件不存在");
|
||||
}
|
||||
|
||||
// 读取日志文件内容
|
||||
StringBuilder content = new StringBuilder();
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(logFile))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
content.append(line).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("content", content.toString());
|
||||
result.put("fileName", logFile.getName());
|
||||
result.put("fileSize", logFile.length());
|
||||
|
||||
return AjaxResult.success(result);
|
||||
} catch (Exception e) {
|
||||
logger.error("读取日志文件失败", e);
|
||||
return AjaxResult.error("读取失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载反馈附带的日志文件
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('monitor:feedback:query')")
|
||||
@GetMapping("/log/download/{id}")
|
||||
public void downloadLog(@PathVariable("id") Long id, HttpServletResponse response) {
|
||||
try {
|
||||
ClientFeedback feedback = feedbackService.selectFeedbackById(id);
|
||||
if (feedback == null) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
response.getWriter().write("反馈记录不存在");
|
||||
return;
|
||||
}
|
||||
|
||||
String logFilePath = feedback.getLogFilePath();
|
||||
if (logFilePath == null || logFilePath.isEmpty()) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
response.getWriter().write("该反馈未附带日志文件");
|
||||
return;
|
||||
}
|
||||
|
||||
File logFile = new File(logFilePath);
|
||||
if (!logFile.exists()) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
response.getWriter().write("日志文件不存在");
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置响应头
|
||||
response.setContentType("application/octet-stream");
|
||||
response.setHeader("Content-Disposition",
|
||||
"attachment; filename=" + logFile.getName());
|
||||
response.setContentLengthLong(logFile.length());
|
||||
|
||||
// 读取文件并写入响应
|
||||
try (FileInputStream fis = new FileInputStream(logFile);
|
||||
OutputStream os = response.getOutputStream()) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int bytesRead;
|
||||
while ((bytesRead = fis.read(buffer)) != -1) {
|
||||
os.write(buffer, 0, bytesRead);
|
||||
}
|
||||
os.flush();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("下载日志文件失败", e);
|
||||
try {
|
||||
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
response.getWriter().write("下载失败: " + e.getMessage());
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新反馈状态
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('monitor:feedback:edit')")
|
||||
@Log(title = "客户端反馈", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/status/{id}")
|
||||
public AjaxResult updateStatus(@PathVariable("id") Long id, @RequestBody Map<String, String> params) {
|
||||
String status = params.get("status");
|
||||
String remark = params.get("remark");
|
||||
|
||||
ClientFeedback feedback = new ClientFeedback();
|
||||
feedback.setId(id);
|
||||
feedback.setStatus(status);
|
||||
feedback.setRemark(remark);
|
||||
|
||||
int result = feedbackService.updateFeedback(feedback);
|
||||
return toAjax(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除反馈
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('monitor:feedback:remove')")
|
||||
@Log(title = "客户端反馈", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{id}")
|
||||
public AjaxResult remove(@PathVariable("id") Long id) {
|
||||
// 删除前先删除日志文件
|
||||
ClientFeedback feedback = feedbackService.selectFeedbackById(id);
|
||||
if (feedback != null && feedback.getLogFilePath() != null) {
|
||||
try {
|
||||
File logFile = new File(feedback.getLogFilePath());
|
||||
if (logFile.exists()) {
|
||||
logFile.delete();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("删除日志文件失败: {}", feedback.getLogFilePath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
return toAjax(feedbackService.deleteFeedbackById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取反馈统计信息
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('monitor:feedback:list')")
|
||||
@GetMapping("/statistics")
|
||||
public AjaxResult getStatistics() {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
stats.put("pendingCount", feedbackService.countPendingFeedback());
|
||||
stats.put("todayCount", feedbackService.countTodayFeedback());
|
||||
return AjaxResult.success(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存日志文件
|
||||
*/
|
||||
private String saveLogFile(String username, String deviceId, MultipartFile file) throws IOException {
|
||||
// 确保目录存在
|
||||
Path uploadPath = Paths.get(feedbackLogPath);
|
||||
if (!Files.exists(uploadPath)) {
|
||||
Files.createDirectories(uploadPath);
|
||||
}
|
||||
|
||||
// 生成文件名: username_deviceId_timestamp.log
|
||||
String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
|
||||
String fileName = String.format("%s_%s_%s.log", username, deviceId, timestamp);
|
||||
Path filePath = uploadPath.resolve(fileName);
|
||||
|
||||
// 保存文件
|
||||
file.transferTo(filePath.toFile());
|
||||
|
||||
return filePath.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,12 +98,8 @@ public class ClientMonitorController extends BaseController {
|
||||
*/
|
||||
@PostMapping("/api/auth")
|
||||
public AjaxResult clientAuth(@RequestBody Map<String, Object> authData) {
|
||||
try {
|
||||
String authKey = (String) authData.get("authKey");
|
||||
return AjaxResult.success("认证成功", clientMonitorService.authenticateClient(authKey, authData));
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error("认证失败:" + e.getMessage());
|
||||
}
|
||||
String authKey = (String) authData.get("authKey");
|
||||
return AjaxResult.success(clientMonitorService.authenticateClient(authKey, authData));
|
||||
}
|
||||
|
||||
|
||||
@@ -112,25 +108,14 @@ public class ClientMonitorController extends BaseController {
|
||||
*/
|
||||
@PostMapping("/api/error")
|
||||
public AjaxResult clientError(@RequestBody Map<String, Object> errorData) {
|
||||
try {
|
||||
clientMonitorService.recordErrorReport(errorData);
|
||||
return AjaxResult.success();
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
clientMonitorService.recordErrorReport(errorData);
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端数据上报API
|
||||
*/
|
||||
@PostMapping("/api/data")
|
||||
public AjaxResult clientDataReport(@RequestBody Map<String, Object> dataReport) {
|
||||
try {
|
||||
clientMonitorService.recordDataReport(dataReport);
|
||||
return AjaxResult.success();
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
clientMonitorService.recordDataReport(dataReport);
|
||||
return AjaxResult.success();
|
||||
}
|
||||
/**
|
||||
* 获取特定客户端的详细信息
|
||||
@@ -155,12 +140,8 @@ public class ClientMonitorController extends BaseController {
|
||||
@PreAuthorize("@ss.hasPermi('monitor:client:export')")
|
||||
@PostMapping("/cleanup")
|
||||
public AjaxResult cleanupExpiredData() {
|
||||
try {
|
||||
clientMonitorService.cleanExpiredData();
|
||||
return AjaxResult.success("过期数据清理完成");
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error("清理过期数据失败: " + e.getMessage());
|
||||
}
|
||||
clientMonitorService.cleanExpiredData();
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -34,28 +34,17 @@ public class VersionController extends BaseController {
|
||||
*/
|
||||
@GetMapping("/check")
|
||||
public AjaxResult checkVersion(@RequestParam String currentVersion) {
|
||||
try {
|
||||
// 从Redis获取最新版本信息
|
||||
String latestVersion = redisTemplate.opsForValue().get(VERSION_REDIS_KEY);
|
||||
// 比较版本号
|
||||
boolean needUpdate = compareVersions(currentVersion, latestVersion) < 0;
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("currentVersion", currentVersion);
|
||||
result.put("latestVersion", latestVersion);
|
||||
result.put("needUpdate", needUpdate);
|
||||
// 从Redis获取下载链接
|
||||
String asarUrl = redisTemplate.opsForValue().get(ASAR_URL_REDIS_KEY);
|
||||
String jarUrl = redisTemplate.opsForValue().get(JAR_URL_REDIS_KEY);
|
||||
result.put("asarUrl", asarUrl);
|
||||
result.put("jarUrl", jarUrl);
|
||||
// 兼容旧版本,保留downloadUrl字段(指向asar)
|
||||
result.put("downloadUrl", asarUrl);
|
||||
|
||||
return AjaxResult.success(result);
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error("版本检查失败: " + e.getMessage());
|
||||
}
|
||||
String latestVersion = redisTemplate.opsForValue().get(VERSION_REDIS_KEY);
|
||||
boolean needUpdate = compareVersions(currentVersion, latestVersion) < 0;
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("currentVersion", currentVersion);
|
||||
data.put("latestVersion", latestVersion);
|
||||
data.put("needUpdate", needUpdate);
|
||||
data.put("asarUrl", redisTemplate.opsForValue().get(ASAR_URL_REDIS_KEY));
|
||||
data.put("jarUrl", redisTemplate.opsForValue().get(JAR_URL_REDIS_KEY));
|
||||
|
||||
return AjaxResult.success(data);
|
||||
}
|
||||
/**
|
||||
* 获取当前版本信息
|
||||
@@ -63,24 +52,18 @@ public class VersionController extends BaseController {
|
||||
@PreAuthorize("@ss.hasPermi('system:version:query')")
|
||||
@GetMapping("/info")
|
||||
public AjaxResult getVersionInfo() {
|
||||
try {
|
||||
String currentVersion = redisTemplate.opsForValue().get(VERSION_REDIS_KEY);
|
||||
if (StringUtils.isEmpty(currentVersion)) {
|
||||
currentVersion = "2.0.0";
|
||||
}
|
||||
String asarUrl = redisTemplate.opsForValue().get(ASAR_URL_REDIS_KEY);
|
||||
String jarUrl = redisTemplate.opsForValue().get(JAR_URL_REDIS_KEY);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("currentVersion", currentVersion);
|
||||
result.put("asarUrl", asarUrl);
|
||||
result.put("jarUrl", jarUrl);
|
||||
result.put("updateTime", System.currentTimeMillis());
|
||||
|
||||
return AjaxResult.success(result);
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error("获取版本信息失败: " + e.getMessage());
|
||||
String currentVersion = redisTemplate.opsForValue().get(VERSION_REDIS_KEY);
|
||||
if (StringUtils.isEmpty(currentVersion)) {
|
||||
currentVersion = "2.0.0";
|
||||
}
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("currentVersion", currentVersion);
|
||||
data.put("asarUrl", redisTemplate.opsForValue().get(ASAR_URL_REDIS_KEY));
|
||||
data.put("jarUrl", redisTemplate.opsForValue().get(JAR_URL_REDIS_KEY));
|
||||
data.put("updateTime", System.currentTimeMillis());
|
||||
|
||||
return AjaxResult.success(data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,23 +75,20 @@ public class VersionController extends BaseController {
|
||||
public AjaxResult updateVersionInfo(@RequestParam("version") String version,
|
||||
@RequestParam(value = "asarUrl", required = false) String asarUrl,
|
||||
@RequestParam(value = "jarUrl", required = false) String jarUrl) {
|
||||
try {
|
||||
redisTemplate.opsForValue().set(VERSION_REDIS_KEY, version);
|
||||
if (StringUtils.isNotEmpty(asarUrl)) {
|
||||
redisTemplate.opsForValue().set(ASAR_URL_REDIS_KEY, asarUrl);
|
||||
}
|
||||
if (StringUtils.isNotEmpty(jarUrl)) {
|
||||
redisTemplate.opsForValue().set(JAR_URL_REDIS_KEY, jarUrl);
|
||||
}
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("version", version);
|
||||
result.put("asarUrl", asarUrl);
|
||||
result.put("jarUrl", jarUrl);
|
||||
result.put("updateTime", System.currentTimeMillis());
|
||||
return AjaxResult.success("版本信息更新成功", result);
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error("版本信息更新失败: " + e.getMessage());
|
||||
redisTemplate.opsForValue().set(VERSION_REDIS_KEY, version);
|
||||
if (StringUtils.isNotEmpty(asarUrl)) {
|
||||
redisTemplate.opsForValue().set(ASAR_URL_REDIS_KEY, asarUrl);
|
||||
}
|
||||
if (StringUtils.isNotEmpty(jarUrl)) {
|
||||
redisTemplate.opsForValue().set(JAR_URL_REDIS_KEY, jarUrl);
|
||||
}
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("version", version);
|
||||
data.put("asarUrl", asarUrl);
|
||||
data.put("jarUrl", jarUrl);
|
||||
data.put("updateTime", System.currentTimeMillis());
|
||||
return AjaxResult.success(data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.ruoyi.web.service;
|
||||
|
||||
import com.ruoyi.system.domain.ClientFeedback;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 客户端反馈服务接口
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public interface IClientFeedbackService
|
||||
{
|
||||
/**
|
||||
* 查询反馈列表
|
||||
*
|
||||
* @param feedback 反馈信息
|
||||
* @return 反馈集合
|
||||
*/
|
||||
List<ClientFeedback> selectFeedbackList(ClientFeedback feedback);
|
||||
|
||||
/**
|
||||
* 根据ID查询反馈
|
||||
*
|
||||
* @param id 反馈ID
|
||||
* @return 反馈信息
|
||||
*/
|
||||
ClientFeedback selectFeedbackById(Long id);
|
||||
|
||||
/**
|
||||
* 新增反馈
|
||||
*
|
||||
* @param feedback 反馈信息
|
||||
* @return 结果
|
||||
*/
|
||||
int insertFeedback(ClientFeedback feedback);
|
||||
|
||||
/**
|
||||
* 更新反馈
|
||||
*
|
||||
* @param feedback 反馈信息
|
||||
* @return 结果
|
||||
*/
|
||||
int updateFeedback(ClientFeedback feedback);
|
||||
|
||||
/**
|
||||
* 删除反馈
|
||||
*
|
||||
* @param id 反馈ID
|
||||
* @return 结果
|
||||
*/
|
||||
int deleteFeedbackById(Long id);
|
||||
|
||||
/**
|
||||
* 统计待处理反馈数量
|
||||
*
|
||||
* @return 数量
|
||||
*/
|
||||
int countPendingFeedback();
|
||||
|
||||
/**
|
||||
* 统计今日反馈数量
|
||||
*
|
||||
* @return 数量
|
||||
*/
|
||||
int countTodayFeedback();
|
||||
|
||||
/**
|
||||
* 更新反馈状态
|
||||
*
|
||||
* @param id 反馈ID
|
||||
* @param status 状态
|
||||
* @return 结果
|
||||
*/
|
||||
int updateFeedbackStatus(Long id, String status);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.ruoyi.web.service.impl;
|
||||
|
||||
import com.ruoyi.system.domain.ClientFeedback;
|
||||
import com.ruoyi.system.mapper.ClientFeedbackMapper;
|
||||
import com.ruoyi.web.service.IClientFeedbackService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 客户端反馈服务实现
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Service
|
||||
public class ClientFeedbackServiceImpl implements IClientFeedbackService
|
||||
{
|
||||
@Autowired
|
||||
private ClientFeedbackMapper clientFeedbackMapper;
|
||||
|
||||
/**
|
||||
* 查询反馈列表
|
||||
*
|
||||
* @param feedback 反馈信息
|
||||
* @return 反馈集合
|
||||
*/
|
||||
@Override
|
||||
public List<ClientFeedback> selectFeedbackList(ClientFeedback feedback)
|
||||
{
|
||||
return clientFeedbackMapper.selectFeedbackList(feedback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查询反馈
|
||||
*
|
||||
* @param id 反馈ID
|
||||
* @return 反馈信息
|
||||
*/
|
||||
@Override
|
||||
public ClientFeedback selectFeedbackById(Long id)
|
||||
{
|
||||
return clientFeedbackMapper.selectFeedbackById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增反馈
|
||||
*
|
||||
* @param feedback 反馈信息
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int insertFeedback(ClientFeedback feedback)
|
||||
{
|
||||
return clientFeedbackMapper.insertFeedback(feedback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新反馈
|
||||
*
|
||||
* @param feedback 反馈信息
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int updateFeedback(ClientFeedback feedback)
|
||||
{
|
||||
return clientFeedbackMapper.updateFeedback(feedback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除反馈
|
||||
*
|
||||
* @param id 反馈ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteFeedbackById(Long id)
|
||||
{
|
||||
return clientFeedbackMapper.deleteFeedbackById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计待处理反馈数量
|
||||
*
|
||||
* @return 数量
|
||||
*/
|
||||
@Override
|
||||
public int countPendingFeedback()
|
||||
{
|
||||
return clientFeedbackMapper.countPendingFeedback();
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计今日反馈数量
|
||||
*
|
||||
* @return 数量
|
||||
*/
|
||||
@Override
|
||||
public int countTodayFeedback()
|
||||
{
|
||||
return clientFeedbackMapper.countTodayFeedback();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新反馈状态
|
||||
*
|
||||
* @param id 反馈ID
|
||||
* @param status 状态
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int updateFeedbackStatus(Long id, String status)
|
||||
{
|
||||
return clientFeedbackMapper.updateFeedbackStatus(id, status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,17 +65,6 @@ public class SseHubService {
|
||||
String key = buildSessionKey(username, clientId);
|
||||
SseEmitter emitter = sessionEmitters.get(key);
|
||||
if (emitter == null) {
|
||||
try {
|
||||
ClientDevice device = clientDeviceMapper.selectByDeviceId(clientId);
|
||||
// 只有当设备状态不是removed时,才更新为offline
|
||||
if (device != null && !"removed".equals(device.getStatus())) {
|
||||
device.setStatus("offline");
|
||||
device.setLastActiveAt(new Date());
|
||||
clientDeviceMapper.updateByDeviceId(device);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// 静默处理,不影响心跳主流程
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -84,7 +73,7 @@ public class SseHubService {
|
||||
} catch (IOException e) {
|
||||
sessionEmitters.remove(key);
|
||||
try { emitter.complete(); } catch (Exception ignored) {}
|
||||
// 发送失败也更新为离线
|
||||
// 发送失败说明连接已断开,更新为离线
|
||||
updateDeviceStatus(clientId, "offline");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user