mirror of
https://github.com/tiennm99/BSK-All-in-One-Clinic-Management-System.git
synced 2026-10-14 22:17:01 +00:00
[update] Add PDF upload functionality to CheckUpPage and server. Implement asynchronous PDF export and upload processes, enhancing patient checkup management. Integrate Google Drive upload capabilities for saved PDFs, ensuring seamless document handling and storage.
This commit is contained in:
1 parent
dc2ad15ae1
commit
6d0820a4e4
22 files changed
+269
-3
No files matched your search
@@ -95,6 +95,7 @@ import org.bytedeco.opencv.opencv_core.IplImage;
|
||||
import static org.bytedeco.opencv.global.opencv_core.cvFlip;
|
||||
|
||||
// Java imports for concurrent tasks
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
@@ -134,11 +135,13 @@ import BsK.common.packet.req.SaveCheckupRequest;
|
||||
import BsK.common.packet.req.SearchAllRole;
|
||||
import BsK.common.packet.req.SearchAllUser;
|
||||
import BsK.common.packet.req.UploadCheckupImageRequest;
|
||||
import BsK.common.packet.req.UploadCheckupPdfRequest;
|
||||
import BsK.common.packet.res.AddCheckupResponse;
|
||||
import BsK.common.packet.res.AddPatientResponse;
|
||||
import BsK.common.packet.res.RegisterSuccessResponse;
|
||||
import BsK.common.packet.res.SaveCheckupRes;
|
||||
import BsK.common.packet.res.UploadCheckupImageResponse;
|
||||
import BsK.common.packet.res.UploadCheckupPdfResponse;
|
||||
import BsK.common.util.date.DateUtils;
|
||||
import java.awt.event.FocusAdapter;
|
||||
import java.awt.event.FocusEvent;
|
||||
@@ -161,6 +164,7 @@ public class CheckUpPage extends JPanel {
|
||||
private final ResponseListener<GetOrderInfoByCheckupRes> orderInfoByCheckupListener = this::handleGetOrderInfoByCheckupResponse;
|
||||
private final ResponseListener<GetAllTemplatesRes> getAllTemplatesListener = this::handleGetAllTemplatesResponse;
|
||||
private final ResponseListener<UploadCheckupImageResponse> uploadImageResponseListener = this::handleUploadImageResponse;
|
||||
private final ResponseListener<UploadCheckupPdfResponse> uploadPdfResponseListener = this::handleUploadPdfResponse;
|
||||
private JTextField checkupIdField, customerLastNameField, customerFirstNameField,customerAddressField, customerPhoneField, customerIdField, customerCccdDdcnField;
|
||||
private JTextArea suggestionField, diagnosisField, conclusionField; // Changed symptomsField to suggestionField
|
||||
private JTextPane notesField;
|
||||
@@ -346,6 +350,7 @@ public class CheckUpPage extends JPanel {
|
||||
ClientHandler.addResponseListener(CallPatientResponse.class, callPatientResponseListener);
|
||||
ClientHandler.addResponseListener(GetAllTemplatesRes.class, getAllTemplatesListener);
|
||||
ClientHandler.addResponseListener(UploadCheckupImageResponse.class, uploadImageResponseListener);
|
||||
ClientHandler.addResponseListener(UploadCheckupPdfResponse.class, uploadPdfResponseListener);
|
||||
|
||||
// Instantiate the new queue page but don't show it yet
|
||||
queueManagementPage = new QueueManagementPage();
|
||||
@@ -1684,9 +1689,10 @@ public class CheckUpPage extends JPanel {
|
||||
}
|
||||
|
||||
// Step 3: If report creation is successful, commit by saving and printing.
|
||||
if ("ĐANG KHÁM".equals(statusComboBox.getSelectedItem())) {
|
||||
statusComboBox.setSelectedItem("ĐÃ KHÁM");
|
||||
}
|
||||
// This will be turn on later
|
||||
// if ("ĐANG KHÁM".equals(statusComboBox.getSelectedItem())) {
|
||||
// statusComboBox.setSelectedItem("ĐÃ KHÁM");
|
||||
// }
|
||||
String statusToSavePrint = (String) statusComboBox.getSelectedItem();
|
||||
handleSave();
|
||||
afterSaveActions(statusToSavePrint, "Đã lưu. Đang gửi lệnh in...");
|
||||
@@ -1698,6 +1704,9 @@ public class CheckUpPage extends JPanel {
|
||||
log.error("Error showing print dialog", e);
|
||||
JOptionPane.showMessageDialog(this, "Không thể hiển thị hộp thoại in: " + e.getMessage(), "Lỗi In", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
// Step 5: Upload PDF to backend after successful print dialog
|
||||
uploadPdfToBackend(ultrasoundResultPrint, "ultrasound_result");
|
||||
break;
|
||||
case "loupe": // This case now handles "Lưu & Xem"
|
||||
// Step 1: Validate conditions first.
|
||||
@@ -1761,6 +1770,9 @@ public class CheckUpPage extends JPanel {
|
||||
|
||||
// Step 4: Finally, show the viewer with the pre-made object.
|
||||
JasperViewer.viewReport(jasperPrintToView, false);
|
||||
|
||||
// Step 5: Upload PDF to backend after successful view
|
||||
uploadPdfToBackend(ultrasoundResultView, "ultrasound_result");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -3277,6 +3289,67 @@ public class CheckUpPage extends JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUploadPdfResponse(UploadCheckupPdfResponse response) {
|
||||
if (response.isSuccess()) {
|
||||
log.info("Successfully uploaded PDF: {} - {}", response.getPdfType(), response.getMessage());
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
callingStatusLabel.setText("Đã lưu PDF thành công: " + response.getPdfType());
|
||||
callingStatusLabel.setForeground(new Color(0, 100, 0));
|
||||
});
|
||||
} else {
|
||||
log.error("Failed to upload PDF: {} - {}", response.getPdfType(), response.getMessage());
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Lỗi khi tải PDF lên server: " + response.getMessage(),
|
||||
"Lỗi Tải PDF",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads PDF to backend asynchronously
|
||||
* @param ultrasoundResult The UltrasoundResult object to export as PDF
|
||||
* @param pdfType The type of PDF ("ultrasound_result" or "medserinvoice")
|
||||
*/
|
||||
private void uploadPdfToBackend(UltrasoundResult ultrasoundResult, String pdfType) {
|
||||
if (currentCheckupIdForMedia == null) {
|
||||
log.warn("No current checkup ID for PDF upload");
|
||||
return;
|
||||
}
|
||||
|
||||
// Run PDF export and upload in background thread to avoid blocking UI
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
// Export PDF as bytes
|
||||
byte[] pdfBytes = ultrasoundResult.exportToPdfBytes();
|
||||
|
||||
// Create filename with override behavior (same name = override)
|
||||
String fileName = pdfType + ".pdf";
|
||||
|
||||
// Create and send upload request
|
||||
UploadCheckupPdfRequest request = new UploadCheckupPdfRequest(
|
||||
currentCheckupIdForMedia,
|
||||
pdfBytes,
|
||||
fileName,
|
||||
pdfType
|
||||
);
|
||||
|
||||
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request);
|
||||
log.info("Sent PDF upload request for checkup: {}, type: {}", currentCheckupIdForMedia, pdfType);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to export or upload PDF for checkup: {}", currentCheckupIdForMedia, e);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Lỗi khi tạo hoặc tải PDF: " + e.getMessage(),
|
||||
"Lỗi PDF",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleRecordVideo() {
|
||||
if (currentCheckupMediaPath == null) {
|
||||
JOptionPane.showMessageDialog(this, "Vui lòng chọn một lượt khám để lưu video.", "Chưa chọn lượt khám", JOptionPane.WARNING_MESSAGE);
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.apache.pdfbox.rendering.PDFRenderer;
|
||||
// --- JasperReports Imports ---
|
||||
import net.sf.jasperreports.engine.JREmptyDataSource;
|
||||
import net.sf.jasperreports.engine.JasperCompileManager;
|
||||
import net.sf.jasperreports.engine.JasperExportManager;
|
||||
import net.sf.jasperreports.engine.JasperFillManager;
|
||||
import net.sf.jasperreports.engine.JasperPrint;
|
||||
import net.sf.jasperreports.engine.JasperReport;
|
||||
@@ -202,6 +203,20 @@ public class UltrasoundResult {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the ultrasound result as PDF bytes for backend upload
|
||||
* @return byte array of the PDF file
|
||||
* @throws JRException if there's an error during PDF export
|
||||
* @throws IOException if there's an error during file operations
|
||||
*/
|
||||
public byte[] exportToPdfBytes() throws JRException, IOException {
|
||||
// First create the JasperPrint object
|
||||
JasperPrint jasperPrint = createJasperPrint();
|
||||
|
||||
// Export to PDF bytes
|
||||
return JasperExportManager.exportReportToPdf(jasperPrint);
|
||||
}
|
||||
|
||||
public void createDialog(JFrame parent) {
|
||||
dialog = new JDialog(parent, "Kết quả siêu âm", true);
|
||||
dialog.setSize(800, 700);
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package BsK.common.packet.req;
|
||||
|
||||
import BsK.common.packet.Packet;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UploadCheckupPdfRequest implements Packet {
|
||||
private String checkupId;
|
||||
private byte[] pdfData;
|
||||
private String fileName;
|
||||
private String pdfType; // "ultrasound_result" or "medserinvoice" (for future use)
|
||||
|
||||
public UploadCheckupPdfRequest(String checkupId, byte[] pdfData, String fileName, String pdfType) {
|
||||
this.checkupId = checkupId;
|
||||
this.pdfData = pdfData;
|
||||
this.fileName = fileName;
|
||||
this.pdfType = pdfType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package BsK.common.packet.res;
|
||||
|
||||
import BsK.common.packet.Packet;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UploadCheckupPdfResponse implements Packet {
|
||||
private boolean success;
|
||||
private String message;
|
||||
private String fileName;
|
||||
private String pdfType;
|
||||
|
||||
public UploadCheckupPdfResponse(boolean success, String message, String fileName, String pdfType) {
|
||||
this.success = success;
|
||||
this.message = message;
|
||||
this.fileName = fileName;
|
||||
this.pdfType = pdfType;
|
||||
}
|
||||
}
|
||||
@@ -1260,6 +1260,44 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
|
||||
}
|
||||
}
|
||||
|
||||
if (packet instanceof UploadCheckupPdfRequest uploadCheckupPdfRequest) {
|
||||
log.info("Received UploadCheckupPdfRequest for checkupId: {}, type: {}", uploadCheckupPdfRequest.getCheckupId(), uploadCheckupPdfRequest.getPdfType());
|
||||
|
||||
String checkupId = uploadCheckupPdfRequest.getCheckupId();
|
||||
String fileName = uploadCheckupPdfRequest.getFileName();
|
||||
String pdfType = uploadCheckupPdfRequest.getPdfType();
|
||||
byte[] pdfData = uploadCheckupPdfRequest.getPdfData();
|
||||
|
||||
if (checkupId == null || checkupId.trim().isEmpty()) {
|
||||
UserUtil.sendPacket(currentUser.getSessionId(), new UploadCheckupPdfResponse(false, "CheckupID is null or empty. Cannot save PDF.", fileName, pdfType));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. Define and create storage directory (use same img_db structure)
|
||||
Path storageDir = Paths.get(IMAGE_DB_PATH, checkupId.trim());
|
||||
Files.createDirectories(storageDir); // Create dirs if they don't exist
|
||||
|
||||
// 2. Save the PDF file (with override behavior - same name = override)
|
||||
Path filePath = storageDir.resolve(fileName);
|
||||
try (FileOutputStream fos = new FileOutputStream(filePath.toFile())) {
|
||||
fos.write(pdfData);
|
||||
}
|
||||
String savedPath = filePath.toString().replace("\\", "/");
|
||||
log.info("Successfully saved PDF to {} (override mode)", savedPath);
|
||||
|
||||
// 3. Send immediate success response to client
|
||||
UserUtil.sendPacket(currentUser.getSessionId(), new UploadCheckupPdfResponse(true, "PDF uploaded successfully to " + savedPath, fileName, pdfType));
|
||||
|
||||
// 4. Upload to Google Drive asynchronously (don't block the response)
|
||||
uploadCheckupPdfToGoogleDriveAsync(checkupId, fileName, pdfData, pdfType);
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to save uploaded PDF for checkupId {}: {}", checkupId, e.getMessage());
|
||||
UserUtil.sendPacket(currentUser.getSessionId(), new UploadCheckupPdfResponse(false, "Server failed to save PDF: " + e.getMessage(), fileName, pdfType));
|
||||
}
|
||||
}
|
||||
|
||||
if (packet instanceof GetImagesByCheckupIdReq getImagesByCheckupIdReq) {
|
||||
String checkupId = getImagesByCheckupIdReq.getCheckupId();
|
||||
log.info("Received GetImagesByCheckupIdReq for checkupId: {}", checkupId);
|
||||
@@ -1623,4 +1661,82 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
|
||||
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a checkup PDF to Google Drive asynchronously using the checkup's folder ID.
|
||||
* This method runs in a background thread to avoid blocking the PDF upload response.
|
||||
* @param checkupId The checkup ID
|
||||
* @param fileName The PDF filename (e.g., "ultrasound_result.pdf")
|
||||
* @param pdfData The PDF file as byte array
|
||||
* @param pdfType The type of PDF ("ultrasound_result" or "medserinvoice")
|
||||
*/
|
||||
private void uploadCheckupPdfToGoogleDriveAsync(String checkupId, String fileName, byte[] pdfData, String pdfType) {
|
||||
// Run in background thread to avoid blocking
|
||||
new Thread(() -> {
|
||||
try {
|
||||
// Check if Google Drive is connected
|
||||
if (!Server.isGoogleDriveConnected()) {
|
||||
log.info("Google Drive not connected - skipping PDF upload for checkup {}", checkupId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get checkup's Google Drive folder ID from database
|
||||
PreparedStatement checkupStmt = Server.connection.prepareStatement(
|
||||
"SELECT drive_folder_id FROM Checkup WHERE checkup_id = ?"
|
||||
);
|
||||
checkupStmt.setString(1, checkupId);
|
||||
ResultSet checkupRs = checkupStmt.executeQuery();
|
||||
|
||||
String checkupDriveFolderId = "";
|
||||
if (checkupRs.next()) {
|
||||
checkupDriveFolderId = checkupRs.getString("drive_folder_id");
|
||||
}
|
||||
checkupStmt.close();
|
||||
checkupRs.close();
|
||||
|
||||
if (checkupDriveFolderId == null || checkupDriveFolderId.trim().isEmpty()) {
|
||||
log.warn("Checkup {} has no Google Drive folder ID - cannot upload PDF", checkupId);
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("Uploading PDF {} ({}) to Google Drive for checkup {}", fileName, pdfType, checkupId);
|
||||
|
||||
// Create temporary file from byte array for Google Drive upload
|
||||
java.io.File tempFile = createTempFileFromBytes(pdfData, fileName);
|
||||
|
||||
try {
|
||||
// Upload file to Google Drive using checkup's folder ID (with override behavior)
|
||||
String uploadedFileId = Server.getGoogleDriveService().uploadFileToFolder(
|
||||
checkupDriveFolderId, tempFile, fileName
|
||||
);
|
||||
|
||||
log.info("PDF uploaded successfully to Google Drive for checkup {}: FileID={}", checkupId, uploadedFileId);
|
||||
|
||||
// Notify dashboard
|
||||
if (ServerDashboard.getInstance() != null) {
|
||||
ServerDashboard.getInstance().addLog(
|
||||
String.format("Uploaded PDF %s (%s) to Google Drive for checkup %s", fileName, pdfType, checkupId)
|
||||
);
|
||||
}
|
||||
|
||||
} finally {
|
||||
// Clean up temporary file
|
||||
if (tempFile.exists()) {
|
||||
tempFile.delete();
|
||||
log.debug("Cleaned up temporary PDF file: {}", tempFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to upload PDF to Google Drive for checkup {}: {}", checkupId, e.getMessage(), e);
|
||||
|
||||
// Notify dashboard of error
|
||||
if (ServerDashboard.getInstance() != null) {
|
||||
ServerDashboard.getInstance().addLog(
|
||||
String.format("Failed to upload PDF %s (%s) to Google Drive for checkup %s: %s", fileName, pdfType, checkupId, e.getMessage())
|
||||
);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<fontFamilies>
|
||||
<fontFamily name="SVN-Arial">
|
||||
<normal>fonts/SVN-Arial Regular.ttf</normal>
|
||||
<bold>fonts/SVN-Arial Bold.ttf</bold>
|
||||
<italic>fonts/SVN-Arial Regular.ttf</italic>
|
||||
<boldItalic>fonts/SVN-Arial Bold.ttf</boldItalic>
|
||||
<pdfEncoding>Identity-H</pdfEncoding>
|
||||
<pdfEmbedded>true</pdfEmbedded>
|
||||
</fontFamily>
|
||||
</fontFamilies>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<fontFamilies>
|
||||
<fontFamily name="Times New Roman">
|
||||
<normal>fonts/TIMES.TTF</normal>
|
||||
<bold>fonts/TIMESBD.TTF</bold>
|
||||
<italic>fonts/TIMESI.TTF</italic>
|
||||
<boldItalic>fonts/TIMESBI.TTF</boldItalic>
|
||||
<pdfEncoding>Identity-H</pdfEncoding>
|
||||
<pdfEmbedded>true</pdfEmbedded>
|
||||
</fontFamily>
|
||||
</fontFamilies>
|
||||
@@ -0,0 +1,2 @@
|
||||
net.sf.jasperreports.extension.registry.factory.fonts=net.sf.jasperreports.engine.fonts.SimpleFontExtensionsRegistryFactory
|
||||
net.sf.jasperreports.extension.simple.font.families.timesnewroman=fonts/times-new-roman-fonts.xml
|
||||
Reference in new issue
Block a user