feat(electron-vue-template):重构认证与设备管理模块
- 统一token存取逻辑,封装getToken/setToken/removeToken方法 -优化设备ID获取逻辑,调整API路径 - 完善设备管理接口类型定义,增强类型安全 - 调整SSE连接逻辑,使用统一配置管理- 重构HTTP客户端,集中管理后端服务配置 - 更新认证相关API接口,完善请求/响应类型 - 优化设备列表展示逻辑,移除冗余字段 - 调整图片代理路径,统一API前缀 - 完善用户反馈列表展示功能,增强交互体验 - 移除冗余的错误处理逻辑,简化代码结构
This commit is contained in:
@@ -1,55 +0,0 @@
|
||||
package com.tashow.erp.controller;
|
||||
import com.tashow.erp.entity.AuthTokenEntity;
|
||||
import com.tashow.erp.repository.AuthTokenRepository;
|
||||
import com.tashow.erp.utils.JsonData;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 客户端本地服务控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
public class AuthController {
|
||||
@Autowired
|
||||
private AuthTokenRepository authTokenRepository;
|
||||
|
||||
/**
|
||||
* 保存认证密钥
|
||||
*/
|
||||
@PostMapping("/auth/save")
|
||||
public JsonData saveAuth(@RequestBody Map<String, Object> data) {
|
||||
String serviceName = (String) data.get("serviceName");
|
||||
String authKey = (String) data.get("authKey");
|
||||
if (serviceName == null || authKey == null) return JsonData.buildError("serviceName和authKey不能为空");
|
||||
AuthTokenEntity entity = authTokenRepository.findByServiceName(serviceName).orElse(new AuthTokenEntity());
|
||||
entity.setServiceName(serviceName);
|
||||
entity.setToken(authKey);
|
||||
authTokenRepository.save(entity);
|
||||
return JsonData.buildSuccess("认证信息保存成功");
|
||||
}
|
||||
|
||||
@GetMapping("/auth/get")
|
||||
public JsonData getAuth(@RequestParam String serviceName) {
|
||||
return JsonData.buildSuccess(authTokenRepository.findByServiceName(serviceName).map(AuthTokenEntity::getToken).orElse(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除认证密钥
|
||||
*/
|
||||
@DeleteMapping("/auth/remove")
|
||||
public JsonData removeAuth(@RequestParam String serviceName) {
|
||||
authTokenRepository.findByServiceName(serviceName).ifPresent(authTokenRepository::delete);
|
||||
return JsonData.buildSuccess("认证信息删除成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备ID
|
||||
*/
|
||||
@GetMapping("/device-id")
|
||||
public JsonData getDeviceId() {
|
||||
String deviceId = com.tashow.erp.utils.DeviceUtils.generateDeviceId();
|
||||
return JsonData.buildSuccess(deviceId);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.tashow.erp.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 配置信息控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/config")
|
||||
public class ConfigController {
|
||||
|
||||
@Value("${api.server.base-url}")
|
||||
private String serverBaseUrl;
|
||||
|
||||
/**
|
||||
* 获取服务器配置
|
||||
*/
|
||||
@GetMapping("/server")
|
||||
public Map<String, Object> getServerConfig() {
|
||||
return Map.of(
|
||||
"baseUrl", serverBaseUrl,
|
||||
"sseUrl", serverBaseUrl + "/monitor/account/events"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.tashow.erp.controller;
|
||||
|
||||
import com.tashow.erp.service.IGenmaiService;
|
||||
import com.tashow.erp.utils.JsonData;
|
||||
import com.tashow.erp.utils.LoggerUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/genmai")
|
||||
public class GenmaiController {
|
||||
private static final Logger logger = LoggerUtil.getLogger(GenmaiController.class);
|
||||
|
||||
@Autowired
|
||||
private IGenmaiService genmaiService;
|
||||
|
||||
/**
|
||||
* 打开跟卖精灵网页
|
||||
*/
|
||||
@PostMapping("/open")
|
||||
public void openGenmaiWebsite() {
|
||||
genmaiService.openGenmaiWebsite();
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.tashow.erp.controller;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
@Controller
|
||||
public class HomeController {
|
||||
|
||||
@GetMapping("/")
|
||||
public String home() {
|
||||
return "redirect:/html/erp-dashboard.html";
|
||||
}
|
||||
|
||||
@GetMapping("/erp")
|
||||
public String erp() {
|
||||
return "redirect:/html/erp-dashboard.html";
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
package com.tashow.erp.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 代理控制器,用于解决CORS跨域问题
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/proxy")
|
||||
public class ProxyController {
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
/**
|
||||
* 代理获取图片
|
||||
* @param requestBody 包含图片URL的请求体
|
||||
* @return 图片字节数组
|
||||
*/
|
||||
@PostMapping("/image")
|
||||
public ResponseEntity<byte[]> proxyImage(@RequestBody Map<String, String> requestBody) {
|
||||
String imageUrl = requestBody.get("imageUrl");
|
||||
if (imageUrl == null || imageUrl.isEmpty()) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
// 设置请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
|
||||
headers.set("Accept", "image/jpeg, image/png, image/webp, image/*");
|
||||
|
||||
HttpEntity<String> entity = new HttpEntity<>(headers);
|
||||
|
||||
// 发送请求获取图片
|
||||
ResponseEntity<byte[]> response = restTemplate.exchange(
|
||||
imageUrl,
|
||||
HttpMethod.GET,
|
||||
entity,
|
||||
byte[].class
|
||||
);
|
||||
|
||||
// 设置响应头
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.setContentType(MediaType.IMAGE_JPEG);
|
||||
|
||||
return new ResponseEntity<>(response.getBody(), responseHeaders, HttpStatus.OK);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过URL参数代理获取图片
|
||||
* @param imageUrl 图片URL
|
||||
* @return 图片字节数组
|
||||
*/
|
||||
@GetMapping("/image-url")
|
||||
public ResponseEntity<byte[]> proxyImageByUrl(@RequestParam("url") String imageUrl) {
|
||||
if (imageUrl == null || imageUrl.isEmpty()) {
|
||||
System.err.println("图片代理请求失败: 图片URL为空");
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
System.out.println("代理图片请求: " + imageUrl);
|
||||
|
||||
try {
|
||||
// 设置请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
|
||||
headers.set("Accept", "image/jpeg, image/png, image/webp, image/*");
|
||||
headers.set("Referer", "https://item.rakuten.co.jp/");
|
||||
|
||||
HttpEntity<String> entity = new HttpEntity<>(headers);
|
||||
|
||||
// 发送请求获取图片
|
||||
ResponseEntity<byte[]> response = restTemplate.exchange(
|
||||
imageUrl,
|
||||
HttpMethod.GET,
|
||||
entity,
|
||||
byte[].class
|
||||
);
|
||||
|
||||
System.out.println("图片代理成功,响应大小: " + (response.getBody() != null ? response.getBody().length : 0) + " bytes");
|
||||
|
||||
// 设置响应头,支持缓存以提升JavaFX WebView性能
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
|
||||
// 尝试从原始响应中获取Content-Type
|
||||
String contentType = response.getHeaders().getFirst("Content-Type");
|
||||
if (contentType != null && contentType.startsWith("image/")) {
|
||||
responseHeaders.setContentType(MediaType.parseMediaType(contentType));
|
||||
} else {
|
||||
responseHeaders.setContentType(MediaType.IMAGE_JPEG);
|
||||
}
|
||||
|
||||
// 设置缓存头以提升性能
|
||||
responseHeaders.setCacheControl("max-age=3600");
|
||||
// 删除手动CORS设置,使用WebConfig中的全局CORS配置
|
||||
|
||||
return new ResponseEntity<>(response.getBody(), responseHeaders, HttpStatus.OK);
|
||||
} catch (Exception e) {
|
||||
System.err.println("图片代理失败: " + imageUrl + " - " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package com.tashow.erp.controller;
|
||||
|
||||
import com.tashow.erp.entity.AuthTokenEntity;
|
||||
import com.tashow.erp.repository.AuthTokenRepository;
|
||||
import com.tashow.erp.service.IGenmaiService;
|
||||
import com.tashow.erp.utils.JsonData;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 系统级接口控制器
|
||||
* 整合:认证、配置、版本、工具、代理等功能
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/system")
|
||||
public class SystemController {
|
||||
|
||||
@Autowired
|
||||
private AuthTokenRepository authTokenRepository;
|
||||
|
||||
@Autowired
|
||||
private IGenmaiService genmaiService;
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@Value("${project.version:2.3.6}")
|
||||
private String currentVersion;
|
||||
|
||||
@Value("${project.build.time:}")
|
||||
private String buildTime;
|
||||
|
||||
@Value("${api.server.base-url}")
|
||||
private String serverBaseUrl;
|
||||
|
||||
// ==================== 认证管理 ====================
|
||||
|
||||
/**
|
||||
* 保存认证密钥
|
||||
*/
|
||||
@PostMapping("/auth/save")
|
||||
public JsonData saveAuth(@RequestBody Map<String, Object> data) {
|
||||
String serviceName = (String) data.get("serviceName");
|
||||
String authKey = (String) data.get("authKey");
|
||||
if (serviceName == null || authKey == null) {
|
||||
return JsonData.buildError("serviceName和authKey不能为空");
|
||||
}
|
||||
AuthTokenEntity entity = authTokenRepository.findByServiceName(serviceName)
|
||||
.orElse(new AuthTokenEntity());
|
||||
entity.setServiceName(serviceName);
|
||||
entity.setToken(authKey);
|
||||
authTokenRepository.save(entity);
|
||||
return JsonData.buildSuccess("认证信息保存成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取认证密钥
|
||||
*/
|
||||
@GetMapping("/auth/get")
|
||||
public JsonData getAuth(@RequestParam String serviceName) {
|
||||
return JsonData.buildSuccess(authTokenRepository.findByServiceName(serviceName)
|
||||
.map(AuthTokenEntity::getToken)
|
||||
.orElse(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除认证密钥
|
||||
*/
|
||||
@DeleteMapping("/auth/remove")
|
||||
public JsonData removeAuth(@RequestParam String serviceName) {
|
||||
authTokenRepository.findByServiceName(serviceName)
|
||||
.ifPresent(authTokenRepository::delete);
|
||||
return JsonData.buildSuccess("认证信息删除成功");
|
||||
}
|
||||
|
||||
// ==================== 设备管理 ====================
|
||||
|
||||
/**
|
||||
* 获取设备ID
|
||||
*/
|
||||
@GetMapping("/device-id")
|
||||
public JsonData getDeviceId() {
|
||||
String deviceId = com.tashow.erp.utils.DeviceUtils.generateDeviceId();
|
||||
return JsonData.buildSuccess(deviceId);
|
||||
}
|
||||
|
||||
// ==================== 版本信息 ====================
|
||||
|
||||
/**
|
||||
* 获取当前版本号
|
||||
*/
|
||||
@GetMapping("/version")
|
||||
public Map<String, Object> getVersion() {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", true);
|
||||
result.put("currentVersion", currentVersion);
|
||||
result.put("buildTime", buildTime);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== 配置信息 ====================
|
||||
|
||||
/**
|
||||
* 获取服务器配置
|
||||
*/
|
||||
@GetMapping("/config/server")
|
||||
public Map<String, Object> getServerConfig() {
|
||||
return Map.of(
|
||||
"baseUrl", serverBaseUrl,
|
||||
"sseUrl", serverBaseUrl + "/monitor/account/events"
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== 工具功能 ====================
|
||||
|
||||
/**
|
||||
* 打开跟卖精灵网页
|
||||
*/
|
||||
@PostMapping("/genmai/open")
|
||||
public void openGenmaiWebsite() {
|
||||
genmaiService.openGenmaiWebsite();
|
||||
}
|
||||
|
||||
// ==================== 图片代理 ====================
|
||||
|
||||
/**
|
||||
* 代理获取图片(解决CORS跨域问题)
|
||||
*/
|
||||
@GetMapping("/proxy/image")
|
||||
public ResponseEntity<byte[]> proxyImage(@RequestParam("url") String imageUrl) {
|
||||
if (imageUrl == null || imageUrl.isEmpty()) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
|
||||
headers.set("Accept", "image/jpeg, image/png, image/webp, image/*");
|
||||
headers.set("Referer", "https://item.rakuten.co.jp/");
|
||||
|
||||
HttpEntity<String> entity = new HttpEntity<>(headers);
|
||||
|
||||
ResponseEntity<byte[]> response = restTemplate.exchange(
|
||||
imageUrl,
|
||||
HttpMethod.GET,
|
||||
entity,
|
||||
byte[].class
|
||||
);
|
||||
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
String contentType = response.getHeaders().getFirst("Content-Type");
|
||||
if (contentType != null && contentType.startsWith("image/")) {
|
||||
responseHeaders.setContentType(MediaType.parseMediaType(contentType));
|
||||
} else {
|
||||
responseHeaders.setContentType(MediaType.IMAGE_JPEG);
|
||||
}
|
||||
responseHeaders.setCacheControl("max-age=3600");
|
||||
|
||||
return new ResponseEntity<>(response.getBody(), responseHeaders, HttpStatus.OK);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.tashow.erp.controller;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Web版本信息控制器
|
||||
*
|
||||
* @author Claude
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/update")
|
||||
public class UpdateController {
|
||||
@Value("${project.version:2.3.6}")
|
||||
private String currentVersion;
|
||||
@Value("${project.build.time:}")
|
||||
private String buildTime;
|
||||
|
||||
/**
|
||||
* 获取当前版本号
|
||||
*
|
||||
* @return 当前版本号
|
||||
*/
|
||||
@GetMapping("/version")
|
||||
public Map<String, Object> getVersion() {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", true);
|
||||
result.put("currentVersion", currentVersion);
|
||||
result.put("buildTime", buildTime);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.tashow.erp.test;
|
||||
|
||||
import com.tashow.erp.utils.DeviceUtils;
|
||||
|
||||
/**
|
||||
* 设备ID获取测试
|
||||
* 独立运行,不依赖 Spring Boot
|
||||
*/
|
||||
public class DeviceIdTest {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=================================");
|
||||
System.out.println("设备ID获取测试");
|
||||
System.out.println("=================================\n");
|
||||
|
||||
try {
|
||||
String deviceId = DeviceUtils.generateDeviceId();
|
||||
System.out.println("✓ 成功获取设备ID: " + deviceId);
|
||||
System.out.println("\n设备ID格式说明:");
|
||||
System.out.println(" MGUID_ - Windows MachineGuid(最可靠)");
|
||||
System.out.println(" HW_ - 硬件UUID");
|
||||
System.out.println(" CPU_ - 处理器ID");
|
||||
System.out.println(" MB_ - 主板序列号");
|
||||
System.out.println(" MAC_ - MAC地址");
|
||||
System.out.println(" SYS_ - 系统信息组合");
|
||||
|
||||
// 再次获取,验证稳定性
|
||||
System.out.println("\n验证稳定性(再次获取):");
|
||||
String deviceId2 = DeviceUtils.generateDeviceId();
|
||||
System.out.println("第二次获取: " + deviceId2);
|
||||
|
||||
if (deviceId.equals(deviceId2)) {
|
||||
System.out.println("✓ 设备ID稳定,两次获取结果一致");
|
||||
} else {
|
||||
System.out.println("✗ 警告:设备ID不稳定,两次获取结果不同");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("✗ 获取设备ID失败:");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
System.out.println("\n=================================");
|
||||
System.out.println("测试完成");
|
||||
System.out.println("=================================");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class aa {
|
||||
@GetMapping("/a")
|
||||
@GetMapping("/aa")
|
||||
public String aa() {
|
||||
DeviceUtils deviceUtils = new DeviceUtils();
|
||||
return deviceUtils.generateDeviceId();
|
||||
|
||||
@@ -24,8 +24,6 @@ public class DeviceUtils {
|
||||
|
||||
public static String generateDeviceId() {
|
||||
String deviceId = null;
|
||||
log.info("========== 开始生成设备ID ==========");
|
||||
|
||||
// 策略1: Windows MachineGuid(注册表)
|
||||
deviceId = getMachineGuid();
|
||||
if (deviceId != null) return deviceId;
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
<!-- 固定日志路径到系统公共数据目录 -->
|
||||
<property name="LOG_HOME" value="C:/ProgramData/erp-logs" />
|
||||
|
||||
<!--
|
||||
日志上报说明:
|
||||
日志文件按天滚动存储在 ${LOG_HOME} 目录下
|
||||
格式:spring-boot-yyyy-MM-dd.log
|
||||
|
||||
用户反馈系统集成说明:
|
||||
- 客户端应用(Electron)可以读取本地日志文件
|
||||
- 用户在"设置-反馈"页面提交反馈时,可选择附带某一天的日志文件
|
||||
- 日志文件将随反馈内容一起上传到服务器
|
||||
- 服务器存储路径:C:/ProgramData/erp-logs/feedback/ (Windows) 或 /opt/erp/feedback-logs/ (Linux)
|
||||
- 管理员可在后台管理界面查看反馈并下载相应日志文件
|
||||
|
||||
日志保留策略:
|
||||
- 本地日志保留30天
|
||||
- 总大小限制1GB
|
||||
- 超过限制时自动删除最旧的日志文件
|
||||
-->
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
|
||||
Reference in New Issue
Block a user