新增打印机图像页面
This commit is contained in:
parent
6876282413
commit
110ee99978
58
CIRCULAR_DEPENDENCY_SOLUTION.md
Normal file
58
CIRCULAR_DEPENDENCY_SOLUTION.md
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# 循环依赖解决方案
|
||||||
|
|
||||||
|
## 当前问题
|
||||||
|
|
||||||
|
在应用程序上下文中存在循环依赖:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────┐
|
||||||
|
| printController (field private com.goeing.printserver.main.service.PrintQueueService com.goeing.printserver.main.PrintController.printQueueService)
|
||||||
|
↑ ↓
|
||||||
|
| printQueueService (field private com.goeing.printserver.main.PrintController com.goeing.printserver.main.service.PrintQueueService.printController)
|
||||||
|
└─────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 临时解决方案
|
||||||
|
|
||||||
|
已在 `application.properties` 中添加以下配置,临时允许循环依赖:
|
||||||
|
|
||||||
|
```properties
|
||||||
|
spring.main.allow-circular-references=true
|
||||||
|
```
|
||||||
|
|
||||||
|
## 已实施的重构步骤
|
||||||
|
|
||||||
|
1. 创建了 `PrintService` 接口,定义打印相关操作
|
||||||
|
2. 修改 `PrintController` 实现 `PrintService` 接口
|
||||||
|
3. 修改 `PrintQueueService` 依赖 `PrintService` 而不是直接依赖 `PrintController`
|
||||||
|
|
||||||
|
## 后续重构建议
|
||||||
|
|
||||||
|
为彻底解决循环依赖问题,建议进一步重构:
|
||||||
|
|
||||||
|
### 方案一:使用事件驱动架构
|
||||||
|
|
||||||
|
1. 使用 Spring 的事件机制(ApplicationEventPublisher)替代直接方法调用
|
||||||
|
2. `PrintQueueService` 发布打印事件
|
||||||
|
3. `PrintController` 订阅并处理这些事件
|
||||||
|
|
||||||
|
### 方案二:引入服务层抽象
|
||||||
|
|
||||||
|
1. 创建更完整的服务层抽象,明确职责分离
|
||||||
|
2. 将 `PrintController` 中的业务逻辑移至专门的服务类
|
||||||
|
3. 让 `PrintController` 和 `PrintQueueService` 都依赖这个新服务类
|
||||||
|
|
||||||
|
### 方案三:重新设计组件职责
|
||||||
|
|
||||||
|
1. 重新评估 `PrintController` 和 `PrintQueueService` 的职责
|
||||||
|
2. 可能的职责划分:
|
||||||
|
- `PrintController`:仅处理 HTTP 请求和响应
|
||||||
|
- `PrintQueueService`:管理打印队列和执行打印操作
|
||||||
|
- 新增 `PrintExecutionService`:实际执行打印操作的逻辑
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
- 遵循单一职责原则,每个类只负责一个功能领域
|
||||||
|
- 使用依赖注入,但避免双向依赖
|
||||||
|
- 考虑使用事件驱动架构处理组件间通信
|
||||||
|
- 使用接口进行解耦,降低组件间直接依赖
|
||||||
76
docs/MACOS_HEADLESS_SOLUTION.md
Normal file
76
docs/MACOS_HEADLESS_SOLUTION.md
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# macOS 系统上的 HeadlessException 解决方案
|
||||||
|
|
||||||
|
## 问题描述
|
||||||
|
|
||||||
|
在 macOS 系统上运行打印服务器时,可能会遇到 `java.awt.HeadlessException` 错误,错误信息类似:
|
||||||
|
|
||||||
|
```
|
||||||
|
Exception in thread "AWT-EventQueue-0" java.awt.HeadlessException
|
||||||
|
at java.desktop/java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:166)
|
||||||
|
at java.desktop/java.awt.Window.<init>(Window.java:553)
|
||||||
|
at java.desktop/java.awt.Frame.<init>(Frame.java:428)
|
||||||
|
at java.desktop/javax.swing.JFrame.<init>(JFrame.java:224)
|
||||||
|
at com.goeing.printserver.main.gui.PrintQueueGUI.initializeGUI(PrintQueueGUI.java:58)
|
||||||
|
```
|
||||||
|
|
||||||
|
这个错误表明应用程序在无头模式(Headless Mode)下运行,但尝试创建图形界面组件。在 macOS 系统上,特别是在某些环境下(如远程会话、无显示器连接或特定的系统配置),Java 应用程序可能会自动进入无头模式。
|
||||||
|
|
||||||
|
## 解决方案
|
||||||
|
|
||||||
|
我们已经对应用程序进行了优化,以更好地处理无头模式。现在有以下几种方式可以解决这个问题:
|
||||||
|
|
||||||
|
### 1. 使用命令行参数启用无头模式
|
||||||
|
|
||||||
|
如果您知道系统不支持图形界面,可以在启动应用程序时明确指定无头模式:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
java -Djava.awt.headless=true -jar goeingPrintServer.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 通过配置文件设置
|
||||||
|
|
||||||
|
在 `application.properties` 文件中,我们添加了一个配置项:
|
||||||
|
|
||||||
|
```properties
|
||||||
|
# 在macOS系统上,如果遇到HeadlessException,可以设置为true强制使用无头模式
|
||||||
|
app.force.headless=false
|
||||||
|
```
|
||||||
|
|
||||||
|
将此值设置为 `true` 可以强制应用程序以无头模式运行。
|
||||||
|
|
||||||
|
### 3. 自动检测和适应
|
||||||
|
|
||||||
|
应用程序现在会自动检测系统是否支持图形界面,并在不支持时自动切换到无头模式。在无头模式下:
|
||||||
|
|
||||||
|
- 图形界面组件不会被初始化
|
||||||
|
- 系统托盘图标不会显示
|
||||||
|
- 通知功能将被禁用
|
||||||
|
- 打印功能仍然正常工作
|
||||||
|
|
||||||
|
## 无头模式下的功能
|
||||||
|
|
||||||
|
在无头模式下,应用程序仍然可以通过以下方式使用:
|
||||||
|
|
||||||
|
1. **REST API**:所有打印功能都可以通过 REST API 访问
|
||||||
|
2. **WebSocket**:打印请求可以通过 WebSocket 连接发送
|
||||||
|
3. **命令行**:可以通过命令行工具与应用程序交互
|
||||||
|
|
||||||
|
## 日志输出
|
||||||
|
|
||||||
|
当应用程序检测到无头模式时,会在日志中输出相关信息:
|
||||||
|
|
||||||
|
```
|
||||||
|
当前环境不支持图形界面,将以无头模式运行
|
||||||
|
系统运行在无头模式下,通知功能将被禁用
|
||||||
|
```
|
||||||
|
|
||||||
|
## 技术说明
|
||||||
|
|
||||||
|
我们通过以下方式改进了应用程序对无头模式的处理:
|
||||||
|
|
||||||
|
1. 在应用启动时检测系统环境
|
||||||
|
2. 在检测到无头模式时设置系统属性 `app.headless.mode=true`
|
||||||
|
3. 所有图形界面组件在初始化前检查此属性
|
||||||
|
4. 添加了异常处理,防止图形界面初始化失败导致整个应用崩溃
|
||||||
|
|
||||||
|
这些改进确保了应用程序在各种环境下都能稳定运行,无论是否支持图形界面。
|
||||||
@ -2,12 +2,47 @@ package com.goeing.printserver;
|
|||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import java.awt.GraphicsEnvironment;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class GoeingPrintServerApplication {
|
public class GoeingPrintServerApplication {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(GoeingPrintServerApplication.class);
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(GoeingPrintServerApplication.class, args);
|
// 检查是否在macOS系统上运行
|
||||||
|
String osName = System.getProperty("os.name").toLowerCase();
|
||||||
|
boolean isMacOS = osName.contains("mac");
|
||||||
|
|
||||||
|
// 检查是否已经设置了java.awt.headless系统属性
|
||||||
|
String headlessProperty = System.getProperty("java.awt.headless");
|
||||||
|
|
||||||
|
// 如果是macOS并且没有明确设置headless属性,可能需要特殊处理
|
||||||
|
if (isMacOS && headlessProperty == null) {
|
||||||
|
log.info("在macOS系统上运行,检查是否需要启用无头模式");
|
||||||
|
|
||||||
|
// 检查是否支持图形界面
|
||||||
|
if (GraphicsEnvironment.isHeadless()) {
|
||||||
|
log.warn("检测到系统不支持图形界面,自动启用无头模式");
|
||||||
|
System.setProperty("java.awt.headless", "true");
|
||||||
|
System.setProperty("app.headless.mode", "true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigurableApplicationContext context = SpringApplication.run(GoeingPrintServerApplication.class, args);
|
||||||
|
|
||||||
|
// 从配置中读取是否强制使用无头模式
|
||||||
|
Environment env = context.getEnvironment();
|
||||||
|
boolean forceHeadless = Boolean.parseBoolean(env.getProperty("app.force.headless", "false"));
|
||||||
|
|
||||||
|
if (forceHeadless) {
|
||||||
|
log.info("根据配置强制启用无头模式");
|
||||||
|
System.setProperty("java.awt.headless", "true");
|
||||||
|
System.setProperty("app.headless.mode", "true");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,11 +3,15 @@ package com.goeing.printserver.main;
|
|||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import cn.hutool.http.HttpUtil;
|
import cn.hutool.http.HttpUtil;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.goeing.printserver.main.config.PrintServerConfig;
|
||||||
import com.goeing.printserver.main.domain.request.PrintRequest;
|
import com.goeing.printserver.main.domain.request.PrintRequest;
|
||||||
|
import com.goeing.printserver.main.service.PrintQueueService;
|
||||||
|
import com.goeing.printserver.main.service.PrintService;
|
||||||
import com.goeing.printserver.main.utils.PdfPrinter;
|
import com.goeing.printserver.main.utils.PdfPrinter;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.print.PrintService;
|
// 使用完全限定名称避免与自定义PrintService接口冲突
|
||||||
import java.awt.print.PrinterJob;
|
import java.awt.print.PrinterJob;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -19,7 +23,14 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api")
|
@RequestMapping("/api")
|
||||||
public class PrintController {
|
public class PrintController implements PrintService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PrintQueueService printQueueService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PrintServerConfig config;
|
||||||
|
|
||||||
private final String rootPath = "/Users/fl0919/work_space/goeingPrintServer/pdf";
|
private final String rootPath = "/Users/fl0919/work_space/goeingPrintServer/pdf";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,8 +40,8 @@ public class PrintController {
|
|||||||
*/
|
*/
|
||||||
@GetMapping("printerList")
|
@GetMapping("printerList")
|
||||||
public List<String> printerList() {
|
public List<String> printerList() {
|
||||||
PrintService[] printServices = PrinterJob.lookupPrintServices();
|
javax.print.PrintService[] printServices = PrinterJob.lookupPrintServices();
|
||||||
Set<String> collect = Arrays.stream(printServices).map(PrintService::getName).collect(Collectors.toSet());
|
Set<String> collect = Arrays.stream(printServices).map(service -> service.getName()).collect(Collectors.toSet());
|
||||||
return collect.stream().sorted().collect(Collectors.toList());
|
return collect.stream().sorted().collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,6 +62,34 @@ public class PrintController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前打印队列状态
|
||||||
|
*
|
||||||
|
* @return 包含队列信息的Map
|
||||||
|
*/
|
||||||
|
@GetMapping("queue/status")
|
||||||
|
public Map<String, Object> getQueueStatus() {
|
||||||
|
Map<String, Object> status = new HashMap<>();
|
||||||
|
status.put("queueSize", printQueueService.getQueueSize());
|
||||||
|
status.put("timestamp", System.currentTimeMillis());
|
||||||
|
status.put("currentTask", printQueueService.getCurrentTaskInfo());
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取打印队列中的所有任务
|
||||||
|
*
|
||||||
|
* @return 任务列表
|
||||||
|
*/
|
||||||
|
@GetMapping("queue/tasks")
|
||||||
|
public Map<String, Object> getQueueTasks() {
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("currentTask", printQueueService.getCurrentTaskInfo());
|
||||||
|
result.put("queuedTasks", printQueueService.getQueuedTasksInfo());
|
||||||
|
result.put("timestamp", System.currentTimeMillis());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("print")
|
@PostMapping("print")
|
||||||
public String print(@RequestBody PrintRequest request) {
|
public String print(@RequestBody PrintRequest request) {
|
||||||
// 记录请求信息
|
// 记录请求信息
|
||||||
@ -65,8 +104,14 @@ public class PrintController {
|
|||||||
throw new IllegalArgumentException("File URL cannot be null or empty");
|
throw new IllegalArgumentException("File URL cannot be null or empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果打印机名称为空,使用默认打印机
|
||||||
if (request.getPrinterName() == null || request.getPrinterName().trim().isEmpty()) {
|
if (request.getPrinterName() == null || request.getPrinterName().trim().isEmpty()) {
|
||||||
throw new IllegalArgumentException("Printer name cannot be null or empty");
|
String defaultPrinter = config.getDefaultPrinter();
|
||||||
|
if (defaultPrinter == null || defaultPrinter.trim().isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Printer name cannot be null or empty, and no default printer is configured");
|
||||||
|
}
|
||||||
|
request.setPrinterName(defaultPrinter);
|
||||||
|
System.out.println("Using default printer: " + defaultPrinter);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证打印机是否存在
|
// 验证打印机是否存在
|
||||||
|
|||||||
@ -0,0 +1,122 @@
|
|||||||
|
package com.goeing.printserver.main.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印服务器配置类
|
||||||
|
* 负责管理打印服务器的所有配置信息,包括默认打印机、最大队列大小、通知设置等
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
@Data
|
||||||
|
public class PrintServerConfig {
|
||||||
|
|
||||||
|
private static final String CONFIG_FILE = "printserver.properties";
|
||||||
|
|
||||||
|
// 默认配置值
|
||||||
|
private static final String DEFAULT_PRINTER = "默认打印机";
|
||||||
|
private static final int DEFAULT_MAX_QUEUE_SIZE = 10;
|
||||||
|
private static final boolean DEFAULT_ENABLE_NOTIFICATIONS = true;
|
||||||
|
private static final boolean DEFAULT_START_MINIMIZED = false;
|
||||||
|
private static final boolean DEFAULT_AUTO_START = false;
|
||||||
|
|
||||||
|
// 配置属性
|
||||||
|
private String defaultPrinter = DEFAULT_PRINTER;
|
||||||
|
private int maxQueueSize = DEFAULT_MAX_QUEUE_SIZE;
|
||||||
|
private boolean enableNotifications = DEFAULT_ENABLE_NOTIFICATIONS;
|
||||||
|
private boolean startMinimized = DEFAULT_START_MINIMIZED;
|
||||||
|
private boolean autoStart = DEFAULT_AUTO_START;
|
||||||
|
|
||||||
|
private Properties properties = new Properties();
|
||||||
|
private File configFile;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
// 确定配置文件路径
|
||||||
|
String userHome = System.getProperty("user.home");
|
||||||
|
configFile = new File(userHome + File.separator + ".goeing" + File.separator + CONFIG_FILE);
|
||||||
|
|
||||||
|
// 确保目录存在
|
||||||
|
if (!configFile.getParentFile().exists()) {
|
||||||
|
configFile.getParentFile().mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载配置
|
||||||
|
loadConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载配置
|
||||||
|
*/
|
||||||
|
public void loadConfig() {
|
||||||
|
if (configFile.exists()) {
|
||||||
|
try (FileInputStream fis = new FileInputStream(configFile)) {
|
||||||
|
properties.load(fis);
|
||||||
|
|
||||||
|
// 读取配置值
|
||||||
|
defaultPrinter = properties.getProperty("defaultPrinter", DEFAULT_PRINTER);
|
||||||
|
maxQueueSize = Integer.parseInt(properties.getProperty("maxQueueSize", String.valueOf(DEFAULT_MAX_QUEUE_SIZE)));
|
||||||
|
enableNotifications = Boolean.parseBoolean(properties.getProperty("enableNotifications", String.valueOf(DEFAULT_ENABLE_NOTIFICATIONS)));
|
||||||
|
startMinimized = Boolean.parseBoolean(properties.getProperty("startMinimized", String.valueOf(DEFAULT_START_MINIMIZED)));
|
||||||
|
autoStart = Boolean.parseBoolean(properties.getProperty("autoStart", String.valueOf(DEFAULT_AUTO_START)));
|
||||||
|
|
||||||
|
log.info("配置已加载: {}", configFile.getAbsolutePath());
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("加载配置文件失败", e);
|
||||||
|
// 使用默认值
|
||||||
|
resetToDefaults();
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.error("解析配置值失败", e);
|
||||||
|
// 使用默认值
|
||||||
|
resetToDefaults();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.info("配置文件不存在,使用默认配置");
|
||||||
|
// 使用默认值并保存
|
||||||
|
resetToDefaults();
|
||||||
|
saveConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存配置
|
||||||
|
*/
|
||||||
|
public void saveConfig() {
|
||||||
|
try {
|
||||||
|
// 更新属性
|
||||||
|
properties.setProperty("defaultPrinter", defaultPrinter);
|
||||||
|
properties.setProperty("maxQueueSize", String.valueOf(maxQueueSize));
|
||||||
|
properties.setProperty("enableNotifications", String.valueOf(enableNotifications));
|
||||||
|
properties.setProperty("startMinimized", String.valueOf(startMinimized));
|
||||||
|
properties.setProperty("autoStart", String.valueOf(autoStart));
|
||||||
|
|
||||||
|
// 保存到文件
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(configFile)) {
|
||||||
|
properties.store(fos, "Goeing Print Server Configuration");
|
||||||
|
log.info("配置已保存: {}", configFile.getAbsolutePath());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("保存配置文件失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置为默认配置
|
||||||
|
*/
|
||||||
|
public void resetToDefaults() {
|
||||||
|
defaultPrinter = DEFAULT_PRINTER;
|
||||||
|
maxQueueSize = DEFAULT_MAX_QUEUE_SIZE;
|
||||||
|
enableNotifications = DEFAULT_ENABLE_NOTIFICATIONS;
|
||||||
|
startMinimized = DEFAULT_START_MINIMIZED;
|
||||||
|
autoStart = DEFAULT_AUTO_START;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
package com.goeing.printserver.main.domain;
|
||||||
|
|
||||||
|
import com.goeing.printserver.main.domain.bo.PrintOption;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印任务实体类,用于表示一个打印任务
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class PrintTask {
|
||||||
|
// 文件URL
|
||||||
|
private String fileUrl;
|
||||||
|
|
||||||
|
// 打印机名称
|
||||||
|
private String printer;
|
||||||
|
|
||||||
|
// 任务状态:queued, processing, completed, failed
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
// 排队时间
|
||||||
|
private LocalDateTime queuedTime;
|
||||||
|
|
||||||
|
// 开始处理时间
|
||||||
|
private LocalDateTime startTime;
|
||||||
|
|
||||||
|
// 结束时间
|
||||||
|
private LocalDateTime endTime;
|
||||||
|
|
||||||
|
// 打印选项
|
||||||
|
private PrintOption printOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将任务转换为Map
|
||||||
|
*
|
||||||
|
* @return 包含任务信息的Map
|
||||||
|
*/
|
||||||
|
public Map<String, Object> toMap() {
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("fileUrl", fileUrl);
|
||||||
|
map.put("printerName", printer);
|
||||||
|
map.put("status", status);
|
||||||
|
map.put("queuedTime", queuedTime);
|
||||||
|
if (startTime != null) {
|
||||||
|
map.put("startTime", startTime);
|
||||||
|
}
|
||||||
|
if (endTime != null) {
|
||||||
|
map.put("endTime", endTime);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
137
src/main/java/com/goeing/printserver/main/gui/AboutDialog.java
Normal file
137
src/main/java/com/goeing/printserver/main/gui/AboutDialog.java
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package com.goeing.printserver.main.gui;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关于对话框,显示应用程序信息
|
||||||
|
*/
|
||||||
|
public class AboutDialog extends JDialog {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建关于对话框
|
||||||
|
*
|
||||||
|
* @param parent 父窗口
|
||||||
|
*/
|
||||||
|
public AboutDialog(Window parent) {
|
||||||
|
super(parent, "关于打印服务器", ModalityType.APPLICATION_MODAL);
|
||||||
|
initializeUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化用户界面
|
||||||
|
*/
|
||||||
|
private void initializeUI() {
|
||||||
|
setSize(400, 300);
|
||||||
|
setLocationRelativeTo(getOwner());
|
||||||
|
setResizable(false);
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
// 创建图标面板
|
||||||
|
JPanel iconPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
|
||||||
|
iconPanel.setBorder(BorderFactory.createEmptyBorder(20, 0, 10, 0));
|
||||||
|
|
||||||
|
// 创建应用图标
|
||||||
|
ImageIcon appIcon = createAppIcon();
|
||||||
|
JLabel iconLabel = new JLabel(appIcon);
|
||||||
|
iconPanel.add(iconLabel);
|
||||||
|
|
||||||
|
add(iconPanel, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
// 创建信息面板
|
||||||
|
JPanel infoPanel = new JPanel();
|
||||||
|
infoPanel.setLayout(new BoxLayout(infoPanel, BoxLayout.Y_AXIS));
|
||||||
|
infoPanel.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20));
|
||||||
|
|
||||||
|
// 添加应用信息
|
||||||
|
addInfoLabel(infoPanel, "打印服务器", Font.BOLD, 16);
|
||||||
|
addInfoLabel(infoPanel, "版本: 1.0.0", Font.PLAIN, 12);
|
||||||
|
addInfoLabel(infoPanel, "构建日期: " + java.time.LocalDate.now().toString(), Font.PLAIN, 12);
|
||||||
|
addInfoLabel(infoPanel, "JDK版本: " + System.getProperty("java.version"), Font.PLAIN, 12);
|
||||||
|
addInfoLabel(infoPanel, "操作系统: " + System.getProperty("os.name") + " " + System.getProperty("os.version"), Font.PLAIN, 12);
|
||||||
|
|
||||||
|
// 添加空白间隔
|
||||||
|
infoPanel.add(Box.createVerticalStrut(10));
|
||||||
|
|
||||||
|
// 添加版权信息
|
||||||
|
addInfoLabel(infoPanel, "© " + java.time.Year.now().getValue() + " Goeing. 保留所有权利。", Font.ITALIC, 12);
|
||||||
|
|
||||||
|
add(infoPanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
// 创建按钮面板
|
||||||
|
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
|
||||||
|
JButton closeButton = new JButton("关闭");
|
||||||
|
closeButton.addActionListener(e -> dispose());
|
||||||
|
buttonPanel.add(closeButton);
|
||||||
|
|
||||||
|
add(buttonPanel, BorderLayout.SOUTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加信息标签
|
||||||
|
*
|
||||||
|
* @param panel 面板
|
||||||
|
* @param text 文本
|
||||||
|
* @param fontStyle 字体样式
|
||||||
|
* @param fontSize 字体大小
|
||||||
|
*/
|
||||||
|
private void addInfoLabel(JPanel panel, String text, int fontStyle, int fontSize) {
|
||||||
|
JLabel label = new JLabel(text);
|
||||||
|
label.setFont(new Font(label.getFont().getName(), fontStyle, fontSize));
|
||||||
|
label.setAlignmentX(0.5f); // CENTER_ALIGNMENT = 0.5f
|
||||||
|
panel.add(label);
|
||||||
|
panel.add(Box.createVerticalStrut(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建应用图标
|
||||||
|
*
|
||||||
|
* @return 图标
|
||||||
|
*/
|
||||||
|
private ImageIcon createAppIcon() {
|
||||||
|
// 创建一个简单的打印机图标
|
||||||
|
int iconSize = 64;
|
||||||
|
BufferedImage image = new BufferedImage(iconSize, iconSize, BufferedImage.TYPE_INT_ARGB);
|
||||||
|
Graphics2D g2d = image.createGraphics();
|
||||||
|
|
||||||
|
// 启用抗锯齿
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
|
||||||
|
// 绘制打印机主体
|
||||||
|
g2d.setColor(new Color(50, 50, 50));
|
||||||
|
g2d.fillRect(10, 25, 44, 25);
|
||||||
|
|
||||||
|
// 绘制打印机纸盒
|
||||||
|
g2d.setColor(new Color(200, 200, 200));
|
||||||
|
g2d.fillRect(15, 50, 34, 5);
|
||||||
|
|
||||||
|
// 绘制打印机出纸口
|
||||||
|
g2d.setColor(new Color(240, 240, 240));
|
||||||
|
g2d.fillRect(15, 15, 34, 10);
|
||||||
|
|
||||||
|
// 绘制打印纸
|
||||||
|
g2d.setColor(Color.WHITE);
|
||||||
|
g2d.fillRect(20, 5, 24, 20);
|
||||||
|
|
||||||
|
// 绘制打印机按钮
|
||||||
|
g2d.setColor(new Color(0, 150, 200));
|
||||||
|
g2d.fillOval(45, 30, 5, 5);
|
||||||
|
|
||||||
|
g2d.dispose();
|
||||||
|
|
||||||
|
return new ImageIcon(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示关于对话框
|
||||||
|
*
|
||||||
|
* @param parent 父窗口
|
||||||
|
*/
|
||||||
|
public static void showDialog(Window parent) {
|
||||||
|
AboutDialog dialog = new AboutDialog(parent);
|
||||||
|
dialog.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
package com.goeing.printserver.main.gui;
|
||||||
|
|
||||||
|
import jakarta.annotation.PreDestroy;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GUI启动器,负责在Spring Boot应用程序启动后初始化图形界面
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class GUILauncher {
|
||||||
|
|
||||||
|
private final PrintQueueGUI printQueueGUI;
|
||||||
|
private final PrintServerTray printServerTray;
|
||||||
|
private final PrintSettingsPanel settingsPanel;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public GUILauncher(PrintQueueGUI printQueueGUI, PrintServerTray printServerTray, PrintSettingsPanel settingsPanel) {
|
||||||
|
this.printQueueGUI = printQueueGUI;
|
||||||
|
this.printServerTray = printServerTray;
|
||||||
|
this.settingsPanel = settingsPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在应用程序准备就绪后启动GUI
|
||||||
|
*/
|
||||||
|
@EventListener(ApplicationReadyEvent.class)
|
||||||
|
public void launchGUI() {
|
||||||
|
log.info("应用程序已准备就绪,正在启动图形界面...");
|
||||||
|
|
||||||
|
// 检查是否支持图形界面
|
||||||
|
boolean isHeadless = GraphicsEnvironment.isHeadless();
|
||||||
|
if (isHeadless) {
|
||||||
|
log.warn("当前环境不支持图形界面,将以无头模式运行");
|
||||||
|
// 在无头模式下,设置一个系统属性,其他组件可以检查这个属性
|
||||||
|
System.setProperty("app.headless.mode", "true");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 初始化GUI和系统托盘
|
||||||
|
printServerTray.initialize();
|
||||||
|
|
||||||
|
// 根据设置决定是否显示主窗口
|
||||||
|
if (!settingsPanel.isStartMinimized()) {
|
||||||
|
printQueueGUI.show();
|
||||||
|
} else {
|
||||||
|
log.info("根据用户设置,应用程序启动时最小化到系统托盘");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示欢迎通知
|
||||||
|
printServerTray.displayMessage(
|
||||||
|
"打印服务器已启动",
|
||||||
|
"打印服务器正在后台运行,点击托盘图标可打开主窗口",
|
||||||
|
TrayIcon.MessageType.INFO
|
||||||
|
);
|
||||||
|
|
||||||
|
// 更新托盘提示
|
||||||
|
printServerTray.updateTooltip("打印服务器 - 运行中");
|
||||||
|
|
||||||
|
log.info("图形界面已启动");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("初始化图形界面时发生错误: {}", e.getMessage());
|
||||||
|
log.warn("将以无头模式运行");
|
||||||
|
System.setProperty("app.headless.mode", "true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在应用程序关闭时释放资源
|
||||||
|
*/
|
||||||
|
@PreDestroy
|
||||||
|
public void shutdown() {
|
||||||
|
log.info("正在关闭图形界面...");
|
||||||
|
printQueueGUI.shutdown();
|
||||||
|
printServerTray.shutdown();
|
||||||
|
log.info("图形界面已关闭");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,163 @@
|
|||||||
|
package com.goeing.printserver.main.gui;
|
||||||
|
|
||||||
|
import com.goeing.printserver.main.domain.request.PrintRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印通知服务,用于在打印任务状态变化时发送系统通知
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class PrintNotificationService {
|
||||||
|
|
||||||
|
private final PrintServerTray printServerTray;
|
||||||
|
private final PrintSettingsPanel settingsPanel;
|
||||||
|
private boolean headless = false;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public PrintNotificationService(PrintServerTray printServerTray, PrintSettingsPanel settingsPanel) {
|
||||||
|
this.printServerTray = printServerTray;
|
||||||
|
this.settingsPanel = settingsPanel;
|
||||||
|
// 检查是否在无头模式下运行
|
||||||
|
this.headless = GraphicsEnvironment.isHeadless() || Boolean.getBoolean("app.headless.mode");
|
||||||
|
if (headless) {
|
||||||
|
log.info("系统运行在无头模式下,通知功能将被禁用");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知打印任务已添加到队列
|
||||||
|
*
|
||||||
|
* @param printRequest 打印请求
|
||||||
|
* @param queueSize 当前队列大小
|
||||||
|
*/
|
||||||
|
public void notifyTaskQueued(PrintRequest printRequest, int queueSize) {
|
||||||
|
if (headless || !settingsPanel.isEnableNotifications()) {
|
||||||
|
return; // 无头模式或通知被禁用时不发送通知
|
||||||
|
}
|
||||||
|
|
||||||
|
String fileName = extractFileName(printRequest.getFileUrl());
|
||||||
|
String message = String.format(
|
||||||
|
"文件 '%s' 已添加到打印队列\n打印机: %s\n队列位置: %d",
|
||||||
|
fileName,
|
||||||
|
printRequest.getPrinterName(),
|
||||||
|
queueSize
|
||||||
|
);
|
||||||
|
|
||||||
|
printServerTray.displayMessage("新打印任务", message, TrayIcon.MessageType.INFO);
|
||||||
|
updateTrayTooltip(queueSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知打印任务开始处理
|
||||||
|
*
|
||||||
|
* @param printRequest 打印请求
|
||||||
|
* @param queueSize 当前队列大小
|
||||||
|
*/
|
||||||
|
public void notifyTaskStarted(PrintRequest printRequest, int queueSize) {
|
||||||
|
if (headless || !settingsPanel.isEnableNotifications()) {
|
||||||
|
return; // 无头模式或通知被禁用时不发送通知
|
||||||
|
}
|
||||||
|
|
||||||
|
String fileName = extractFileName(printRequest.getFileUrl());
|
||||||
|
String message = String.format(
|
||||||
|
"开始打印文件 '%s'\n打印机: %s",
|
||||||
|
fileName,
|
||||||
|
printRequest.getPrinterName()
|
||||||
|
);
|
||||||
|
|
||||||
|
printServerTray.displayMessage("打印开始", message, TrayIcon.MessageType.INFO);
|
||||||
|
updateTrayTooltip(queueSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知打印任务完成
|
||||||
|
*
|
||||||
|
* @param printRequest 打印请求
|
||||||
|
* @param queueSize 当前队列大小
|
||||||
|
*/
|
||||||
|
public void notifyTaskCompleted(PrintRequest printRequest, int queueSize) {
|
||||||
|
if (headless || !settingsPanel.isEnableNotifications()) {
|
||||||
|
return; // 无头模式或通知被禁用时不发送通知
|
||||||
|
}
|
||||||
|
|
||||||
|
String fileName = extractFileName(printRequest.getFileUrl());
|
||||||
|
String message = String.format(
|
||||||
|
"文件 '%s' 打印完成\n打印机: %s",
|
||||||
|
fileName,
|
||||||
|
printRequest.getPrinterName()
|
||||||
|
);
|
||||||
|
|
||||||
|
printServerTray.displayMessage("打印完成", message, TrayIcon.MessageType.INFO);
|
||||||
|
updateTrayTooltip(queueSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知打印任务失败
|
||||||
|
*
|
||||||
|
* @param printRequest 打印请求
|
||||||
|
* @param errorMessage 错误消息
|
||||||
|
* @param queueSize 当前队列大小
|
||||||
|
*/
|
||||||
|
public void notifyTaskFailed(PrintRequest printRequest, String errorMessage, int queueSize) {
|
||||||
|
if (headless || !settingsPanel.isEnableNotifications()) {
|
||||||
|
return; // 无头模式或通知被禁用时不发送通知
|
||||||
|
}
|
||||||
|
|
||||||
|
String fileName = extractFileName(printRequest.getFileUrl());
|
||||||
|
String message = String.format(
|
||||||
|
"文件 '%s' 打印失败\n打印机: %s\n错误: %s",
|
||||||
|
fileName,
|
||||||
|
printRequest.getPrinterName(),
|
||||||
|
errorMessage
|
||||||
|
);
|
||||||
|
|
||||||
|
printServerTray.displayMessage("打印失败", message, TrayIcon.MessageType.ERROR);
|
||||||
|
updateTrayTooltip(queueSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从URL中提取文件名
|
||||||
|
*
|
||||||
|
* @param fileUrl 文件URL
|
||||||
|
* @return 文件名
|
||||||
|
*/
|
||||||
|
private String extractFileName(String fileUrl) {
|
||||||
|
if (fileUrl == null || fileUrl.isEmpty()) {
|
||||||
|
return "未知文件";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试从URL中提取文件名
|
||||||
|
int lastSlashIndex = fileUrl.lastIndexOf('/');
|
||||||
|
if (lastSlashIndex >= 0 && lastSlashIndex < fileUrl.length() - 1) {
|
||||||
|
String fileName = fileUrl.substring(lastSlashIndex + 1);
|
||||||
|
// 移除查询参数
|
||||||
|
int queryIndex = fileName.indexOf('?');
|
||||||
|
if (queryIndex > 0) {
|
||||||
|
fileName = fileName.substring(0, queryIndex);
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileUrl; // 如果无法提取,则返回完整URL
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新托盘提示
|
||||||
|
*
|
||||||
|
* @param queueSize 当前队列大小
|
||||||
|
*/
|
||||||
|
private void updateTrayTooltip(int queueSize) {
|
||||||
|
String tooltip;
|
||||||
|
if (queueSize > 0) {
|
||||||
|
tooltip = String.format("打印服务器 - 运行中 (队列中有 %d 个任务)", queueSize);
|
||||||
|
} else {
|
||||||
|
tooltip = "打印服务器 - 运行中 (队列为空)";
|
||||||
|
}
|
||||||
|
printServerTray.updateTooltip(tooltip);
|
||||||
|
}
|
||||||
|
}
|
||||||
388
src/main/java/com/goeing/printserver/main/gui/PrintQueueGUI.java
Normal file
388
src/main/java/com/goeing/printserver/main/gui/PrintQueueGUI.java
Normal file
@ -0,0 +1,388 @@
|
|||||||
|
package com.goeing.printserver.main.gui;
|
||||||
|
|
||||||
|
import com.goeing.printserver.main.service.PrintQueueService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import jakarta.annotation.PreDestroy;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.table.DefaultTableModel;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.WindowAdapter;
|
||||||
|
import java.awt.event.WindowEvent;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印队列图形界面
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class PrintQueueGUI {
|
||||||
|
|
||||||
|
private final PrintQueueService printQueueService;
|
||||||
|
private final PrinterStatusPanel printerStatusPanel;
|
||||||
|
private final PrintStatisticsPanel statisticsPanel;
|
||||||
|
private final PrintTaskSearchPanel searchPanel;
|
||||||
|
private final PrintSettingsPanel settingsPanel;
|
||||||
|
private JFrame frame;
|
||||||
|
private JTable currentTaskTable;
|
||||||
|
private JTable queuedTasksTable;
|
||||||
|
private DefaultTableModel currentTaskModel;
|
||||||
|
private DefaultTableModel queuedTasksModel;
|
||||||
|
private JLabel statusLabel;
|
||||||
|
private final ScheduledExecutorService refreshExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public PrintQueueGUI(PrintQueueService printQueueService, PrinterStatusPanel printerStatusPanel,
|
||||||
|
PrintStatisticsPanel statisticsPanel, PrintTaskSearchPanel searchPanel,
|
||||||
|
PrintSettingsPanel settingsPanel) {
|
||||||
|
this.printQueueService = printQueueService;
|
||||||
|
this.printerStatusPanel = printerStatusPanel;
|
||||||
|
this.statisticsPanel = statisticsPanel;
|
||||||
|
this.searchPanel = searchPanel;
|
||||||
|
this.settingsPanel = settingsPanel;
|
||||||
|
|
||||||
|
// 只有在非无头模式下才初始化GUI
|
||||||
|
if (!GraphicsEnvironment.isHeadless() && !Boolean.getBoolean("app.headless.mode")) {
|
||||||
|
SwingUtilities.invokeLater(this::initializeGUI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化图形界面
|
||||||
|
*/
|
||||||
|
private void initializeGUI() {
|
||||||
|
// 创建主窗口
|
||||||
|
frame = new JFrame("打印队列监控");
|
||||||
|
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||||
|
frame.setSize(800, 600);
|
||||||
|
frame.setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
// 添加窗口关闭监听器
|
||||||
|
frame.addWindowListener(new WindowAdapter() {
|
||||||
|
@Override
|
||||||
|
public void windowClosing(WindowEvent e) {
|
||||||
|
// 只隐藏窗口,不关闭应用程序
|
||||||
|
frame.setVisible(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建菜单栏
|
||||||
|
JMenuBar menuBar = createMenuBar();
|
||||||
|
frame.setJMenuBar(menuBar);
|
||||||
|
|
||||||
|
// 创建顶部面板
|
||||||
|
JPanel topPanel = new JPanel(new BorderLayout());
|
||||||
|
statusLabel = new JLabel("队列状态: 空闲");
|
||||||
|
statusLabel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
|
topPanel.add(statusLabel, BorderLayout.WEST);
|
||||||
|
|
||||||
|
// 创建刷新按钮
|
||||||
|
JButton refreshButton = new JButton("刷新");
|
||||||
|
refreshButton.addActionListener(e -> refreshData());
|
||||||
|
topPanel.add(refreshButton, BorderLayout.EAST);
|
||||||
|
|
||||||
|
frame.add(topPanel, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
// 创建选项卡面板
|
||||||
|
JTabbedPane tabbedPane = new JTabbedPane();
|
||||||
|
|
||||||
|
// 创建当前任务面板
|
||||||
|
JPanel currentTaskPanel = createCurrentTaskPanel();
|
||||||
|
tabbedPane.addTab("当前任务", currentTaskPanel);
|
||||||
|
|
||||||
|
// 创建队列任务面板
|
||||||
|
JPanel queuedTasksPanel = createQueuedTasksPanel();
|
||||||
|
tabbedPane.addTab("队列任务", queuedTasksPanel);
|
||||||
|
|
||||||
|
// 添加打印机状态面板
|
||||||
|
tabbedPane.addTab("打印机状态", printerStatusPanel);
|
||||||
|
|
||||||
|
// 添加统计面板
|
||||||
|
tabbedPane.addTab("统计信息", statisticsPanel);
|
||||||
|
|
||||||
|
// 添加任务搜索面板
|
||||||
|
tabbedPane.addTab("任务搜索", searchPanel);
|
||||||
|
|
||||||
|
// 添加设置面板
|
||||||
|
tabbedPane.addTab("设置", settingsPanel);
|
||||||
|
|
||||||
|
frame.add(tabbedPane, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
// 启动定时刷新
|
||||||
|
startRefreshTimer();
|
||||||
|
|
||||||
|
// 显示窗口
|
||||||
|
frame.setLocationRelativeTo(null);
|
||||||
|
frame.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建当前任务面板
|
||||||
|
*/
|
||||||
|
private JPanel createCurrentTaskPanel() {
|
||||||
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
|
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
|
|
||||||
|
// 创建表格模型
|
||||||
|
String[] columnNames = {"文件URL", "打印机", "状态", "队列时间", "开始时间", "结束时间"};
|
||||||
|
currentTaskModel = new DefaultTableModel(columnNames, 0) {
|
||||||
|
@Override
|
||||||
|
public boolean isCellEditable(int row, int column) {
|
||||||
|
return false; // 禁止编辑单元格
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 创建表格
|
||||||
|
currentTaskTable = new JTable(currentTaskModel);
|
||||||
|
currentTaskTable.getTableHeader().setReorderingAllowed(false);
|
||||||
|
currentTaskTable.setFillsViewportHeight(true);
|
||||||
|
|
||||||
|
// 添加双击事件监听器
|
||||||
|
currentTaskTable.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(java.awt.event.MouseEvent e) {
|
||||||
|
if (e.getClickCount() == 2) {
|
||||||
|
showTaskDetails(currentTaskTable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加滚动面板
|
||||||
|
JScrollPane scrollPane = new JScrollPane(currentTaskTable);
|
||||||
|
panel.add(scrollPane, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建队列任务面板
|
||||||
|
*/
|
||||||
|
private JPanel createQueuedTasksPanel() {
|
||||||
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
|
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
|
|
||||||
|
// 创建表格模型
|
||||||
|
String[] columnNames = {"文件URL", "打印机", "状态", "队列时间"};
|
||||||
|
queuedTasksModel = new DefaultTableModel(columnNames, 0) {
|
||||||
|
@Override
|
||||||
|
public boolean isCellEditable(int row, int column) {
|
||||||
|
return false; // 禁止编辑单元格
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 创建表格
|
||||||
|
queuedTasksTable = new JTable(queuedTasksModel);
|
||||||
|
queuedTasksTable.getTableHeader().setReorderingAllowed(false);
|
||||||
|
queuedTasksTable.setFillsViewportHeight(true);
|
||||||
|
|
||||||
|
// 添加双击事件监听器
|
||||||
|
queuedTasksTable.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(java.awt.event.MouseEvent e) {
|
||||||
|
if (e.getClickCount() == 2) {
|
||||||
|
showTaskDetails(queuedTasksTable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加滚动面板
|
||||||
|
JScrollPane scrollPane = new JScrollPane(queuedTasksTable);
|
||||||
|
panel.add(scrollPane, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动定时刷新
|
||||||
|
*/
|
||||||
|
private void startRefreshTimer() {
|
||||||
|
refreshExecutor.scheduleAtFixedRate(this::refreshData, 0, 2, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新数据
|
||||||
|
*/
|
||||||
|
private void refreshData() {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
try {
|
||||||
|
// 更新队列状态
|
||||||
|
int queueSize = printQueueService.getQueueSize();
|
||||||
|
Map<String, Object> currentTask = printQueueService.getCurrentTaskInfo();
|
||||||
|
|
||||||
|
if (currentTask != null) {
|
||||||
|
statusLabel.setText("队列状态: 正在处理任务 (队列中还有 " + queueSize + " 个任务)");
|
||||||
|
} else if (queueSize > 0) {
|
||||||
|
statusLabel.setText("队列状态: 等待处理 (队列中有 " + queueSize + " 个任务)");
|
||||||
|
} else {
|
||||||
|
statusLabel.setText("队列状态: 空闲");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新当前任务表格
|
||||||
|
updateCurrentTaskTable(currentTask);
|
||||||
|
|
||||||
|
// 更新队列任务表格
|
||||||
|
List<Map<String, Object>> queuedTasks = printQueueService.getQueuedTasksInfo();
|
||||||
|
updateQueuedTasksTable(queuedTasks);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
statusLabel.setText("刷新数据时发生错误: " + e.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新当前任务表格
|
||||||
|
*/
|
||||||
|
private void updateCurrentTaskTable(Map<String, Object> currentTask) {
|
||||||
|
// 清空表格
|
||||||
|
currentTaskModel.setRowCount(0);
|
||||||
|
|
||||||
|
if (currentTask != null) {
|
||||||
|
Object[] rowData = new Object[6];
|
||||||
|
rowData[0] = currentTask.get("fileUrl");
|
||||||
|
rowData[1] = currentTask.get("printerName");
|
||||||
|
rowData[2] = currentTask.get("status");
|
||||||
|
rowData[3] = formatDateTime(currentTask.get("queuedTime"));
|
||||||
|
rowData[4] = formatDateTime(currentTask.get("startTime"));
|
||||||
|
rowData[5] = formatDateTime(currentTask.get("endTime"));
|
||||||
|
currentTaskModel.addRow(rowData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新队列任务表格
|
||||||
|
*/
|
||||||
|
private void updateQueuedTasksTable(List<Map<String, Object>> queuedTasks) {
|
||||||
|
// 清空表格
|
||||||
|
queuedTasksModel.setRowCount(0);
|
||||||
|
|
||||||
|
if (queuedTasks != null && !queuedTasks.isEmpty()) {
|
||||||
|
for (Map<String, Object> task : queuedTasks) {
|
||||||
|
Object[] rowData = new Object[4];
|
||||||
|
rowData[0] = task.get("fileUrl");
|
||||||
|
rowData[1] = task.get("printerName");
|
||||||
|
rowData[2] = task.get("status");
|
||||||
|
rowData[3] = formatDateTime(task.get("queuedTime"));
|
||||||
|
queuedTasksModel.addRow(rowData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化日期时间
|
||||||
|
*/
|
||||||
|
private String formatDateTime(Object dateTimeObj) {
|
||||||
|
if (dateTimeObj == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dateTimeObj instanceof LocalDateTime) {
|
||||||
|
return ((LocalDateTime) dateTimeObj).format(dateFormatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dateTimeObj.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示窗口
|
||||||
|
*/
|
||||||
|
public void show() {
|
||||||
|
if (frame != null) {
|
||||||
|
frame.setVisible(true);
|
||||||
|
frame.toFront();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建菜单栏
|
||||||
|
*/
|
||||||
|
private JMenuBar createMenuBar() {
|
||||||
|
JMenuBar menuBar = new JMenuBar();
|
||||||
|
|
||||||
|
// 文件菜单
|
||||||
|
JMenu fileMenu = new JMenu("文件");
|
||||||
|
JMenuItem refreshItem = new JMenuItem("刷新");
|
||||||
|
JMenuItem exitItem = new JMenuItem("退出");
|
||||||
|
|
||||||
|
refreshItem.addActionListener(e -> refreshData());
|
||||||
|
exitItem.addActionListener(e -> System.exit(0));
|
||||||
|
|
||||||
|
fileMenu.add(refreshItem);
|
||||||
|
fileMenu.addSeparator();
|
||||||
|
fileMenu.add(exitItem);
|
||||||
|
|
||||||
|
// 视图菜单
|
||||||
|
JMenu viewMenu = new JMenu("视图");
|
||||||
|
JMenuItem alwaysOnTopItem = new JCheckBoxMenuItem("窗口置顶");
|
||||||
|
|
||||||
|
alwaysOnTopItem.addActionListener(e -> {
|
||||||
|
boolean selected = ((JCheckBoxMenuItem) e.getSource()).isSelected();
|
||||||
|
frame.setAlwaysOnTop(selected);
|
||||||
|
});
|
||||||
|
|
||||||
|
viewMenu.add(alwaysOnTopItem);
|
||||||
|
|
||||||
|
// 帮助菜单
|
||||||
|
JMenu helpMenu = new JMenu("帮助");
|
||||||
|
JMenuItem aboutItem = new JMenuItem("关于");
|
||||||
|
|
||||||
|
aboutItem.addActionListener(e -> AboutDialog.showDialog(frame));
|
||||||
|
|
||||||
|
helpMenu.add(aboutItem);
|
||||||
|
|
||||||
|
// 添加菜单到菜单栏
|
||||||
|
menuBar.add(fileMenu);
|
||||||
|
menuBar.add(viewMenu);
|
||||||
|
menuBar.add(helpMenu);
|
||||||
|
|
||||||
|
return menuBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示任务详情
|
||||||
|
*
|
||||||
|
* @param table 表格
|
||||||
|
*/
|
||||||
|
private void showTaskDetails(JTable table) {
|
||||||
|
int selectedRow = table.getSelectedRow();
|
||||||
|
if (selectedRow >= 0) {
|
||||||
|
Map<String, Object> taskInfo = null;
|
||||||
|
|
||||||
|
if (table == currentTaskTable) {
|
||||||
|
// 当前任务表格
|
||||||
|
taskInfo = printQueueService.getCurrentTaskInfo();
|
||||||
|
} else if (table == queuedTasksTable) {
|
||||||
|
// 队列任务表格
|
||||||
|
List<Map<String, Object>> queuedTasks = printQueueService.getQueuedTasksInfo();
|
||||||
|
if (selectedRow < queuedTasks.size()) {
|
||||||
|
taskInfo = queuedTasks.get(selectedRow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (taskInfo != null) {
|
||||||
|
PrintTaskDetailDialog dialog = new PrintTaskDetailDialog(frame, taskInfo);
|
||||||
|
dialog.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭窗口和资源
|
||||||
|
*/
|
||||||
|
@PreDestroy
|
||||||
|
public void shutdown() {
|
||||||
|
refreshExecutor.shutdownNow();
|
||||||
|
printerStatusPanel.shutdown();
|
||||||
|
statisticsPanel.shutdown();
|
||||||
|
if (frame != null) {
|
||||||
|
frame.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,162 @@
|
|||||||
|
package com.goeing.printserver.main.gui;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印服务器系统托盘
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class PrintServerTray {
|
||||||
|
|
||||||
|
private final PrintQueueGUI printQueueGUI;
|
||||||
|
private TrayIcon trayIcon;
|
||||||
|
private SystemTray systemTray;
|
||||||
|
private boolean traySupported;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public PrintServerTray(PrintQueueGUI printQueueGUI) {
|
||||||
|
this.printQueueGUI = printQueueGUI;
|
||||||
|
// 初始化逻辑移至initialize()方法
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化系统托盘
|
||||||
|
*/
|
||||||
|
public void initialize() {
|
||||||
|
// 在无头模式下不初始化系统托盘
|
||||||
|
if (GraphicsEnvironment.isHeadless() || Boolean.getBoolean("app.headless.mode")) {
|
||||||
|
log.info("在无头模式下运行,跳过系统托盘初始化");
|
||||||
|
traySupported = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
initializeTray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化系统托盘
|
||||||
|
*/
|
||||||
|
private void initializeTray() {
|
||||||
|
// 检查系统是否支持系统托盘
|
||||||
|
if (!SystemTray.isSupported()) {
|
||||||
|
log.warn("系统不支持系统托盘功能");
|
||||||
|
traySupported = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
traySupported = true;
|
||||||
|
systemTray = SystemTray.getSystemTray();
|
||||||
|
|
||||||
|
// 创建托盘图标
|
||||||
|
Image trayImage = createTrayImage();
|
||||||
|
trayIcon = new TrayIcon(trayImage, "打印服务器");
|
||||||
|
trayIcon.setImageAutoSize(true);
|
||||||
|
|
||||||
|
// 创建弹出菜单
|
||||||
|
PopupMenu popupMenu = createPopupMenu();
|
||||||
|
trayIcon.setPopupMenu(popupMenu);
|
||||||
|
|
||||||
|
// 添加鼠标点击事件
|
||||||
|
trayIcon.addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
if (e.getButton() == MouseEvent.BUTTON1) {
|
||||||
|
// 左键点击显示主窗口
|
||||||
|
printQueueGUI.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加托盘图标
|
||||||
|
try {
|
||||||
|
systemTray.add(trayIcon);
|
||||||
|
log.info("系统托盘图标已添加");
|
||||||
|
} catch (AWTException e) {
|
||||||
|
log.error("添加系统托盘图标失败", e);
|
||||||
|
traySupported = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建托盘图标图像
|
||||||
|
*/
|
||||||
|
private Image createTrayImage() {
|
||||||
|
// 创建一个简单的打印机图标
|
||||||
|
BufferedImage image = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
|
||||||
|
Graphics2D g2d = image.createGraphics();
|
||||||
|
|
||||||
|
// 设置抗锯齿
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
|
||||||
|
// 绘制打印机图标
|
||||||
|
g2d.setColor(Color.BLACK);
|
||||||
|
g2d.fillRect(2, 10, 12, 5); // 打印机底座
|
||||||
|
g2d.fillRect(3, 3, 10, 7); // 打印机主体
|
||||||
|
g2d.setColor(Color.WHITE);
|
||||||
|
g2d.fillRect(4, 4, 8, 5); // 打印机内部
|
||||||
|
g2d.setColor(Color.BLACK);
|
||||||
|
g2d.fillRect(5, 6, 6, 1); // 打印纸
|
||||||
|
|
||||||
|
g2d.dispose();
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建弹出菜单
|
||||||
|
*/
|
||||||
|
private PopupMenu createPopupMenu() {
|
||||||
|
PopupMenu popupMenu = new PopupMenu();
|
||||||
|
|
||||||
|
// 添加菜单项
|
||||||
|
MenuItem openItem = new MenuItem("打开主窗口");
|
||||||
|
MenuItem exitItem = new MenuItem("退出");
|
||||||
|
|
||||||
|
// 设置事件监听器
|
||||||
|
openItem.addActionListener(e -> printQueueGUI.show());
|
||||||
|
exitItem.addActionListener(e -> System.exit(0));
|
||||||
|
|
||||||
|
// 添加菜单项到弹出菜单
|
||||||
|
popupMenu.add(openItem);
|
||||||
|
popupMenu.addSeparator();
|
||||||
|
popupMenu.add(exitItem);
|
||||||
|
|
||||||
|
return popupMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示通知消息
|
||||||
|
*/
|
||||||
|
public void displayMessage(String caption, String text, TrayIcon.MessageType messageType) {
|
||||||
|
if (traySupported && trayIcon != null) {
|
||||||
|
trayIcon.displayMessage(caption, text, messageType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新托盘图标提示文本
|
||||||
|
*/
|
||||||
|
public void updateTooltip(String tooltip) {
|
||||||
|
if (traySupported && trayIcon != null) {
|
||||||
|
trayIcon.setToolTip(tooltip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭系统托盘
|
||||||
|
*/
|
||||||
|
public void shutdown() {
|
||||||
|
if (traySupported && systemTray != null && trayIcon != null) {
|
||||||
|
systemTray.remove(trayIcon);
|
||||||
|
log.info("系统托盘图标已移除");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,322 @@
|
|||||||
|
package com.goeing.printserver.main.gui;
|
||||||
|
|
||||||
|
import com.goeing.printserver.main.PrintController;
|
||||||
|
import com.goeing.printserver.main.config.PrintServerConfig;
|
||||||
|
import com.goeing.printserver.main.service.PrintQueueService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.goeing.printserver.main.gui.PrinterStatusPanel.PrinterListUpdatedEvent;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印设置面板
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class PrintSettingsPanel extends JPanel {
|
||||||
|
|
||||||
|
private final PrintQueueService printQueueService;
|
||||||
|
private final PrintServerConfig config;
|
||||||
|
private final PrintController printController;
|
||||||
|
|
||||||
|
// UI组件
|
||||||
|
private JComboBox<String> defaultPrinterComboBox;
|
||||||
|
private JSpinner maxQueueSizeSpinner;
|
||||||
|
private JCheckBox enableNotificationsCheckBox;
|
||||||
|
private JCheckBox startMinimizedCheckBox;
|
||||||
|
private JCheckBox autoStartCheckBox;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public PrintSettingsPanel(PrintQueueService printQueueService, PrintServerConfig config, PrintController printController) {
|
||||||
|
this.printQueueService = printQueueService;
|
||||||
|
this.config = config;
|
||||||
|
this.printController = printController;
|
||||||
|
initializeUI();
|
||||||
|
loadSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化用户界面
|
||||||
|
*/
|
||||||
|
private void initializeUI() {
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
|
|
||||||
|
// 创建标题
|
||||||
|
JLabel titleLabel = new JLabel("打印服务器设置");
|
||||||
|
titleLabel.setFont(new Font(titleLabel.getFont().getName(), Font.BOLD, 16));
|
||||||
|
titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
|
||||||
|
add(titleLabel, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
// 创建设置面板
|
||||||
|
JPanel settingsPanel = new JPanel(new GridBagLayout());
|
||||||
|
GridBagConstraints gbc = new GridBagConstraints();
|
||||||
|
gbc.insets = new Insets(5, 5, 5, 5);
|
||||||
|
gbc.anchor = GridBagConstraints.WEST;
|
||||||
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
|
||||||
|
// 默认打印机设置
|
||||||
|
gbc.gridx = 0;
|
||||||
|
gbc.gridy = 0;
|
||||||
|
gbc.gridwidth = 1;
|
||||||
|
settingsPanel.add(new JLabel("默认打印机:"), gbc);
|
||||||
|
|
||||||
|
gbc.gridx = 1;
|
||||||
|
gbc.weightx = 1.0;
|
||||||
|
defaultPrinterComboBox = new JComboBox<>();
|
||||||
|
updatePrinterList(); // 初始化打印机列表
|
||||||
|
settingsPanel.add(defaultPrinterComboBox, gbc);
|
||||||
|
|
||||||
|
// 最大队列大小设置
|
||||||
|
gbc.gridx = 0;
|
||||||
|
gbc.gridy = 1;
|
||||||
|
gbc.weightx = 0.0;
|
||||||
|
settingsPanel.add(new JLabel("最大队列大小:"), gbc);
|
||||||
|
|
||||||
|
gbc.gridx = 1;
|
||||||
|
gbc.weightx = 1.0;
|
||||||
|
SpinnerNumberModel spinnerModel = new SpinnerNumberModel(10, 1, 100, 1);
|
||||||
|
maxQueueSizeSpinner = new JSpinner(spinnerModel);
|
||||||
|
settingsPanel.add(maxQueueSizeSpinner, gbc);
|
||||||
|
|
||||||
|
// 启用通知设置
|
||||||
|
gbc.gridx = 0;
|
||||||
|
gbc.gridy = 2;
|
||||||
|
gbc.gridwidth = 2;
|
||||||
|
enableNotificationsCheckBox = new JCheckBox("启用系统通知");
|
||||||
|
settingsPanel.add(enableNotificationsCheckBox, gbc);
|
||||||
|
|
||||||
|
// 启动时最小化设置
|
||||||
|
gbc.gridx = 0;
|
||||||
|
gbc.gridy = 3;
|
||||||
|
startMinimizedCheckBox = new JCheckBox("启动时最小化到系统托盘");
|
||||||
|
settingsPanel.add(startMinimizedCheckBox, gbc);
|
||||||
|
|
||||||
|
// 开机自启动设置
|
||||||
|
gbc.gridx = 0;
|
||||||
|
gbc.gridy = 4;
|
||||||
|
autoStartCheckBox = new JCheckBox("开机自动启动");
|
||||||
|
settingsPanel.add(autoStartCheckBox, gbc);
|
||||||
|
|
||||||
|
// 添加一个弹性空间
|
||||||
|
gbc.gridx = 0;
|
||||||
|
gbc.gridy = 5;
|
||||||
|
gbc.weighty = 1.0;
|
||||||
|
settingsPanel.add(Box.createVerticalGlue(), gbc);
|
||||||
|
|
||||||
|
// 添加设置面板到滚动面板
|
||||||
|
JScrollPane scrollPane = new JScrollPane(settingsPanel);
|
||||||
|
scrollPane.setBorder(null);
|
||||||
|
add(scrollPane, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
// 创建按钮面板
|
||||||
|
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||||
|
JButton saveButton = new JButton("保存设置");
|
||||||
|
saveButton.addActionListener(e -> saveSettings());
|
||||||
|
buttonPanel.add(saveButton);
|
||||||
|
|
||||||
|
JButton resetButton = new JButton("重置默认");
|
||||||
|
resetButton.addActionListener(e -> resetSettings());
|
||||||
|
buttonPanel.add(resetButton);
|
||||||
|
|
||||||
|
add(buttonPanel, BorderLayout.SOUTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载设置
|
||||||
|
*/
|
||||||
|
private void loadSettings() {
|
||||||
|
// 从配置对象加载设置
|
||||||
|
config.loadConfig();
|
||||||
|
|
||||||
|
// 更新UI组件
|
||||||
|
defaultPrinterComboBox.setSelectedItem(config.getDefaultPrinter());
|
||||||
|
maxQueueSizeSpinner.setValue(config.getMaxQueueSize());
|
||||||
|
enableNotificationsCheckBox.setSelected(config.isEnableNotifications());
|
||||||
|
startMinimizedCheckBox.setSelected(config.isStartMinimized());
|
||||||
|
autoStartCheckBox.setSelected(config.isAutoStart());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存设置
|
||||||
|
*/
|
||||||
|
private void saveSettings() {
|
||||||
|
try {
|
||||||
|
// 从UI组件获取设置值
|
||||||
|
String defaultPrinter = defaultPrinterComboBox.getSelectedItem().toString();
|
||||||
|
int maxQueueSize = (Integer) maxQueueSizeSpinner.getValue();
|
||||||
|
boolean enableNotifications = enableNotificationsCheckBox.isSelected();
|
||||||
|
boolean startMinimized = startMinimizedCheckBox.isSelected();
|
||||||
|
boolean autoStart = autoStartCheckBox.isSelected();
|
||||||
|
|
||||||
|
// 更新配置对象
|
||||||
|
config.setDefaultPrinter(defaultPrinter);
|
||||||
|
config.setMaxQueueSize(maxQueueSize);
|
||||||
|
config.setEnableNotifications(enableNotifications);
|
||||||
|
config.setStartMinimized(startMinimized);
|
||||||
|
config.setAutoStart(autoStart);
|
||||||
|
|
||||||
|
// 保存配置
|
||||||
|
config.saveConfig();
|
||||||
|
|
||||||
|
// 应用设置到服务
|
||||||
|
applySettings();
|
||||||
|
|
||||||
|
JOptionPane.showMessageDialog(this, "设置已保存", "成功", JOptionPane.INFORMATION_MESSAGE);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("保存设置时发生错误", e);
|
||||||
|
JOptionPane.showMessageDialog(this, "保存设置失败: " + e.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置设置为默认值
|
||||||
|
*/
|
||||||
|
private void resetSettings() {
|
||||||
|
int option = JOptionPane.showConfirmDialog(this, "确定要重置所有设置为默认值吗?", "确认", JOptionPane.YES_NO_OPTION);
|
||||||
|
if (option == JOptionPane.YES_OPTION) {
|
||||||
|
try {
|
||||||
|
// 重置配置对象到默认值
|
||||||
|
config.resetToDefaults();
|
||||||
|
// 保存默认配置
|
||||||
|
config.saveConfig();
|
||||||
|
// 更新UI
|
||||||
|
loadSettings();
|
||||||
|
// 应用默认设置
|
||||||
|
applySettings();
|
||||||
|
|
||||||
|
JOptionPane.showMessageDialog(this, "设置已重置为默认值", "成功", JOptionPane.INFORMATION_MESSAGE);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("重置设置时发生错误", e);
|
||||||
|
JOptionPane.showMessageDialog(this, "重置设置失败: " + e.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用设置到服务
|
||||||
|
*/
|
||||||
|
private void applySettings() {
|
||||||
|
// 在这里应用设置到相关服务
|
||||||
|
// 例如,可以设置打印队列服务的最大队列大小等
|
||||||
|
printQueueService.setMaxQueueSize(config.getMaxQueueSize());
|
||||||
|
|
||||||
|
// 通知设置变更
|
||||||
|
log.info("应用设置: 默认打印机={}, 最大队列大小={}, 启用通知={}, 启动时最小化={}, 开机自启动={}",
|
||||||
|
config.getDefaultPrinter(),
|
||||||
|
config.getMaxQueueSize(),
|
||||||
|
config.isEnableNotifications(),
|
||||||
|
config.isStartMinimized(),
|
||||||
|
config.isAutoStart());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取是否启动时最小化设置
|
||||||
|
*
|
||||||
|
* @return 是否启动时最小化
|
||||||
|
*/
|
||||||
|
public boolean isStartMinimized() {
|
||||||
|
return config.isStartMinimized();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取是否启用通知设置
|
||||||
|
*
|
||||||
|
* @return 是否启用通知
|
||||||
|
*/
|
||||||
|
public boolean isEnableNotifications() {
|
||||||
|
return config.isEnableNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取默认打印机设置
|
||||||
|
*
|
||||||
|
* @return 默认打印机名称
|
||||||
|
*/
|
||||||
|
public String getDefaultPrinter() {
|
||||||
|
return config.getDefaultPrinter();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最大队列大小设置
|
||||||
|
*
|
||||||
|
* @return 最大队列大小
|
||||||
|
*/
|
||||||
|
public int getMaxQueueSize() {
|
||||||
|
return config.getMaxQueueSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新打印机列表
|
||||||
|
*/
|
||||||
|
public void updatePrinterList() {
|
||||||
|
try {
|
||||||
|
// 保存当前选中的打印机
|
||||||
|
String selectedPrinter = defaultPrinterComboBox.getSelectedItem() != null ?
|
||||||
|
defaultPrinterComboBox.getSelectedItem().toString() : null;
|
||||||
|
|
||||||
|
// 清空打印机列表
|
||||||
|
defaultPrinterComboBox.removeAllItems();
|
||||||
|
|
||||||
|
List<String> printers = printController.printerList();
|
||||||
|
updatePrinterComboBox(printers, selectedPrinter);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("更新打印机列表时发生错误", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用提供的打印机列表更新下拉框
|
||||||
|
*
|
||||||
|
* @param printers 打印机列表
|
||||||
|
* @param selectedPrinter 当前选中的打印机
|
||||||
|
*/
|
||||||
|
private void updatePrinterComboBox(List<String> printers, String selectedPrinter) {
|
||||||
|
if (printers != null && !printers.isEmpty()) {
|
||||||
|
for (String printer : printers) {
|
||||||
|
defaultPrinterComboBox.addItem(printer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试恢复之前选中的打印机
|
||||||
|
if (selectedPrinter != null && printers.contains(selectedPrinter)) {
|
||||||
|
defaultPrinterComboBox.setSelectedItem(selectedPrinter);
|
||||||
|
} else {
|
||||||
|
// 如果之前选中的打印机不存在,则使用配置中的默认打印机
|
||||||
|
String defaultPrinter = config.getDefaultPrinter();
|
||||||
|
if (defaultPrinter != null && printers.contains(defaultPrinter)) {
|
||||||
|
defaultPrinterComboBox.setSelectedItem(defaultPrinter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用debug级别记录日志,减少info日志输出
|
||||||
|
log.debug("已更新打印机列表,共{}个打印机", printers.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听打印机列表更新事件
|
||||||
|
*
|
||||||
|
* @param event 打印机列表更新事件
|
||||||
|
*/
|
||||||
|
@EventListener
|
||||||
|
public void onPrinterListUpdated(PrinterListUpdatedEvent event) {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
// 保存当前选中的打印机
|
||||||
|
String selectedPrinter = defaultPrinterComboBox.getSelectedItem() != null ?
|
||||||
|
defaultPrinterComboBox.getSelectedItem().toString() : null;
|
||||||
|
|
||||||
|
// 清空并重新填充打印机列表
|
||||||
|
defaultPrinterComboBox.removeAllItems();
|
||||||
|
|
||||||
|
updatePrinterComboBox(event.getPrinters(), selectedPrinter);
|
||||||
|
// 使用debug级别记录日志,减少info日志输出
|
||||||
|
log.debug("收到打印机列表更新事件,已更新打印机下拉列表");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,200 @@
|
|||||||
|
package com.goeing.printserver.main.gui;
|
||||||
|
|
||||||
|
import com.goeing.printserver.main.service.PrintQueueService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印统计面板
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class PrintStatisticsPanel extends JPanel {
|
||||||
|
|
||||||
|
private final PrintQueueService printQueueService;
|
||||||
|
private final ScheduledExecutorService refreshExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
// 统计数据
|
||||||
|
private final AtomicInteger totalTasksCount = new AtomicInteger(0);
|
||||||
|
private final AtomicInteger completedTasksCount = new AtomicInteger(0);
|
||||||
|
private final AtomicInteger failedTasksCount = new AtomicInteger(0);
|
||||||
|
private LocalDateTime startTime = LocalDateTime.now();
|
||||||
|
|
||||||
|
// UI组件
|
||||||
|
private JLabel totalTasksLabel;
|
||||||
|
private JLabel completedTasksLabel;
|
||||||
|
private JLabel failedTasksLabel;
|
||||||
|
private JLabel queueSizeLabel;
|
||||||
|
private JLabel upTimeLabel;
|
||||||
|
private JLabel currentTimeLabel;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public PrintStatisticsPanel(PrintQueueService printQueueService) {
|
||||||
|
this.printQueueService = printQueueService;
|
||||||
|
initializeUI();
|
||||||
|
startRefreshTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化用户界面
|
||||||
|
*/
|
||||||
|
private void initializeUI() {
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
|
|
||||||
|
// 创建顶部面板
|
||||||
|
JPanel topPanel = new JPanel(new BorderLayout());
|
||||||
|
JLabel titleLabel = new JLabel("打印统计");
|
||||||
|
titleLabel.setFont(new Font(titleLabel.getFont().getName(), Font.BOLD, 14));
|
||||||
|
topPanel.add(titleLabel, BorderLayout.WEST);
|
||||||
|
|
||||||
|
// 创建刷新按钮
|
||||||
|
JButton refreshButton = new JButton("刷新");
|
||||||
|
refreshButton.addActionListener(e -> refreshStatistics());
|
||||||
|
topPanel.add(refreshButton, BorderLayout.EAST);
|
||||||
|
|
||||||
|
add(topPanel, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
// 创建统计信息面板
|
||||||
|
JPanel statsPanel = new JPanel();
|
||||||
|
statsPanel.setLayout(new BoxLayout(statsPanel, BoxLayout.Y_AXIS));
|
||||||
|
statsPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
|
|
||||||
|
// 添加统计信息
|
||||||
|
totalTasksLabel = createStatLabel("总任务数: 0");
|
||||||
|
completedTasksLabel = createStatLabel("已完成任务: 0");
|
||||||
|
failedTasksLabel = createStatLabel("失败任务: 0");
|
||||||
|
queueSizeLabel = createStatLabel("当前队列长度: 0");
|
||||||
|
upTimeLabel = createStatLabel("运行时间: 0小时0分钟");
|
||||||
|
currentTimeLabel = createStatLabel("当前时间: " + LocalDateTime.now().format(dateFormatter));
|
||||||
|
|
||||||
|
statsPanel.add(totalTasksLabel);
|
||||||
|
statsPanel.add(Box.createVerticalStrut(10));
|
||||||
|
statsPanel.add(completedTasksLabel);
|
||||||
|
statsPanel.add(Box.createVerticalStrut(10));
|
||||||
|
statsPanel.add(failedTasksLabel);
|
||||||
|
statsPanel.add(Box.createVerticalStrut(10));
|
||||||
|
statsPanel.add(queueSizeLabel);
|
||||||
|
statsPanel.add(Box.createVerticalStrut(10));
|
||||||
|
statsPanel.add(upTimeLabel);
|
||||||
|
statsPanel.add(Box.createVerticalStrut(10));
|
||||||
|
statsPanel.add(currentTimeLabel);
|
||||||
|
|
||||||
|
// 添加一个弹性空间,使内容居上
|
||||||
|
statsPanel.add(Box.createVerticalGlue());
|
||||||
|
|
||||||
|
// 添加重置按钮
|
||||||
|
JButton resetButton = new JButton("重置统计");
|
||||||
|
resetButton.addActionListener(e -> resetStatistics());
|
||||||
|
resetButton.setAlignmentX(0.0f); // LEFT_ALIGNMENT = 0.0f
|
||||||
|
|
||||||
|
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||||
|
buttonPanel.add(resetButton);
|
||||||
|
buttonPanel.setAlignmentX(0.0f); // LEFT_ALIGNMENT = 0.0f
|
||||||
|
|
||||||
|
statsPanel.add(buttonPanel);
|
||||||
|
|
||||||
|
add(new JScrollPane(statsPanel), BorderLayout.CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建统计标签
|
||||||
|
*
|
||||||
|
* @param text 标签文本
|
||||||
|
* @return 标签组件
|
||||||
|
*/
|
||||||
|
private JLabel createStatLabel(String text) {
|
||||||
|
JLabel label = new JLabel(text);
|
||||||
|
label.setFont(new Font(label.getFont().getName(), Font.PLAIN, 14));
|
||||||
|
label.setAlignmentX(0.0f); // LEFT_ALIGNMENT = 0.0f
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动定时刷新
|
||||||
|
*/
|
||||||
|
private void startRefreshTimer() {
|
||||||
|
refreshExecutor.scheduleAtFixedRate(this::refreshStatistics, 0, 1, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新统计信息
|
||||||
|
*/
|
||||||
|
private void refreshStatistics() {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
try {
|
||||||
|
// 更新队列大小
|
||||||
|
int queueSize = printQueueService.getQueueSize();
|
||||||
|
queueSizeLabel.setText("当前队列长度: " + queueSize);
|
||||||
|
|
||||||
|
// 更新运行时间
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
long hours = java.time.Duration.between(startTime, now).toHours();
|
||||||
|
long minutes = java.time.Duration.between(startTime, now).toMinutes() % 60;
|
||||||
|
upTimeLabel.setText(String.format("运行时间: %d小时%d分钟", hours, minutes));
|
||||||
|
|
||||||
|
// 更新当前时间
|
||||||
|
currentTimeLabel.setText("当前时间: " + now.format(dateFormatter));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("刷新统计信息时发生错误", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置统计信息
|
||||||
|
*/
|
||||||
|
private void resetStatistics() {
|
||||||
|
totalTasksCount.set(0);
|
||||||
|
completedTasksCount.set(0);
|
||||||
|
failedTasksCount.set(0);
|
||||||
|
startTime = LocalDateTime.now();
|
||||||
|
|
||||||
|
totalTasksLabel.setText("总任务数: 0");
|
||||||
|
completedTasksLabel.setText("已完成任务: 0");
|
||||||
|
failedTasksLabel.setText("失败任务: 0");
|
||||||
|
upTimeLabel.setText("运行时间: 0小时0分钟");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加总任务数
|
||||||
|
*/
|
||||||
|
public void incrementTotalTasks() {
|
||||||
|
int total = totalTasksCount.incrementAndGet();
|
||||||
|
SwingUtilities.invokeLater(() -> totalTasksLabel.setText("总任务数: " + total));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加已完成任务数
|
||||||
|
*/
|
||||||
|
public void incrementCompletedTasks() {
|
||||||
|
int completed = completedTasksCount.incrementAndGet();
|
||||||
|
SwingUtilities.invokeLater(() -> completedTasksLabel.setText("已完成任务: " + completed));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加失败任务数
|
||||||
|
*/
|
||||||
|
public void incrementFailedTasks() {
|
||||||
|
int failed = failedTasksCount.incrementAndGet();
|
||||||
|
SwingUtilities.invokeLater(() -> failedTasksLabel.setText("失败任务: " + failed));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭资源
|
||||||
|
*/
|
||||||
|
public void shutdown() {
|
||||||
|
refreshExecutor.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,193 @@
|
|||||||
|
package com.goeing.printserver.main.gui;
|
||||||
|
|
||||||
|
import com.goeing.printserver.main.domain.PrintTask;
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.border.EmptyBorder;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.Dialog.ModalityType;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印任务详情对话框
|
||||||
|
*/
|
||||||
|
public class PrintTaskDetailDialog extends JDialog {
|
||||||
|
|
||||||
|
private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建打印任务详情对话框
|
||||||
|
*
|
||||||
|
* @param parent 父窗口
|
||||||
|
* @param task 任务信息
|
||||||
|
*/
|
||||||
|
public PrintTaskDetailDialog(Frame parent, Map<String, Object> task) {
|
||||||
|
super(parent, "打印任务详情", true);
|
||||||
|
initializeUI(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建打印任务详情对话框
|
||||||
|
*
|
||||||
|
* @param parent 父窗口
|
||||||
|
* @param task 打印任务对象
|
||||||
|
*/
|
||||||
|
public PrintTaskDetailDialog(Frame parent, PrintTask task) {
|
||||||
|
super(parent, "打印任务详情", true);
|
||||||
|
initializeUI(task.toMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建打印任务详情对话框(接受Window类型参数)
|
||||||
|
*
|
||||||
|
* @param parent 父窗口
|
||||||
|
* @param task 打印任务对象
|
||||||
|
*/
|
||||||
|
public PrintTaskDetailDialog(Window parent, PrintTask task) {
|
||||||
|
super(parent, "打印任务详情", ModalityType.APPLICATION_MODAL);
|
||||||
|
initializeUI(task.toMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建打印任务详情对话框(接受Window类型参数和Map类型任务信息)
|
||||||
|
*
|
||||||
|
* @param parent 父窗口
|
||||||
|
* @param task 任务信息
|
||||||
|
*/
|
||||||
|
public PrintTaskDetailDialog(Window parent, Map<String, Object> task) {
|
||||||
|
super(parent, "打印任务详情", ModalityType.APPLICATION_MODAL);
|
||||||
|
initializeUI(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化用户界面
|
||||||
|
*
|
||||||
|
* @param task 任务信息
|
||||||
|
*/
|
||||||
|
private void initializeUI(Map<String, Object> task) {
|
||||||
|
setSize(500, 400);
|
||||||
|
setLocationRelativeTo(getParent());
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
// 创建内容面板
|
||||||
|
JPanel contentPanel = new JPanel();
|
||||||
|
contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS));
|
||||||
|
contentPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||||
|
|
||||||
|
// 添加任务信息
|
||||||
|
addTaskInfo(contentPanel, task);
|
||||||
|
|
||||||
|
// 添加滚动面板
|
||||||
|
JScrollPane scrollPane = new JScrollPane(contentPanel);
|
||||||
|
scrollPane.setBorder(null);
|
||||||
|
add(scrollPane, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
// 添加关闭按钮
|
||||||
|
JButton closeButton = new JButton("关闭");
|
||||||
|
closeButton.addActionListener(e -> dispose());
|
||||||
|
|
||||||
|
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||||
|
buttonPanel.add(closeButton);
|
||||||
|
add(buttonPanel, BorderLayout.SOUTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加任务信息
|
||||||
|
*
|
||||||
|
* @param panel 面板
|
||||||
|
* @param task 任务信息
|
||||||
|
*/
|
||||||
|
private void addTaskInfo(JPanel panel, Map<String, Object> task) {
|
||||||
|
// 添加标题
|
||||||
|
JLabel titleLabel = new JLabel("打印任务详情");
|
||||||
|
titleLabel.setFont(new Font(titleLabel.getFont().getName(), Font.BOLD, 16));
|
||||||
|
titleLabel.setAlignmentX(0.0f); // LEFT_ALIGNMENT = 0.0f
|
||||||
|
panel.add(titleLabel);
|
||||||
|
panel.add(Box.createVerticalStrut(10));
|
||||||
|
|
||||||
|
// 添加基本信息
|
||||||
|
addInfoField(panel, "文件URL:", getStringValue(task.get("fileUrl")));
|
||||||
|
addInfoField(panel, "打印机:", getStringValue(task.get("printerName")));
|
||||||
|
addInfoField(panel, "状态:", getStringValue(task.get("status")));
|
||||||
|
addInfoField(panel, "队列时间:", formatDateTime(task.get("queuedTime")));
|
||||||
|
|
||||||
|
// 添加开始和结束时间(如果有)
|
||||||
|
if (task.containsKey("startTime") && task.get("startTime") != null) {
|
||||||
|
addInfoField(panel, "开始时间:", formatDateTime(task.get("startTime")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.containsKey("endTime") && task.get("endTime") != null) {
|
||||||
|
addInfoField(panel, "结束时间:", formatDateTime(task.get("endTime")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加打印选项(如果有)
|
||||||
|
if (task.containsKey("printOption") && task.get("printOption") != null) {
|
||||||
|
panel.add(Box.createVerticalStrut(10));
|
||||||
|
JLabel optionsLabel = new JLabel("打印选项");
|
||||||
|
optionsLabel.setFont(new Font(optionsLabel.getFont().getName(), Font.BOLD, 14));
|
||||||
|
optionsLabel.setAlignmentX(0.0f); // LEFT_ALIGNMENT = 0.0f
|
||||||
|
panel.add(optionsLabel);
|
||||||
|
panel.add(Box.createVerticalStrut(5));
|
||||||
|
|
||||||
|
Map<String, Object> printOption = (Map<String, Object>) task.get("printOption");
|
||||||
|
for (Map.Entry<String, Object> entry : printOption.entrySet()) {
|
||||||
|
addInfoField(panel, entry.getKey() + ":", getStringValue(entry.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加信息字段
|
||||||
|
*
|
||||||
|
* @param panel 面板
|
||||||
|
* @param label 标签
|
||||||
|
* @param value 值
|
||||||
|
*/
|
||||||
|
private void addInfoField(JPanel panel, String label, String value) {
|
||||||
|
JPanel fieldPanel = new JPanel(new BorderLayout());
|
||||||
|
fieldPanel.setAlignmentX(0.0f); // LEFT_ALIGNMENT = 0.0f
|
||||||
|
fieldPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 25));
|
||||||
|
|
||||||
|
JLabel labelComponent = new JLabel(label);
|
||||||
|
labelComponent.setPreferredSize(new Dimension(100, 20));
|
||||||
|
fieldPanel.add(labelComponent, BorderLayout.WEST);
|
||||||
|
|
||||||
|
JTextField valueField = new JTextField(value);
|
||||||
|
valueField.setEditable(false);
|
||||||
|
valueField.setBorder(null);
|
||||||
|
valueField.setBackground(null);
|
||||||
|
fieldPanel.add(valueField, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
panel.add(fieldPanel);
|
||||||
|
panel.add(Box.createVerticalStrut(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字符串值
|
||||||
|
*
|
||||||
|
* @param value 值
|
||||||
|
* @return 字符串值
|
||||||
|
*/
|
||||||
|
private String getStringValue(Object value) {
|
||||||
|
return value != null ? value.toString() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化日期时间
|
||||||
|
*
|
||||||
|
* @param dateTimeObj 日期时间对象
|
||||||
|
* @return 格式化后的日期时间字符串
|
||||||
|
*/
|
||||||
|
private String formatDateTime(Object dateTimeObj) {
|
||||||
|
if (dateTimeObj == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dateTimeObj instanceof LocalDateTime) {
|
||||||
|
return ((LocalDateTime) dateTimeObj).format(dateFormatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dateTimeObj.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,305 @@
|
|||||||
|
package com.goeing.printserver.main.gui;
|
||||||
|
|
||||||
|
import com.goeing.printserver.main.domain.PrintTask;
|
||||||
|
import com.goeing.printserver.main.service.PrintQueueService;
|
||||||
|
import com.goeing.printserver.main.service.PrintHistoryService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.table.DefaultTableModel;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印任务搜索面板
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class PrintTaskSearchPanel extends JPanel {
|
||||||
|
|
||||||
|
private final PrintQueueService printQueueService;
|
||||||
|
private final PrintHistoryService historyService;
|
||||||
|
private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
// UI组件
|
||||||
|
private JComboBox<String> printerComboBox;
|
||||||
|
private JComboBox<String> statusComboBox;
|
||||||
|
private JTextField fileUrlField;
|
||||||
|
private JCheckBox includeHistoryCheckBox;
|
||||||
|
private JTable resultsTable;
|
||||||
|
private DefaultTableModel tableModel;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public PrintTaskSearchPanel(PrintQueueService printQueueService, PrintHistoryService historyService) {
|
||||||
|
this.printQueueService = printQueueService;
|
||||||
|
this.historyService = historyService;
|
||||||
|
initializeUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化用户界面
|
||||||
|
*/
|
||||||
|
private void initializeUI() {
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
|
|
||||||
|
// 创建搜索条件面板
|
||||||
|
JPanel searchPanel = new JPanel(new GridBagLayout());
|
||||||
|
GridBagConstraints gbc = new GridBagConstraints();
|
||||||
|
gbc.insets = new Insets(5, 5, 5, 5);
|
||||||
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
|
||||||
|
// 打印机选择
|
||||||
|
gbc.gridx = 0;
|
||||||
|
gbc.gridy = 0;
|
||||||
|
searchPanel.add(new JLabel("打印机:"), gbc);
|
||||||
|
|
||||||
|
gbc.gridx = 1;
|
||||||
|
gbc.weightx = 1.0;
|
||||||
|
printerComboBox = new JComboBox<>(new String[]{"全部", "打印机1", "打印机2", "打印机3"});
|
||||||
|
searchPanel.add(printerComboBox, gbc);
|
||||||
|
|
||||||
|
// 状态选择
|
||||||
|
gbc.gridx = 0;
|
||||||
|
gbc.gridy = 1;
|
||||||
|
gbc.weightx = 0.0;
|
||||||
|
searchPanel.add(new JLabel("状态:"), gbc);
|
||||||
|
|
||||||
|
gbc.gridx = 1;
|
||||||
|
gbc.weightx = 1.0;
|
||||||
|
statusComboBox = new JComboBox<>(new String[]{"全部", "queued", "processing", "completed", "failed"});
|
||||||
|
searchPanel.add(statusComboBox, gbc);
|
||||||
|
|
||||||
|
// 文件URL
|
||||||
|
gbc.gridx = 0;
|
||||||
|
gbc.gridy = 2;
|
||||||
|
gbc.weightx = 0.0;
|
||||||
|
searchPanel.add(new JLabel("文件URL:"), gbc);
|
||||||
|
|
||||||
|
gbc.gridx = 1;
|
||||||
|
gbc.weightx = 1.0;
|
||||||
|
fileUrlField = new JTextField();
|
||||||
|
searchPanel.add(fileUrlField, gbc);
|
||||||
|
|
||||||
|
// 包含历史记录选项
|
||||||
|
gbc.gridx = 0;
|
||||||
|
gbc.gridy = 3;
|
||||||
|
gbc.gridwidth = 2;
|
||||||
|
includeHistoryCheckBox = new JCheckBox("包含历史记录");
|
||||||
|
includeHistoryCheckBox.setSelected(true);
|
||||||
|
searchPanel.add(includeHistoryCheckBox, gbc);
|
||||||
|
|
||||||
|
// 搜索按钮
|
||||||
|
gbc.gridx = 0;
|
||||||
|
gbc.gridy = 4;
|
||||||
|
gbc.gridwidth = 2;
|
||||||
|
gbc.weightx = 1.0;
|
||||||
|
gbc.anchor = GridBagConstraints.CENTER;
|
||||||
|
JButton searchButton = new JButton("搜索");
|
||||||
|
searchButton.addActionListener(e -> performSearch());
|
||||||
|
searchPanel.add(searchButton, gbc);
|
||||||
|
|
||||||
|
add(searchPanel, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
// 创建结果表格
|
||||||
|
String[] columnNames = {"文件URL", "打印机", "状态", "队列时间", "开始时间", "结束时间"};
|
||||||
|
tableModel = new DefaultTableModel(columnNames, 0) {
|
||||||
|
@Override
|
||||||
|
public boolean isCellEditable(int row, int column) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
resultsTable = new JTable(tableModel);
|
||||||
|
resultsTable.getTableHeader().setReorderingAllowed(false);
|
||||||
|
resultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
|
|
||||||
|
// 添加双击事件
|
||||||
|
resultsTable.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(java.awt.event.MouseEvent evt) {
|
||||||
|
if (evt.getClickCount() == 2) {
|
||||||
|
showTaskDetails();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
JScrollPane scrollPane = new JScrollPane(resultsTable);
|
||||||
|
add(scrollPane, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
// 创建按钮面板
|
||||||
|
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||||
|
JButton detailsButton = new JButton("查看详情");
|
||||||
|
detailsButton.addActionListener(e -> showTaskDetails());
|
||||||
|
buttonPanel.add(detailsButton);
|
||||||
|
|
||||||
|
JButton clearButton = new JButton("清空结果");
|
||||||
|
clearButton.addActionListener(e -> clearResults());
|
||||||
|
buttonPanel.add(clearButton);
|
||||||
|
|
||||||
|
add(buttonPanel, BorderLayout.SOUTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行搜索
|
||||||
|
*/
|
||||||
|
private void performSearch() {
|
||||||
|
// 清空当前结果
|
||||||
|
clearResults();
|
||||||
|
|
||||||
|
// 获取搜索条件
|
||||||
|
String printer = printerComboBox.getSelectedItem().toString();
|
||||||
|
String status = statusComboBox.getSelectedItem().toString();
|
||||||
|
String fileUrl = fileUrlField.getText().trim();
|
||||||
|
|
||||||
|
// 获取当前任务和队列任务
|
||||||
|
List<PrintTask> currentTasks = printQueueService.getCurrentTask() != null ?
|
||||||
|
List.of(printQueueService.getCurrentTask()) : List.of();
|
||||||
|
List<PrintTask> queuedTasks = printQueueService.getQueuedTasks();
|
||||||
|
|
||||||
|
// 添加当前任务和队列任务到结果中
|
||||||
|
addTasksToResults(currentTasks, printer, status, fileUrl);
|
||||||
|
addTasksToResults(queuedTasks, printer, status, fileUrl);
|
||||||
|
|
||||||
|
// 如果选择包含历史记录,则添加历史任务到结果中
|
||||||
|
if (includeHistoryCheckBox.isSelected()) {
|
||||||
|
List<PrintTask> historyTasks = historyService.getAllHistory();
|
||||||
|
addTasksToResults(historyTasks, printer, status, fileUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新状态
|
||||||
|
updateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将符合条件的任务添加到结果表格中
|
||||||
|
*
|
||||||
|
* @param tasks 任务列表
|
||||||
|
* @param printer 打印机筛选条件
|
||||||
|
* @param status 状态筛选条件
|
||||||
|
* @param fileUrl 文件URL筛选条件
|
||||||
|
*/
|
||||||
|
private void addTasksToResults(List<PrintTask> tasks, String printer, String status, String fileUrl) {
|
||||||
|
for (PrintTask task : tasks) {
|
||||||
|
// 应用筛选条件
|
||||||
|
if (!"全部".equals(printer) && !printer.equals(task.getPrinter())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!"全部".equals(status) && !status.equals(task.getStatus())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!fileUrl.isEmpty() && !task.getFileUrl().contains(fileUrl)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加到表格
|
||||||
|
Object[] row = {
|
||||||
|
task.getFileUrl(),
|
||||||
|
task.getPrinter(),
|
||||||
|
task.getStatus(),
|
||||||
|
formatDateTime(task.getQueuedTime()),
|
||||||
|
formatDateTime(task.getStartTime()),
|
||||||
|
formatDateTime(task.getEndTime())
|
||||||
|
};
|
||||||
|
tableModel.addRow(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化日期时间
|
||||||
|
*
|
||||||
|
* @param dateTime 日期时间
|
||||||
|
* @return 格式化后的字符串
|
||||||
|
*/
|
||||||
|
private String formatDateTime(LocalDateTime dateTime) {
|
||||||
|
return dateTime != null ? dateTime.format(dateFormatter) : "--";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新状态信息
|
||||||
|
*/
|
||||||
|
private void updateStatus() {
|
||||||
|
int resultCount = tableModel.getRowCount();
|
||||||
|
if (resultCount == 0) {
|
||||||
|
JOptionPane.showMessageDialog(this, "没有找到符合条件的任务", "搜索结果", JOptionPane.INFORMATION_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空搜索结果
|
||||||
|
*/
|
||||||
|
private void clearResults() {
|
||||||
|
while (tableModel.getRowCount() > 0) {
|
||||||
|
tableModel.removeRow(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示任务详情
|
||||||
|
*/
|
||||||
|
private void showTaskDetails() {
|
||||||
|
int selectedRow = resultsTable.getSelectedRow();
|
||||||
|
if (selectedRow >= 0) {
|
||||||
|
String fileUrl = (String) tableModel.getValueAt(selectedRow, 0);
|
||||||
|
String printer = (String) tableModel.getValueAt(selectedRow, 1);
|
||||||
|
String status = (String) tableModel.getValueAt(selectedRow, 2);
|
||||||
|
String queuedTime = (String) tableModel.getValueAt(selectedRow, 3);
|
||||||
|
String startTime = (String) tableModel.getValueAt(selectedRow, 4);
|
||||||
|
String endTime = (String) tableModel.getValueAt(selectedRow, 5);
|
||||||
|
|
||||||
|
// 查找对应的任务对象
|
||||||
|
PrintTask task = findTaskByFileUrl(fileUrl);
|
||||||
|
if (task != null) {
|
||||||
|
// 显示详情对话框
|
||||||
|
PrintTaskDetailDialog dialog = new PrintTaskDetailDialog(SwingUtilities.getWindowAncestor(this), task);
|
||||||
|
dialog.setVisible(true);
|
||||||
|
} else {
|
||||||
|
// 使用表格中的数据创建一个简化的任务对象
|
||||||
|
PrintTask simpleTask = new PrintTask();
|
||||||
|
simpleTask.setFileUrl(fileUrl);
|
||||||
|
simpleTask.setPrinter(printer);
|
||||||
|
simpleTask.setStatus(status);
|
||||||
|
|
||||||
|
// 显示详情对话框
|
||||||
|
PrintTaskDetailDialog dialog = new PrintTaskDetailDialog(SwingUtilities.getWindowAncestor(this), simpleTask);
|
||||||
|
dialog.setVisible(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
JOptionPane.showMessageDialog(this, "请先选择一个任务", "提示", JOptionPane.INFORMATION_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文件URL查找任务
|
||||||
|
*
|
||||||
|
* @param fileUrl 文件URL
|
||||||
|
* @return 任务对象,如果未找到则返回null
|
||||||
|
*/
|
||||||
|
private PrintTask findTaskByFileUrl(String fileUrl) {
|
||||||
|
// 检查当前任务
|
||||||
|
PrintTask currentTask = printQueueService.getCurrentTask();
|
||||||
|
if (currentTask != null && currentTask.getFileUrl().equals(fileUrl)) {
|
||||||
|
return currentTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查队列中的任务
|
||||||
|
for (PrintTask task : printQueueService.getQueuedTasks()) {
|
||||||
|
if (task.getFileUrl().equals(fileUrl)) {
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查历史记录中的任务
|
||||||
|
if (includeHistoryCheckBox.isSelected()) {
|
||||||
|
List<PrintTask> historyTasks = historyService.getHistoryByFileUrl(fileUrl);
|
||||||
|
if (!historyTasks.isEmpty()) {
|
||||||
|
return historyTasks.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,166 @@
|
|||||||
|
package com.goeing.printserver.main.gui;
|
||||||
|
|
||||||
|
import com.goeing.printserver.main.PrintController;
|
||||||
|
import com.goeing.printserver.main.config.PrintServerConfig;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.table.DefaultTableModel;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印机状态面板
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class PrinterStatusPanel extends JPanel {
|
||||||
|
|
||||||
|
private final PrintController printController;
|
||||||
|
private final PrintServerConfig config;
|
||||||
|
private JTable printerTable;
|
||||||
|
private DefaultTableModel printerModel;
|
||||||
|
private final ScheduledExecutorService refreshExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
|
||||||
|
// 使用ApplicationEventPublisher来发布事件,避免循环依赖
|
||||||
|
@Autowired
|
||||||
|
private org.springframework.context.ApplicationEventPublisher eventPublisher;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public PrinterStatusPanel(PrintController printController, PrintServerConfig config) {
|
||||||
|
this.printController = printController;
|
||||||
|
this.config = config;
|
||||||
|
initializeUI();
|
||||||
|
startRefreshTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化用户界面
|
||||||
|
*/
|
||||||
|
private void initializeUI() {
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
|
|
||||||
|
// 创建顶部面板
|
||||||
|
JPanel topPanel = new JPanel(new BorderLayout());
|
||||||
|
JLabel titleLabel = new JLabel("打印机状态");
|
||||||
|
titleLabel.setFont(new Font(titleLabel.getFont().getName(), Font.BOLD, 14));
|
||||||
|
topPanel.add(titleLabel, BorderLayout.WEST);
|
||||||
|
|
||||||
|
// 创建刷新按钮
|
||||||
|
JButton refreshButton = new JButton("刷新");
|
||||||
|
refreshButton.addActionListener(e -> refreshPrinterList());
|
||||||
|
topPanel.add(refreshButton, BorderLayout.EAST);
|
||||||
|
|
||||||
|
add(topPanel, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
// 创建表格模型
|
||||||
|
String[] columnNames = {"打印机名称", "状态", "默认"};
|
||||||
|
printerModel = new DefaultTableModel(columnNames, 0) {
|
||||||
|
@Override
|
||||||
|
public boolean isCellEditable(int row, int column) {
|
||||||
|
return false; // 禁止编辑单元格
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getColumnClass(int columnIndex) {
|
||||||
|
return columnIndex == 2 ? Boolean.class : String.class;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 创建表格
|
||||||
|
printerTable = new JTable(printerModel);
|
||||||
|
printerTable.getTableHeader().setReorderingAllowed(false);
|
||||||
|
printerTable.setFillsViewportHeight(true);
|
||||||
|
|
||||||
|
// 添加滚动面板
|
||||||
|
JScrollPane scrollPane = new JScrollPane(printerTable);
|
||||||
|
add(scrollPane, BorderLayout.CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动定时刷新
|
||||||
|
*/
|
||||||
|
private void startRefreshTimer() {
|
||||||
|
// 增加刷新间隔到60秒,减少刷新频率
|
||||||
|
refreshExecutor.scheduleAtFixedRate(this::refreshPrinterList, 0, 60, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新打印机列表
|
||||||
|
*/
|
||||||
|
private void refreshPrinterList() {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
try {
|
||||||
|
List<String> printers = printController.printerList();
|
||||||
|
updatePrinterTable(printers);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("刷新打印机列表时发生错误", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新打印机表格
|
||||||
|
*
|
||||||
|
* @param printers 打印机列表
|
||||||
|
*/
|
||||||
|
private void updatePrinterTable(List<String> printers) {
|
||||||
|
// 清空表格
|
||||||
|
printerModel.setRowCount(0);
|
||||||
|
|
||||||
|
if (printers != null && !printers.isEmpty()) {
|
||||||
|
String defaultPrinter = config.getDefaultPrinter();
|
||||||
|
|
||||||
|
for (String printer : printers) {
|
||||||
|
Object[] rowData = new Object[3];
|
||||||
|
rowData[0] = printer;
|
||||||
|
rowData[1] = "可用"; // 默认状态为可用
|
||||||
|
rowData[2] = printer.equals(defaultPrinter); // 是否为默认打印机
|
||||||
|
printerModel.addRow(rowData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新PrintSettingsPanel中的打印机下拉列表
|
||||||
|
updatePrinterComboBoxes(printers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新所有打印机下拉列表
|
||||||
|
*
|
||||||
|
* @param printers 打印机列表
|
||||||
|
*/
|
||||||
|
private void updatePrinterComboBoxes(List<String> printers) {
|
||||||
|
// 发布打印机列表更新事件
|
||||||
|
eventPublisher.publishEvent(new PrinterListUpdatedEvent(printers));
|
||||||
|
// 使用debug级别记录日志,减少info日志输出
|
||||||
|
log.debug("已发布打印机列表更新事件,共{}个打印机", printers.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印机列表更新事件
|
||||||
|
*/
|
||||||
|
public static class PrinterListUpdatedEvent {
|
||||||
|
private final List<String> printers;
|
||||||
|
|
||||||
|
public PrinterListUpdatedEvent(List<String> printers) {
|
||||||
|
this.printers = printers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPrinters() {
|
||||||
|
return printers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭资源
|
||||||
|
*/
|
||||||
|
public void shutdown() {
|
||||||
|
refreshExecutor.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,168 @@
|
|||||||
|
package com.goeing.printserver.main.service;
|
||||||
|
|
||||||
|
import com.goeing.printserver.main.domain.PrintTask;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import jakarta.annotation.PreDestroy;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印历史记录服务,用于保存和查询打印任务的历史记录
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class PrintHistoryService {
|
||||||
|
|
||||||
|
// 使用线程安全的列表存储历史记录
|
||||||
|
private final CopyOnWriteArrayList<PrintTask> historyTasks = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
// 最大历史记录数量
|
||||||
|
private static final int MAX_HISTORY_SIZE = 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加任务到历史记录
|
||||||
|
*
|
||||||
|
* @param task 打印任务
|
||||||
|
*/
|
||||||
|
public void addTaskToHistory(PrintTask task) {
|
||||||
|
// 创建任务的副本,避免引用原始对象
|
||||||
|
PrintTask historyCopy = createTaskCopy(task);
|
||||||
|
|
||||||
|
// 添加到历史记录
|
||||||
|
historyTasks.add(historyCopy);
|
||||||
|
log.debug("添加任务到历史记录: {}", historyCopy.getFileUrl());
|
||||||
|
|
||||||
|
// 如果历史记录超过最大数量,移除最旧的记录
|
||||||
|
if (historyTasks.size() > MAX_HISTORY_SIZE) {
|
||||||
|
historyTasks.remove(0);
|
||||||
|
log.debug("历史记录超过最大数量,移除最旧的记录");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建任务的副本
|
||||||
|
*
|
||||||
|
* @param original 原始任务
|
||||||
|
* @return 任务副本
|
||||||
|
*/
|
||||||
|
private PrintTask createTaskCopy(PrintTask original) {
|
||||||
|
PrintTask copy = new PrintTask();
|
||||||
|
copy.setFileUrl(original.getFileUrl());
|
||||||
|
copy.setPrinter(original.getPrinter());
|
||||||
|
copy.setStatus(original.getStatus());
|
||||||
|
copy.setQueuedTime(original.getQueuedTime());
|
||||||
|
copy.setStartTime(original.getStartTime());
|
||||||
|
copy.setEndTime(original.getEndTime());
|
||||||
|
copy.setPrintOptions(original.getPrintOptions());
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有历史记录
|
||||||
|
*
|
||||||
|
* @return 历史记录列表
|
||||||
|
*/
|
||||||
|
public List<PrintTask> getAllHistory() {
|
||||||
|
return Collections.unmodifiableList(new ArrayList<>(historyTasks));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据打印机名称查询历史记录
|
||||||
|
*
|
||||||
|
* @param printer 打印机名称
|
||||||
|
* @return 历史记录列表
|
||||||
|
*/
|
||||||
|
public List<PrintTask> getHistoryByPrinter(String printer) {
|
||||||
|
return historyTasks.stream()
|
||||||
|
.filter(task -> task.getPrinter().equals(printer))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据状态查询历史记录
|
||||||
|
*
|
||||||
|
* @param status 状态
|
||||||
|
* @return 历史记录列表
|
||||||
|
*/
|
||||||
|
public List<PrintTask> getHistoryByStatus(String status) {
|
||||||
|
return historyTasks.stream()
|
||||||
|
.filter(task -> task.getStatus().equals(status))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据时间范围查询历史记录
|
||||||
|
*
|
||||||
|
* @param startTime 开始时间
|
||||||
|
* @param endTime 结束时间
|
||||||
|
* @return 历史记录列表
|
||||||
|
*/
|
||||||
|
public List<PrintTask> getHistoryByTimeRange(LocalDateTime startTime, LocalDateTime endTime) {
|
||||||
|
return historyTasks.stream()
|
||||||
|
.filter(task -> {
|
||||||
|
LocalDateTime taskTime = task.getQueuedTime();
|
||||||
|
return taskTime != null && !taskTime.isBefore(startTime) && !taskTime.isAfter(endTime);
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文件URL查询历史记录
|
||||||
|
*
|
||||||
|
* @param fileUrl 文件URL
|
||||||
|
* @return 历史记录列表
|
||||||
|
*/
|
||||||
|
public List<PrintTask> getHistoryByFileUrl(String fileUrl) {
|
||||||
|
return historyTasks.stream()
|
||||||
|
.filter(task -> task.getFileUrl().contains(fileUrl))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空历史记录
|
||||||
|
*/
|
||||||
|
public void clearHistory() {
|
||||||
|
historyTasks.clear();
|
||||||
|
log.info("历史记录已清空");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取历史记录数量
|
||||||
|
*
|
||||||
|
* @return 历史记录数量
|
||||||
|
*/
|
||||||
|
public int getHistoryCount() {
|
||||||
|
return historyTasks.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最近的历史记录
|
||||||
|
*
|
||||||
|
* @param count 数量
|
||||||
|
* @return 历史记录列表
|
||||||
|
*/
|
||||||
|
public List<PrintTask> getRecentHistory(int count) {
|
||||||
|
int size = historyTasks.size();
|
||||||
|
if (size <= count) {
|
||||||
|
return Collections.unmodifiableList(new ArrayList<>(historyTasks));
|
||||||
|
} else {
|
||||||
|
return Collections.unmodifiableList(new ArrayList<>(historyTasks.subList(size - count, size)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在应用程序关闭时执行清理操作
|
||||||
|
*/
|
||||||
|
@PreDestroy
|
||||||
|
public void shutdown() {
|
||||||
|
log.info("正在关闭打印历史记录服务...");
|
||||||
|
// 可以在这里添加持久化历史记录的逻辑
|
||||||
|
log.info("打印历史记录服务已关闭");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,394 @@
|
|||||||
|
package com.goeing.printserver.main.service;
|
||||||
|
|
||||||
|
import com.goeing.printserver.main.domain.dto.WebSocketMessageDTO;
|
||||||
|
import com.goeing.printserver.main.domain.request.PrintRequest;
|
||||||
|
import com.goeing.printserver.main.gui.PrintNotificationService;
|
||||||
|
import com.goeing.printserver.main.gui.PrintStatisticsPanel;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import jakarta.websocket.Session;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import jakarta.annotation.PreDestroy;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印队列服务,用于管理打印任务的队列和执行
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class PrintQueueService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PrintService printService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PrintNotificationService notificationService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PrintStatisticsPanel statisticsPanel;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PrintHistoryService historyService;
|
||||||
|
|
||||||
|
// 默认最大队列大小
|
||||||
|
private static final int DEFAULT_MAX_QUEUE_SIZE = 10;
|
||||||
|
|
||||||
|
// 当前最大队列大小
|
||||||
|
private int maxQueueSize = DEFAULT_MAX_QUEUE_SIZE;
|
||||||
|
|
||||||
|
// 打印任务队列
|
||||||
|
private final BlockingQueue<PrintTask> printQueue = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
|
// 记录当前正在处理的任务
|
||||||
|
private PrintTask currentTask;
|
||||||
|
|
||||||
|
// 打印任务线程池,使用单线程确保任务按顺序执行
|
||||||
|
private final ThreadPoolExecutor printExecutor = new ThreadPoolExecutor(
|
||||||
|
1, // 核心线程数
|
||||||
|
1, // 最大线程数
|
||||||
|
0L, // 空闲线程存活时间
|
||||||
|
TimeUnit.MILLISECONDS,
|
||||||
|
new LinkedBlockingQueue<>() // 工作队列
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印任务内部类,封装打印请求和WebSocket会话
|
||||||
|
*/
|
||||||
|
private static class PrintTask {
|
||||||
|
private final PrintRequest printRequest;
|
||||||
|
private final WebSocketMessageDTO messageDTO;
|
||||||
|
private final Session session;
|
||||||
|
private final LocalDateTime queuedTime;
|
||||||
|
private LocalDateTime startTime;
|
||||||
|
private LocalDateTime endTime;
|
||||||
|
private String status; // queued, processing, completed, failed
|
||||||
|
|
||||||
|
public PrintTask(PrintRequest printRequest, WebSocketMessageDTO messageDTO, Session session) {
|
||||||
|
this.printRequest = printRequest;
|
||||||
|
this.messageDTO = messageDTO;
|
||||||
|
this.session = session;
|
||||||
|
this.queuedTime = LocalDateTime.now();
|
||||||
|
this.status = "queued";
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrintRequest getPrintRequest() {
|
||||||
|
return printRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebSocketMessageDTO getMessageDTO() {
|
||||||
|
return messageDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Session getSession() {
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getQueuedTime() {
|
||||||
|
return queuedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getStartTime() {
|
||||||
|
return startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStartTime(LocalDateTime startTime) {
|
||||||
|
this.startTime = startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getEndTime() {
|
||||||
|
return endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEndTime(LocalDateTime endTime) {
|
||||||
|
this.endTime = endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(String status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> toMap() {
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("fileUrl", printRequest.getFileUrl());
|
||||||
|
map.put("printerName", printRequest.getPrinterName());
|
||||||
|
map.put("status", status);
|
||||||
|
map.put("queuedTime", queuedTime);
|
||||||
|
if (startTime != null) {
|
||||||
|
map.put("startTime", startTime);
|
||||||
|
}
|
||||||
|
if (endTime != null) {
|
||||||
|
map.put("endTime", endTime);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数,启动打印任务处理线程
|
||||||
|
*/
|
||||||
|
public PrintQueueService() {
|
||||||
|
// 启动打印任务处理线程
|
||||||
|
startPrintTaskProcessor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加打印任务到队列
|
||||||
|
*
|
||||||
|
* @param printRequest 打印请求
|
||||||
|
* @param messageDTO WebSocket消息DTO
|
||||||
|
* @param session WebSocket会话
|
||||||
|
* @return 是否成功添加到队列,如果队列已满则返回false
|
||||||
|
*/
|
||||||
|
public boolean addPrintTask(PrintRequest printRequest, WebSocketMessageDTO messageDTO, Session session) {
|
||||||
|
// 检查队列是否已满
|
||||||
|
if (printQueue.size() >= maxQueueSize) {
|
||||||
|
log.warn("打印队列已满,无法添加新任务: {}, 当前队列长度: {}, 最大队列大小: {}",
|
||||||
|
printRequest.getFileUrl(), printQueue.size(), maxQueueSize);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintTask task = new PrintTask(printRequest, messageDTO, session);
|
||||||
|
printQueue.offer(task);
|
||||||
|
int queueSize = printQueue.size();
|
||||||
|
log.info("打印任务已添加到队列: {}, 当前队列长度: {}, 最大队列大小: {}",
|
||||||
|
printRequest.getFileUrl(), queueSize, maxQueueSize);
|
||||||
|
|
||||||
|
// 发送任务已加入队列的通知
|
||||||
|
notificationService.notifyTaskQueued(printRequest, queueSize);
|
||||||
|
statisticsPanel.incrementTotalTasks();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动打印任务处理线程
|
||||||
|
*/
|
||||||
|
private void startPrintTaskProcessor() {
|
||||||
|
printExecutor.execute(() -> {
|
||||||
|
while (!Thread.currentThread().isInterrupted()) {
|
||||||
|
try {
|
||||||
|
// 从队列中获取打印任务,如果队列为空则阻塞等待
|
||||||
|
PrintTask task = printQueue.take();
|
||||||
|
processPrintTask(task);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
log.error("打印任务处理线程被中断", e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("处理打印任务时发生错误", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
log.info("打印任务处理线程已启动");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理打印任务
|
||||||
|
*
|
||||||
|
* @param task 打印任务
|
||||||
|
*/
|
||||||
|
private void processPrintTask(PrintTask task) {
|
||||||
|
PrintRequest printRequest = task.getPrintRequest();
|
||||||
|
WebSocketMessageDTO messageDTO = task.getMessageDTO();
|
||||||
|
Session session = task.getSession();
|
||||||
|
|
||||||
|
// 更新任务状态
|
||||||
|
currentTask = task;
|
||||||
|
task.setStartTime(LocalDateTime.now());
|
||||||
|
task.setStatus("processing");
|
||||||
|
log.info("开始处理打印任务: {}", printRequest.getFileUrl());
|
||||||
|
|
||||||
|
// 发送任务开始处理的通知
|
||||||
|
notificationService.notifyTaskStarted(printRequest, printQueue.size());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 执行打印
|
||||||
|
printService.print(printRequest);
|
||||||
|
log.info("打印任务完成: {}", printRequest.getFileUrl());
|
||||||
|
|
||||||
|
// 更新任务状态
|
||||||
|
task.setEndTime(LocalDateTime.now());
|
||||||
|
task.setStatus("completed");
|
||||||
|
|
||||||
|
// 发送任务完成的通知
|
||||||
|
notificationService.notifyTaskCompleted(printRequest, printQueue.size());
|
||||||
|
statisticsPanel.incrementCompletedTasks();
|
||||||
|
historyService.addTaskToHistory(convertToHistoryTask(task));
|
||||||
|
|
||||||
|
// 发送成功响应
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
map.put("status", "success");
|
||||||
|
map.put("msg", "");
|
||||||
|
|
||||||
|
messageDTO.setType("RESPONSE");
|
||||||
|
messageDTO.setPayload(JSONUtil.toJsonStr(map));
|
||||||
|
session.getBasicRemote().sendText(JSONUtil.toJsonStr(messageDTO));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("打印失败: {}", printRequest.getFileUrl(), e);
|
||||||
|
|
||||||
|
// 更新任务状态
|
||||||
|
task.setEndTime(LocalDateTime.now());
|
||||||
|
task.setStatus("failed");
|
||||||
|
|
||||||
|
// 获取错误消息
|
||||||
|
String errorMsg = e.getMessage();
|
||||||
|
if (errorMsg == null) {
|
||||||
|
errorMsg = "未知错误";
|
||||||
|
} else {
|
||||||
|
// 转义JSON特殊字符
|
||||||
|
errorMsg = errorMsg.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送任务失败的通知
|
||||||
|
notificationService.notifyTaskFailed(printRequest, errorMsg, printQueue.size());
|
||||||
|
statisticsPanel.incrementFailedTasks();
|
||||||
|
historyService.addTaskToHistory(convertToHistoryTask(task));
|
||||||
|
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
map.put("status", "fail");
|
||||||
|
map.put("msg", errorMsg);
|
||||||
|
messageDTO.setType("RESPONSE");
|
||||||
|
messageDTO.setPayload(JSONUtil.toJsonStr(map));
|
||||||
|
|
||||||
|
try {
|
||||||
|
session.getBasicRemote().sendText(JSONUtil.toJsonStr(messageDTO));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
log.error("发送打印失败消息时发生错误", ex);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
currentTask = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前队列长度
|
||||||
|
*
|
||||||
|
* @return 队列长度
|
||||||
|
*/
|
||||||
|
public int getQueueSize() {
|
||||||
|
return printQueue.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前正在处理的任务信息
|
||||||
|
*
|
||||||
|
* @return 当前任务信息,如果没有则返回null
|
||||||
|
*/
|
||||||
|
public Map<String, Object> getCurrentTaskInfo() {
|
||||||
|
if (currentTask == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return currentTask.toMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前正在处理的任务
|
||||||
|
*
|
||||||
|
* @return 当前任务,如果没有则返回null
|
||||||
|
*/
|
||||||
|
public com.goeing.printserver.main.domain.PrintTask getCurrentTask() {
|
||||||
|
if (currentTask == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return convertToHistoryTask(currentTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取队列中所有任务的信息
|
||||||
|
*
|
||||||
|
* @return 任务信息列表
|
||||||
|
*/
|
||||||
|
public List<Map<String, Object>> getQueuedTasksInfo() {
|
||||||
|
List<Map<String, Object>> tasksInfo = new ArrayList<>();
|
||||||
|
for (PrintTask task : printQueue) {
|
||||||
|
tasksInfo.add(task.toMap());
|
||||||
|
}
|
||||||
|
return tasksInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取队列中所有任务
|
||||||
|
*
|
||||||
|
* @return 任务列表
|
||||||
|
*/
|
||||||
|
public List<com.goeing.printserver.main.domain.PrintTask> getQueuedTasks() {
|
||||||
|
List<com.goeing.printserver.main.domain.PrintTask> tasks = new ArrayList<>();
|
||||||
|
for (PrintTask task : printQueue) {
|
||||||
|
tasks.add(convertToHistoryTask(task));
|
||||||
|
}
|
||||||
|
return tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置最大队列大小
|
||||||
|
*
|
||||||
|
* @param maxQueueSize 最大队列大小
|
||||||
|
*/
|
||||||
|
public void setMaxQueueSize(int maxQueueSize) {
|
||||||
|
if (maxQueueSize < 1) {
|
||||||
|
log.warn("最大队列大小不能小于1,设置为默认值: {}", DEFAULT_MAX_QUEUE_SIZE);
|
||||||
|
this.maxQueueSize = DEFAULT_MAX_QUEUE_SIZE;
|
||||||
|
} else {
|
||||||
|
this.maxQueueSize = maxQueueSize;
|
||||||
|
log.info("最大队列大小已设置为: {}", maxQueueSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最大队列大小
|
||||||
|
*
|
||||||
|
* @return 最大队列大小
|
||||||
|
*/
|
||||||
|
public int getMaxQueueSize() {
|
||||||
|
return maxQueueSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭打印任务处理线程池
|
||||||
|
*/
|
||||||
|
@PreDestroy
|
||||||
|
public void shutdown() {
|
||||||
|
log.info("正在关闭打印队列服务...");
|
||||||
|
printExecutor.shutdown();
|
||||||
|
try {
|
||||||
|
if (!printExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||||
|
printExecutor.shutdownNow();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
printExecutor.shutdownNow();
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
log.info("打印任务处理线程池已关闭");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将内部PrintTask转换为域对象PrintTask
|
||||||
|
*
|
||||||
|
* @param task 内部PrintTask对象
|
||||||
|
* @return 域对象PrintTask
|
||||||
|
*/
|
||||||
|
private com.goeing.printserver.main.domain.PrintTask convertToHistoryTask(PrintTask task) {
|
||||||
|
com.goeing.printserver.main.domain.PrintTask historyTask = new com.goeing.printserver.main.domain.PrintTask();
|
||||||
|
historyTask.setFileUrl(task.getPrintRequest().getFileUrl());
|
||||||
|
historyTask.setPrinter(task.getPrintRequest().getPrinterName());
|
||||||
|
historyTask.setStatus(task.getStatus());
|
||||||
|
historyTask.setQueuedTime(task.getQueuedTime());
|
||||||
|
historyTask.setStartTime(task.getStartTime());
|
||||||
|
historyTask.setEndTime(task.getEndTime());
|
||||||
|
historyTask.setPrintOptions(task.getPrintRequest().getPrintOption());
|
||||||
|
return historyTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package com.goeing.printserver.main.service;
|
||||||
|
|
||||||
|
import com.goeing.printserver.main.domain.request.PrintRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印服务接口,定义打印相关操作
|
||||||
|
* 用于解决PrintController和PrintQueueService之间的循环依赖
|
||||||
|
*/
|
||||||
|
public interface PrintService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行打印操作
|
||||||
|
*
|
||||||
|
* @param request 打印请求
|
||||||
|
* @return 打印结果
|
||||||
|
*/
|
||||||
|
String print(PrintRequest request);
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ import com.alibaba.fastjson.JSON;
|
|||||||
import com.goeing.printserver.main.PrintController;
|
import com.goeing.printserver.main.PrintController;
|
||||||
import com.goeing.printserver.main.domain.dto.WebSocketMessageDTO;
|
import com.goeing.printserver.main.domain.dto.WebSocketMessageDTO;
|
||||||
import com.goeing.printserver.main.domain.request.PrintRequest;
|
import com.goeing.printserver.main.domain.request.PrintRequest;
|
||||||
|
import com.goeing.printserver.main.service.PrintQueueService;
|
||||||
import jakarta.websocket.*;
|
import jakarta.websocket.*;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@ -27,7 +28,8 @@ import java.util.stream.Collectors;
|
|||||||
@ClientEndpoint
|
@ClientEndpoint
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class PrinterClient implements ApplicationRunner {
|
public class PrinterClient implements ApplicationRunner {
|
||||||
|
private final PrintQueueService printQueueService;
|
||||||
private Session session;
|
private Session session;
|
||||||
@Value("${print.printer.id}")
|
@Value("${print.printer.id}")
|
||||||
private String printerId;
|
private String printerId;
|
||||||
@ -40,8 +42,9 @@ public class PrinterClient implements ApplicationRunner {
|
|||||||
private final ScheduledExecutorService heartbeatExecutor = Executors.newSingleThreadScheduledExecutor();
|
private final ScheduledExecutorService heartbeatExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||||
private boolean isConnecting = false;
|
private boolean isConnecting = false;
|
||||||
|
|
||||||
// 无参构造函数,由Spring管理
|
// 构造函数,注入PrintQueueService
|
||||||
public PrinterClient() {
|
public PrinterClient(PrintQueueService printQueueService) {
|
||||||
|
this.printQueueService = printQueueService;
|
||||||
// 构造函数不做连接操作,在run方法中进行连接
|
// 构造函数不做连接操作,在run方法中进行连接
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,47 +66,31 @@ public class PrinterClient implements ApplicationRunner {
|
|||||||
if ("print".equals(type)) {
|
if ("print".equals(type)) {
|
||||||
String payload = webSocketMessageDTO.getPayload();
|
String payload = webSocketMessageDTO.getPayload();
|
||||||
PrintRequest printRequest = JSONUtil.toBean(payload, PrintRequest.class);
|
PrintRequest printRequest = JSONUtil.toBean(payload, PrintRequest.class);
|
||||||
PrintController bean = SpringUtil.getBean(PrintController.class);
|
|
||||||
|
// 将打印任务添加到队列
|
||||||
|
log.info("收到打印任务: {}, 添加到打印队列", printRequest);
|
||||||
// 处理打印任务
|
printQueueService.addPrintTask(printRequest, webSocketMessageDTO, session);
|
||||||
log.info("收到打印任务: {}, ", printRequest);
|
|
||||||
|
// 发送任务已接收的确认消息
|
||||||
|
Map<String,String> map = new HashMap<>();
|
||||||
|
map.put("status", "queued");
|
||||||
|
map.put("msg", "打印任务已加入队列,等待处理");
|
||||||
|
map.put("queueSize", String.valueOf(printQueueService.getQueueSize()));
|
||||||
|
|
||||||
|
WebSocketMessageDTO queuedResponse = new WebSocketMessageDTO();
|
||||||
|
queuedResponse.setType("QUEUED");
|
||||||
|
queuedResponse.setRequestId(webSocketMessageDTO.getRequestId());
|
||||||
|
queuedResponse.setPayload(JSONUtil.toJsonStr(map));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bean.print(printRequest);
|
session.getBasicRemote().sendText(JSONUtil.toJsonStr(queuedResponse));
|
||||||
log.info("打印任务完成: {}", printRequest.getFileUrl());
|
} catch (IOException e) {
|
||||||
|
log.error("发送队列确认消息失败", e);
|
||||||
Map<String,String> map = new HashMap<>();
|
|
||||||
map.put("status", "success");
|
|
||||||
map.put("msg", "");
|
|
||||||
|
|
||||||
webSocketMessageDTO.setType("RESPONSE");
|
|
||||||
webSocketMessageDTO.setPayload(JSONUtil.toJsonStr(map));
|
|
||||||
session.getBasicRemote().sendText(JSONUtil.toJsonStr(webSocketMessageDTO));
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("打印失败: {}", printRequest.getFileUrl(), e);
|
|
||||||
// 发送打印失败消息
|
|
||||||
String errorMsg = e.getMessage();
|
|
||||||
if (errorMsg == null) {
|
|
||||||
errorMsg = "未知错误";
|
|
||||||
} else {
|
|
||||||
// 转义JSON特殊字符
|
|
||||||
errorMsg = errorMsg.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String,String> map = new HashMap<>();
|
|
||||||
map.put("status", "fail");
|
|
||||||
map.put("msg", errorMsg);
|
|
||||||
webSocketMessageDTO.setType("RESPONSE");
|
|
||||||
webSocketMessageDTO.setPayload(JSONUtil.toJsonStr(map));
|
|
||||||
|
|
||||||
session.getBasicRemote().sendText(JSONUtil.toJsonStr(webSocketMessageDTO));
|
|
||||||
}
|
}
|
||||||
} else if ("heartbeat_ack".equals(type)) {
|
} else if ("heartbeat_ack".equals(type)) {
|
||||||
// 心跳响应,可以记录最后一次心跳时间
|
// 心跳响应,可以记录最后一次心跳时间
|
||||||
log.debug("收到心跳响应");
|
log.debug("收到心跳响应");
|
||||||
}else if ("printerList".equals(type)) {
|
} else if ("printerList".equals(type)) {
|
||||||
|
|
||||||
PrintService[] printServices = PrinterJob.lookupPrintServices();
|
PrintService[] printServices = PrinterJob.lookupPrintServices();
|
||||||
Set<String> collect = Arrays.stream(printServices).map(PrintService::getName).collect(Collectors.toSet());
|
Set<String> collect = Arrays.stream(printServices).map(PrintService::getName).collect(Collectors.toSet());
|
||||||
|
|
||||||
@ -112,6 +99,26 @@ public class PrinterClient implements ApplicationRunner {
|
|||||||
webSocketMessageDTO.setType("RESPONSE");
|
webSocketMessageDTO.setType("RESPONSE");
|
||||||
webSocketMessageDTO.setPayload(JSONUtil.toJsonStr(collect1));
|
webSocketMessageDTO.setPayload(JSONUtil.toJsonStr(collect1));
|
||||||
session.getBasicRemote().sendText(JSONUtil.toJsonStr(webSocketMessageDTO));
|
session.getBasicRemote().sendText(JSONUtil.toJsonStr(webSocketMessageDTO));
|
||||||
|
} else if ("queueStatus".equals(type)) {
|
||||||
|
// 返回当前打印队列状态
|
||||||
|
Map<String, Object> status = new HashMap<>();
|
||||||
|
status.put("queueSize", printQueueService.getQueueSize());
|
||||||
|
status.put("timestamp", System.currentTimeMillis());
|
||||||
|
status.put("currentTask", printQueueService.getCurrentTaskInfo());
|
||||||
|
|
||||||
|
webSocketMessageDTO.setType("RESPONSE");
|
||||||
|
webSocketMessageDTO.setPayload(JSONUtil.toJsonStr(status));
|
||||||
|
session.getBasicRemote().sendText(JSONUtil.toJsonStr(webSocketMessageDTO));
|
||||||
|
} else if ("queueTasks".equals(type)) {
|
||||||
|
// 返回打印队列中的所有任务
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("currentTask", printQueueService.getCurrentTaskInfo());
|
||||||
|
result.put("queuedTasks", printQueueService.getQueuedTasksInfo());
|
||||||
|
result.put("timestamp", System.currentTimeMillis());
|
||||||
|
|
||||||
|
webSocketMessageDTO.setType("RESPONSE");
|
||||||
|
webSocketMessageDTO.setPayload(JSONUtil.toJsonStr(result));
|
||||||
|
session.getBasicRemote().sendText(JSONUtil.toJsonStr(webSocketMessageDTO));
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import org.apache.pdfbox.printing.Orientation;
|
|||||||
import org.apache.pdfbox.printing.PDFPrintable;
|
import org.apache.pdfbox.printing.PDFPrintable;
|
||||||
import org.apache.pdfbox.printing.Scaling;
|
import org.apache.pdfbox.printing.Scaling;
|
||||||
|
|
||||||
import javax.print.PrintService;
|
// 使用完全限定名称避免与自定义PrintService接口冲突
|
||||||
import javax.print.PrintServiceLookup;
|
import javax.print.PrintServiceLookup;
|
||||||
import javax.print.attribute.HashPrintRequestAttributeSet;
|
import javax.print.attribute.HashPrintRequestAttributeSet;
|
||||||
import javax.print.attribute.PrintRequestAttributeSet;
|
import javax.print.attribute.PrintRequestAttributeSet;
|
||||||
@ -125,12 +125,12 @@ public class PdfPrinter {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
|
javax.print.PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
|
||||||
if (services == null || services.length == 0) {
|
if (services == null || services.length == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PrintService service : services) {
|
for (javax.print.PrintService service : services) {
|
||||||
if (service.getName().equalsIgnoreCase(printerName)) {
|
if (service.getName().equalsIgnoreCase(printerName)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -151,13 +151,13 @@ public class PdfPrinter {
|
|||||||
throw new IllegalArgumentException("Printer name cannot be null or empty");
|
throw new IllegalArgumentException("Printer name cannot be null or empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
|
javax.print.PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
|
||||||
|
|
||||||
if (services == null || services.length == 0) {
|
if (services == null || services.length == 0) {
|
||||||
throw new RuntimeException("No working printers found");
|
throw new RuntimeException("No working printers found");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PrintService service : services) {
|
for (javax.print.PrintService service : services) {
|
||||||
if (service.getName().equalsIgnoreCase(printerName)) {
|
if (service.getName().equalsIgnoreCase(printerName)) {
|
||||||
PrinterJob job = PrinterJob.getPrinterJob();
|
PrinterJob job = PrinterJob.getPrinterJob();
|
||||||
job.setPrintService(service);
|
job.setPrintService(service);
|
||||||
|
|||||||
@ -5,3 +5,15 @@ print.websocket.url=ws://127.0.0.1:8080/print-websocket
|
|||||||
print.printer.id=123456
|
print.printer.id=123456
|
||||||
print.websocket.apiKey=519883ab-3677-ce4b-59ba-7263870d0a26
|
print.websocket.apiKey=519883ab-3677-ce4b-59ba-7263870d0a26
|
||||||
|
|
||||||
|
# 临时允许循环依赖,后续应该通过重构完全消除
|
||||||
|
spring.main.allow-circular-references=true
|
||||||
|
|
||||||
|
# 在macOS系统上,如果遇到HeadlessException,可以设置为true强制使用无头模式
|
||||||
|
# 或者在启动时添加JVM参数:-Djava.awt.headless=true
|
||||||
|
app.force.headless=false
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
# 设置打印机状态面板和设置面板的日志级别为WARN,减少日志输出
|
||||||
|
logging.level.com.goeing.printserver.main.gui.PrinterStatusPanel=WARN
|
||||||
|
logging.level.com.goeing.printserver.main.gui.PrintSettingsPanel=WARN
|
||||||
|
|
||||||
|
|||||||
89
start-printserver.sh
Executable file
89
start-printserver.sh
Executable file
@ -0,0 +1,89 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 打印服务器启动脚本
|
||||||
|
# 此脚本提供了多种启动模式,适应不同的环境需求
|
||||||
|
|
||||||
|
# 默认配置
|
||||||
|
JAR_FILE="target/goeingPrintServer.jar"
|
||||||
|
JAVA_OPTS=""
|
||||||
|
HEADLESS_MODE=false
|
||||||
|
DEBUG_MODE=false
|
||||||
|
MEMORY="512m"
|
||||||
|
|
||||||
|
# 显示帮助信息
|
||||||
|
show_help() {
|
||||||
|
echo "打印服务器启动脚本"
|
||||||
|
echo "用法: $0 [选项]"
|
||||||
|
echo ""
|
||||||
|
echo "选项:"
|
||||||
|
echo " -h, --help 显示此帮助信息"
|
||||||
|
echo " -j, --jar FILE 指定JAR文件路径 (默认: $JAR_FILE)"
|
||||||
|
echo " --headless 以无头模式运行 (无图形界面)"
|
||||||
|
echo " --debug 启用远程调试 (端口: 5005)"
|
||||||
|
echo " -m, --memory SIZE 设置最大内存 (默认: $MEMORY)"
|
||||||
|
echo ""
|
||||||
|
echo "示例:"
|
||||||
|
echo " $0 --headless 以无头模式启动服务器"
|
||||||
|
echo " $0 --memory 1g 设置最大内存为1GB"
|
||||||
|
echo " $0 --jar custom.jar 使用自定义JAR文件"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# 解析命令行参数
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-h|--help)
|
||||||
|
show_help
|
||||||
|
;;
|
||||||
|
-j|--jar)
|
||||||
|
JAR_FILE="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--headless)
|
||||||
|
HEADLESS_MODE=true
|
||||||
|
;;
|
||||||
|
--debug)
|
||||||
|
DEBUG_MODE=true
|
||||||
|
;;
|
||||||
|
-m|--memory)
|
||||||
|
MEMORY="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "未知选项: $1"
|
||||||
|
echo "使用 --help 查看帮助信息"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
# 检查JAR文件是否存在
|
||||||
|
if [ ! -f "$JAR_FILE" ]; then
|
||||||
|
echo "错误: JAR文件 '$JAR_FILE' 不存在"
|
||||||
|
echo "请先构建项目或使用 --jar 选项指定正确的JAR文件路径"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 构建Java选项
|
||||||
|
JAVA_OPTS="$JAVA_OPTS -Xmx$MEMORY"
|
||||||
|
|
||||||
|
# 添加无头模式选项
|
||||||
|
if [ "$HEADLESS_MODE" = true ]; then
|
||||||
|
JAVA_OPTS="$JAVA_OPTS -Djava.awt.headless=true"
|
||||||
|
echo "启用无头模式 (无图形界面)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 添加调试选项
|
||||||
|
if [ "$DEBUG_MODE" = true ]; then
|
||||||
|
JAVA_OPTS="$JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"
|
||||||
|
echo "启用远程调试模式 (端口: 5005)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 启动应用程序
|
||||||
|
echo "正在启动打印服务器..."
|
||||||
|
echo "使用JAR文件: $JAR_FILE"
|
||||||
|
echo "Java选项: $JAVA_OPTS"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
java $JAVA_OPTS -jar "$JAR_FILE"
|
||||||
Loading…
Reference in New Issue
Block a user