[update] Enhance Client and Server functionality by increasing HTTP object aggregator limits for improved data handling. Refactor CheckUpPage and HistoryViewDialog to support additional patient history details, including diagnosis and conclusion, and implement image retrieval for checkups, enhancing user experience and data management.

This commit is contained in:
Lê Thành Đạt committed 2025-07-04 00:34:46 +07:00
1 parent 790e8aada9
commit 75aa04d77f
9 files changed
+356 -189

No files matched your search

+2 -2
View File
@@ -142,7 +142,7 @@ public class Client {
.addLast(new IdleStateHandler(60 * 30,
0, 0, TimeUnit.SECONDS))
.addLast(new HttpClientCodec())
.addLast(new HttpObjectAggregator(8192))
.addLast(new HttpObjectAggregator(50 * 1024 * 1024))
.addLast(
new WebSocketClientProtocolHandler(
finalUri,
@@ -150,7 +150,7 @@ public class Client {
null,
true,
new DefaultHttpHeaders(),
100000))
50 * 1024 * 1024))
.addLast("ws", ClientHandler.INSTANCE);
}
});
@@ -60,16 +60,12 @@ public class ClientHandler extends SimpleChannelInboundHandler<TextWebSocketFram
if (packet != null) {
// Handle HandshakeCompleteResponse to initialize ctx and frame
if (packet instanceof HandshakeCompleteResponse handshakeResponse) {
if (packet instanceof HandshakeCompleteResponse) {
log.info("Handshake complete");
ClientHandler.ctx = ctx;
ClientHandler.frame = frame;
// If
UIHandler.INSTANCE.showUI();
// When the handshake is complete, the UI is shown
return; // No further processing needed for handshake response
return;
}
if (packet instanceof ClinicInfoResponse clinicInfoResponse) {
@@ -93,12 +89,7 @@ public class ClientHandler extends SimpleChannelInboundHandler<TextWebSocketFram
return;
}
listeners.forEach((key, value) -> {
log.debug("Key: {}", key.getName());
});
// Process other packets
// Dispatch to registered listeners
List<ResponseListener<?>> responseListeners = listeners.get(packet.getClass());
if (responseListeners != null && !responseListeners.isEmpty()) {
for (ResponseListener<?> listener : new ArrayList<>(responseListeners)) {
@@ -1100,18 +1100,24 @@ public class CheckUpPage extends JPanel {
historyTable.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 1) {
if (e.getClickCount() == 1) { // Single click is enough
int selectedRow = historyTable.getSelectedRow();
if (selectedRow >= 0 && selectedRow < history.length) {
// Get selected history data
// Check if the row index is valid for our data array
if (selectedRow >= 0 && history != null && selectedRow < history.length) {
String[] selectedHistory = history[selectedRow];
if (selectedHistory.length > 1) {
String checkupId = selectedHistory[1]; // Corrected: Index 1 is the checkup ID
// Ensure the selected record has enough data fields
if (selectedHistory != null && selectedHistory.length >= 8) {
String checkupId = selectedHistory[1];
String patientName = customerFirstNameField.getText() + " " + customerLastNameField.getText();
String checkupDate = selectedHistory[0]; // Corrected: Index 0 is the date
String checkupDate = selectedHistory[0];
String suggestion = selectedHistory[2];
String diagnosis = selectedHistory[3];
String notes = selectedHistory[5];
String conclusion = selectedHistory[7];
// Open history view dialog
openHistoryViewDialog(checkupId, patientName, checkupDate);
openHistoryViewDialog(checkupId, patientName, checkupDate, suggestion, diagnosis, conclusion, notes);
} else {
log.warn("Selected history record has incomplete data. Length: {}", (selectedHistory != null ? selectedHistory.length : "null"));
}
}
}
@@ -1703,15 +1709,16 @@ public class CheckUpPage extends JPanel {
String[][] tableData = new String[this.history.length][5];
for (int i = 0; i < this.history.length; i++) {
if (this.history[i] != null && this.history[i].length >= 6) {
tableData[i][0] = this.history[i][0]; // "Ngày Tháng" (Date)
tableData[i][1] = this.history[i][1]; // "Mã khám bệnh" (ID)
tableData[i][2] = this.history[i][2]; // "Triệu chứng" (Suggestion)
tableData[i][3] = this.history[i][3]; // "Chẩn đoán" (Diagnosis)
tableData[i][4] = this.history[i][5]; // "Ghi chú" (Notes) - was incorrectly showing prescription_id
// 0: checkup_date, 1: checkup_id, 2: suggestion, 3: diagnosis, 4: prescription_id, 5: notes, 6: checkup_type, 7: conclusion
// display 0, 1, 6, 7
tableData[i][0] = this.history[i][0];
tableData[i][1] = this.history[i][1];
tableData[i][2] = this.history[i][6];
tableData[i][3] = this.history[i][7];
}
}
historyModel.setDataVector(tableData, new String[]{"Ngày Tháng","Mã khám bệnh", "Triệu chứng", "Chẩn đoán", "Ghi chú"});
historyModel.setDataVector(tableData, new String[]{"Ngày Tháng","Mã khám bệnh", "Loại khám", "Kết luận"});
});
}
@@ -2579,6 +2586,15 @@ public class CheckUpPage extends JPanel {
JLayeredPane layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new Dimension(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT));
layeredPane.setBorder(BorderFactory.createLineBorder(Color.GRAY));
layeredPane.setCursor(new Cursor(Cursor.HAND_CURSOR)); // Set the hand cursor
// Add the click listener directly to the image label
imageLabel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
showFullImageDialog(imageFile);
}
});
// The label will fill the entire pane
imageLabel.setBounds(0, 0, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT);
@@ -2622,6 +2638,50 @@ public class CheckUpPage extends JPanel {
imageGalleryPanel.repaint();
}
private void showFullImageDialog(File imageFile) {
try {
BufferedImage fullImage = ImageIO.read(imageFile);
if (fullImage == null) {
JOptionPane.showMessageDialog(this, "Không thể tải hình ảnh.", "Lỗi", JOptionPane.ERROR_MESSAGE);
return;
}
// Create a dialog to show the image
JDialog imageDialog = new JDialog(mainFrame, "Xem ảnh - " + imageFile.getName(), true);
imageDialog.setLayout(new BorderLayout());
// Scale image to fit screen dimensions
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int maxWidth = (int) (screenSize.width * 0.9);
int maxHeight = (int) (screenSize.height * 0.9);
int imgWidth = fullImage.getWidth();
int imgHeight = fullImage.getHeight();
double scale = Math.min(1.0, Math.min((double) maxWidth / imgWidth, (double) maxHeight / imgHeight));
int scaledWidth = (int) (imgWidth * scale);
int scaledHeight = (int) (imgHeight * scale);
Image scaledImage = fullImage.getScaledInstance(scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
JLabel imageLabel = new JLabel(new ImageIcon(scaledImage));
// Add label to a scroll pane in case it's still large
JScrollPane scrollPane = new JScrollPane(imageLabel);
scrollPane.setBorder(BorderFactory.createEmptyBorder());
imageDialog.add(scrollPane, BorderLayout.CENTER);
// Set dialog size and position
imageDialog.setSize(scaledWidth + 20, scaledHeight + 45); // Add padding for borders and title bar
imageDialog.setLocationRelativeTo(this);
imageDialog.setVisible(true);
} catch (IOException e) {
log.error("Error opening full image view for {}", imageFile.getAbsolutePath(), e);
JOptionPane.showMessageDialog(this, "Lỗi khi mở ảnh: " + e.getMessage(), "Lỗi", JOptionPane.ERROR_MESSAGE);
}
}
private void ensureMediaDirectoryExists(String checkupId) {
try {
// First ensure base directory exists
@@ -3044,9 +3104,10 @@ public class CheckUpPage extends JPanel {
/**
* Opens the history view dialog for the selected checkup
*/
private void openHistoryViewDialog(String checkupId, String patientName, String checkupDate) {
private void openHistoryViewDialog(String checkupId, String patientName, String checkupDate, String suggestion, String diagnosis, String conclusion, String notes) {
try {
HistoryViewDialog historyDialog = new HistoryViewDialog(mainFrame, checkupId, patientName, checkupDate);
// Pass all the necessary details to the dialog
HistoryViewDialog historyDialog = new HistoryViewDialog(mainFrame, checkupId, patientName, checkupDate, suggestion, diagnosis, conclusion, notes);
historyDialog.setVisible(true);
} catch (Exception e) {
log.error("Error opening history view dialog", e);
@@ -3058,55 +3119,57 @@ public class CheckUpPage extends JPanel {
}
private void clearPatientDetails() {
// Reset all fields to default values
checkupIdField.setText("");
customerLastNameField.setText("");
customerFirstNameField.setText("");
customerAddressField.setText("");
customerPhoneField.setText("");
customerIdField.setText("");
suggestionField.setText("");
diagnosisField.setText("");
conclusionField.setText("");
setRtfContentFromString("{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fnil\\fcharset163 Times New Roman;}}\\viewkind4\\uc1\\pard\\f0\\fs32\\par}"); // Clear notes
if (SwingUtilities.isEventDispatchThread()) {
// Reset all fields to default values
checkupIdField.setText("");
customerLastNameField.setText("");
customerFirstNameField.setText("");
customerAddressField.setText("");
customerPhoneField.setText("");
customerIdField.setText("");
suggestionField.setText("");
diagnosisField.setText("");
conclusionField.setText("");
setRtfContentFromString("{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fnil\\fcharset163 Times New Roman;}}\\viewkind4\\uc1\\pard\\f0\\fs32\\par}"); // Clear notes
if (doctorComboBox.getItemCount() > 0) doctorComboBox.setSelectedIndex(0);
statusComboBox.setSelectedIndex(0);
genderComboBox.setSelectedIndex(0);
if (provinceComboBox.getItemCount() > 0) provinceComboBox.setSelectedIndex(0);
// district and ward will be cleared by province selection listener
checkupTypeComboBox.setSelectedIndex(0);
if (templateComboBox.getItemCount() > 0) templateComboBox.setSelectedIndex(0);
if (doctorComboBox.getItemCount() > 0) doctorComboBox.setSelectedIndex(0);
statusComboBox.setSelectedIndex(0);
genderComboBox.setSelectedIndex(0);
if (provinceComboBox.getItemCount() > 0) provinceComboBox.setSelectedIndex(0);
// district and ward will be cleared by province selection listener
checkupTypeComboBox.setSelectedIndex(0);
if (templateComboBox.getItemCount() > 0) templateComboBox.setSelectedIndex(0);
customerWeightSpinner.setValue(0);
customerHeightSpinner.setValue(0);
customerWeightSpinner.setValue(0);
customerHeightSpinner.setValue(0);
datePicker.getModel().setValue(null);
dobPicker.getModel().setValue(null);
datePicker.getModel().setValue(null);
dobPicker.getModel().setValue(null);
medicinePrescription = new String[0][0];
servicePrescription = new String[0][0];
updatePrescriptionTree();
medicinePrescription = new String[0][0];
servicePrescription = new String[0][0];
updatePrescriptionTree();
historyModel.setRowCount(0); // Clear history table
historyModel.setRowCount(0); // Clear history table
// Disable all action buttons
for (Component comp : ((JPanel)((JPanel)rightContainer.getComponent(1))).getComponents()) {
if (comp instanceof JButton) {
comp.setEnabled(false);
// Disable all action buttons
for (Component comp : ((JPanel)((JPanel)rightContainer.getComponent(1))).getComponents()) {
if (comp instanceof JButton) {
comp.setEnabled(false);
}
}
}
// Clear media view
if (imageGalleryPanel != null) {
imageGalleryPanel.removeAll();
imageGalleryPanel.add(new JLabel("Chọn bệnh nhân để xem media."));
imageGalleryPanel.revalidate();
imageGalleryPanel.repaint();
}
// Clear media view
if (imageGalleryPanel != null) {
imageGalleryPanel.removeAll();
imageGalleryPanel.add(new JLabel("Chọn bệnh nhân để xem media."));
imageGalleryPanel.revalidate();
imageGalleryPanel.repaint();
}
selectedCheckupId = null;
saved = true; // No patient loaded, so it's in a "saved" state.
selectedCheckupId = null;
saved = true; // No patient loaded, so it's in a "saved" state.
}
}
}
@@ -11,6 +11,7 @@ import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
@@ -22,10 +23,13 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.text.rtf.RTFEditorKit;
import lombok.extern.slf4j.Slf4j;
import BsK.client.network.handler.ClientHandler;
import BsK.client.network.handler.ResponseListener;
import BsK.common.packet.req.GetImagesByCheckupIdReq;
import BsK.common.packet.req.GetOrderInfoByCheckupReq;
import BsK.common.packet.res.GetImagesByCheckupIdRes;
import BsK.common.packet.res.GetOrderInfoByCheckupRes;
import BsK.common.util.network.NetworkUtil;
@@ -38,6 +42,7 @@ public class HistoryViewDialog extends JDialog {
private String checkupDate;
private String content;
private String suggestion;
private String diagnosis;
private String conclusion;
private String[][] medicinePrescription;
private String[][] servicePrescription;
@@ -46,8 +51,9 @@ public class HistoryViewDialog extends JDialog {
private JLabel imagePreviewLabel;
private JPanel imageListPanel;
private JScrollPane imageListScrollPane;
private JTextArea contentArea;
private JTextPane contentArea;
private JTextArea suggestionArea;
private JTextArea diagnosisArea;
private JTextArea conclusionArea;
private JTree prescriptionTree;
private DefaultTreeModel prescriptionTreeModel;
@@ -65,76 +71,67 @@ public class HistoryViewDialog extends JDialog {
// Current selected image
private BufferedImage currentSelectedImage;
private List<File> imageFiles;
private ResponseListener<GetOrderInfoByCheckupRes> orderInfoListener;
private final ResponseListener<GetOrderInfoByCheckupRes> orderInfoListener = this::handleGetOrderInfoResponse;
private final ResponseListener<GetImagesByCheckupIdRes> imagesListener = this::handleGetImagesResponse;
public HistoryViewDialog(Frame parent, String checkupId, String patientName, String checkupDate) {
public HistoryViewDialog(Frame parent, String checkupId, String patientName, String checkupDate, String suggestion, String diagnosis, String conclusion, String notes) {
super(parent, "Xem chi tiết lịch sử khám - " + patientName, true);
this.checkupId = checkupId;
this.patientName = patientName;
this.checkupDate = checkupDate;
this.imageFiles = new ArrayList<>();
// Initialize sample data for demonstration (will be overwritten by network response)
initializeSampleData();
// Set data from parameters
this.content = notes; // This is the RTF content
this.suggestion = suggestion;
this.diagnosis = diagnosis;
this.conclusion = conclusion;
initializeDialog(parent);
setupUI();
loadImagesForCheckup();
updatePrescriptionTree();
// Add listeners
ClientHandler.addResponseListener(GetOrderInfoByCheckupRes.class, orderInfoListener);
ClientHandler.addResponseListener(GetImagesByCheckupIdRes.class, imagesListener);
// Fetch real data from server
fetchOrderInformation();
}
private void initializeSampleData() {
// Sample data - in real implementation, this would come from database
this.content = "Bệnh nhân có triệu chứng đau đầu, sốt nhẹ. Khám tổng quát không có bất thường đáng kể.";
this.suggestion = "Nghỉ ngơi đầy đủ, uống nhiều nước, theo dõi thêm 2-3 ngày.";
this.conclusion = "Cảm cúm thông thường, không có biến chứng.";
// Sample medicine prescription
this.medicinePrescription = new String[][] {
{"1", "Paracetamol 500mg", "2", "viên", "1", "1", "1", "5000", "30000", "Uống sau ăn"},
{"2", "Vitamin C", "1", "lọ", "0", "1", "0", "15000", "15000", "Uống trước ăn"}
};
// Sample service prescription
this.servicePrescription = new String[][] {
{"1", "Xét nghiệm máu", "1", "100000", "100000", "Xét nghiệm tổng quát"},
{"2", "Siêu âm", "1", "200000", "200000", "Siêu âm bụng"}
};
fetchCheckupImages();
}
private void fetchOrderInformation() {
try {
orderInfoListener = new ResponseListener<>() {
@Override
public void onResponse(GetOrderInfoByCheckupRes response) {
SwingUtilities.invokeLater(() -> {
try {
setMedicinePrescription(response.getMedicinePrescription());
setServicePrescription(response.getServicePrescription());
log.info("Successfully updated history order tree for checkupId: {}", checkupId);
} finally {
// Remove listener after execution to prevent memory leaks
if (orderInfoListener != null) {
ClientHandler.deleteListener(GetOrderInfoByCheckupRes.class, orderInfoListener);
orderInfoListener = null;
}
}
});
}
};
ClientHandler.addResponseListener(GetOrderInfoByCheckupRes.class, orderInfoListener);
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetOrderInfoByCheckupReq(this.checkupId));
log.info("Sent GetOrderInfoByCheckupReq for checkupId: {}", this.checkupId);
} catch (Exception e) {
log.error("Error sending GetOrderInfoByCheckupReq for checkupId: {}", this.checkupId, e);
if (orderInfoListener != null) {
ClientHandler.deleteListener(GetOrderInfoByCheckupRes.class, orderInfoListener);
orderInfoListener = null;
}
}
}
private void fetchCheckupImages() {
try {
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetImagesByCheckupIdReq(this.checkupId));
log.info("Sent GetImagesByCheckupIdReq for checkupId: {}", this.checkupId);
} catch (Exception e) {
log.error("Error sending GetImagesByCheckupIdReq for checkupId: {}", this.checkupId, e);
}
}
private void handleGetOrderInfoResponse(GetOrderInfoByCheckupRes response) {
SwingUtilities.invokeLater(() -> {
setMedicinePrescription(response.getMedicinePrescription());
setServicePrescription(response.getServicePrescription());
log.info("Successfully updated history order tree for checkupId: {}", checkupId);
});
}
private void handleGetImagesResponse(GetImagesByCheckupIdRes response) {
log.info("Client received GetImagesByCheckupIdRes for checkupId: {}. Image count: {}",
response.getCheckupId(), response.getImageDatas() != null ? response.getImageDatas().size() : 0);
if (checkupId.equals(response.getCheckupId())) {
SwingUtilities.invokeLater(() -> {
updateImageList(response.getImageNames(), response.getImageDatas());
});
}
}
@@ -162,10 +159,9 @@ public class HistoryViewDialog extends JDialog {
@Override
public void windowClosed(WindowEvent e) {
// Clean up listener when dialog is closed, just in case
if (orderInfoListener != null) {
ClientHandler.deleteListener(GetOrderInfoByCheckupRes.class, orderInfoListener);
}
// Clean up listeners when dialog is closed
ClientHandler.deleteListener(GetOrderInfoByCheckupRes.class, orderInfoListener);
ClientHandler.deleteListener(GetImagesByCheckupIdRes.class, imagesListener);
}
});
}
@@ -376,18 +372,17 @@ public class HistoryViewDialog extends JDialog {
tabbedPane.setFont(new Font("Arial", Font.BOLD, 12));
// Content tab
contentArea = new JTextArea(content);
contentArea = new JTextPane();
contentArea.setEditable(false);
contentArea.setLineWrap(true);
contentArea.setWrapStyleWord(true);
contentArea.setFont(new Font("Arial", Font.PLAIN, 12));
contentArea.setBackground(new Color(248, 249, 250));
contentArea.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
setRtfContent(contentArea, this.content);
JScrollPane contentScroll = new JScrollPane(contentArea);
tabbedPane.addTab("Nội dung khám", contentScroll);
// Suggestion tab
suggestionArea = new JTextArea(suggestion);
suggestionArea = new JTextArea(getDisplayableString(this.suggestion));
suggestionArea.setEditable(false);
suggestionArea.setLineWrap(true);
suggestionArea.setWrapStyleWord(true);
@@ -397,8 +392,19 @@ public class HistoryViewDialog extends JDialog {
JScrollPane suggestionScroll = new JScrollPane(suggestionArea);
tabbedPane.addTab("Đề nghị", suggestionScroll);
// Diagnosis tab
diagnosisArea = new JTextArea(getDisplayableString(this.diagnosis));
diagnosisArea.setEditable(false);
diagnosisArea.setLineWrap(true);
diagnosisArea.setWrapStyleWord(true);
diagnosisArea.setFont(new Font("Arial", Font.PLAIN, 12));
diagnosisArea.setBackground(new Color(248, 249, 250));
diagnosisArea.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
JScrollPane diagnosisScroll = new JScrollPane(diagnosisArea);
tabbedPane.addTab("Chẩn đoán", diagnosisScroll);
// Conclusion tab
conclusionArea = new JTextArea(conclusion);
conclusionArea = new JTextArea(getDisplayableString(this.conclusion));
conclusionArea.setEditable(false);
conclusionArea.setLineWrap(true);
conclusionArea.setWrapStyleWord(true);
@@ -468,32 +474,42 @@ public class HistoryViewDialog extends JDialog {
return panel;
}
private void loadImagesForCheckup() {
private void updateImageList(List<String> imageNames, List<byte[]> imageDatas) {
SwingUtilities.invokeLater(() -> {
imageFiles.clear();
imageListPanel.removeAll();
// Load images from checkup media directory
Path mediaPath = Paths.get(CHECKUP_MEDIA_BASE_DIR, checkupId);
if (Files.exists(mediaPath) && Files.isDirectory(mediaPath)) {
try {
Files.list(mediaPath)
.filter(path -> isImageFile(path.toString()))
.forEach(path -> {
File imageFile = path.toFile();
imageFiles.add(imageFile);
// Create thumbnail button
JButton thumbnailButton = createThumbnailButton(imageFile);
imageListPanel.add(thumbnailButton);
});
} catch (IOException e) {
log.error("Error loading images for checkup {}", checkupId, e);
if (imageNames != null && !imageNames.isEmpty()) {
log.info("Populating image list with {} images for checkupId: {}", imageNames.size(), checkupId);
for (int i = 0; i < imageNames.size(); i++) {
byte[] imageData = imageDatas.get(i);
String imageName = imageNames.get(i);
try {
ByteArrayInputStream bais = new ByteArrayInputStream(imageData);
BufferedImage image = ImageIO.read(bais);
if (image != null) {
JButton thumbnailButton = createThumbnailButton(image, imageName);
imageListPanel.add(thumbnailButton);
} else {
log.warn("Failed to decode image data for '{}'. ImageIO.read returned null. Data length: {} bytes.", imageName, imageData.length);
}
} catch (IOException e) {
log.error("Error decoding image data for {}", imageName, e);
}
}
}
if (imageFiles.isEmpty()) {
// Display the first image automatically
if (!imageDatas.isEmpty()) {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(imageDatas.get(0));
BufferedImage firstImage = ImageIO.read(bais);
if (firstImage != null) {
displayFullImage(firstImage, imageNames.get(0));
}
} catch (IOException e) {
log.error("Error decoding first image", e);
}
}
} else {
log.info("No images found for checkupId: {}. Displaying 'No images' label.", checkupId);
JLabel noImagesLabel = new JLabel("Không có hình ảnh nào");
noImagesLabel.setFont(new Font("Arial", Font.ITALIC, 12));
noImagesLabel.setForeground(Color.GRAY);
@@ -505,26 +521,22 @@ public class HistoryViewDialog extends JDialog {
});
}
private JButton createThumbnailButton(File imageFile) {
private JButton createThumbnailButton(BufferedImage image, String imageName) {
JButton button = new JButton();
button.setPreferredSize(new Dimension(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT));
button.setBorder(BorderFactory.createRaisedBevelBorder());
button.setFocusPainted(false);
button.setCursor(new Cursor(Cursor.HAND_CURSOR));
try {
BufferedImage originalImage = ImageIO.read(imageFile);
if (originalImage != null) {
Image scaledImage = originalImage.getScaledInstance(
THUMBNAIL_WIDTH - 4, THUMBNAIL_HEIGHT - 4, Image.SCALE_SMOOTH);
button.setIcon(new ImageIcon(scaledImage));
// Add click listener to show full image
button.addActionListener(e -> displayFullImage(originalImage, imageFile.getName()));
}
} catch (IOException e) {
log.error("Error creating thumbnail for image {}", imageFile.getName(), e);
button.setText("Error");
if (image != null) {
Image scaledImage = image.getScaledInstance(
THUMBNAIL_WIDTH - 4, THUMBNAIL_HEIGHT - 4, Image.SCALE_SMOOTH);
button.setIcon(new ImageIcon(scaledImage));
// Add click listener to show full image
button.addActionListener(e -> displayFullImage(image, imageName));
} else {
button.setText("Error");
}
// Add hover effects
@@ -545,23 +557,25 @@ public class HistoryViewDialog extends JDialog {
private void displayFullImage(BufferedImage image, String imageName) {
currentSelectedImage = image;
// Calculate the best fit for the preview area
Dimension previewSize = imagePreviewLabel.getSize();
if (previewSize.width <= 0 || previewSize.height <= 0) {
previewSize = new Dimension(400, 300); // Default size
}
// Calculate the best fit for the preview area.
// Use the container's size (the JScrollPane's viewport) instead of the label's own size
// to avoid a feedback loop where the scaled image size influences the next scaling operation.
Container previewContainer = imagePreviewLabel.getParent();
Dimension previewSize = (previewContainer != null && previewContainer.getWidth() > 0)
? previewContainer.getSize()
: new Dimension(400, 300); // Fallback size
double scaleX = (double) previewSize.width / image.getWidth();
double scaleY = (double) previewSize.height / image.getHeight();
double scale = Math.min(scaleX, scaleY);
int scaledWidth = (int) (image.getWidth() * scale);
int scaledHeight = (int) (image.getHeight() * scale);
Image scaledImage = image.getScaledInstance(scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
ImageIcon icon = new ImageIcon(scaledImage);
imagePreviewLabel.setText(""); // Clear text
imagePreviewLabel.setIcon(icon);
imagePreviewLabel.setToolTipText("Hình ảnh: " + imageName + " (" + image.getWidth() + "x" + image.getHeight() + ")");
@@ -670,21 +684,28 @@ public class HistoryViewDialog extends JDialog {
public void setContent(String content) {
this.content = content;
if (contentArea != null) {
contentArea.setText(content);
setRtfContent(contentArea, content);
}
}
public void setSuggestion(String suggestion) {
this.suggestion = suggestion;
if (suggestionArea != null) {
suggestionArea.setText(suggestion);
suggestionArea.setText(getDisplayableString(suggestion));
}
}
public void setDiagnosis(String diagnosis) {
this.diagnosis = diagnosis;
if (diagnosisArea != null) {
diagnosisArea.setText(getDisplayableString(diagnosis));
}
}
public void setConclusion(String conclusion) {
this.conclusion = conclusion;
if (conclusionArea != null) {
conclusionArea.setText(conclusion);
conclusionArea.setText(getDisplayableString(conclusion));
}
}
@@ -697,4 +718,29 @@ public class HistoryViewDialog extends JDialog {
this.servicePrescription = servicePrescription;
updatePrescriptionTree();
}
private void setRtfContent(JTextPane textPane, String rtfContent) {
textPane.setContentType("text/rtf");
// Check for null, empty, or placeholder RTF content
if (rtfContent == null || rtfContent.isBlank() || rtfContent.contains("\\cf0 null\\par")) {
rtfContent = "{\\rtf1\\ansi\\deff0 {\\fonttbl{\\f0 Arial;}}\\fs24\\i\\cf1 Không có thông tin.\\par}"; // Italic "No information"
}
try {
// The RTF data from the database might be encoded in a specific way.
// Using a stream helps handle character encoding correctly.
textPane.getEditorKit().read(new java.io.ByteArrayInputStream(rtfContent.getBytes()), textPane.getDocument(), 0);
} catch (Exception e) {
log.error("Failed to set RTF content for checkupId {}", checkupId, e);
// Fallback to plain text if RTF parsing fails
textPane.setContentType("text/plain");
textPane.setText("Không thể hiển thị nội dung chi tiết (lỗi định dạng).");
}
}
private String getDisplayableString(String input) {
if (input == null || input.isBlank() || "null".equalsIgnoreCase(input.trim())) {
return "Không có thông tin";
}
return input;
}
}
@@ -0,0 +1,11 @@
package BsK.common.packet.req;
import BsK.common.packet.Packet;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class GetImagesByCheckupIdReq implements Packet {
private String checkupId;
}
@@ -0,0 +1,14 @@
package BsK.common.packet.res;
import BsK.common.packet.Packet;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
@Data
@AllArgsConstructor
public class GetImagesByCheckupIdRes implements Packet {
private String checkupId;
private List<String> imageNames;
private List<byte[]> imageDatas;
}
+2 -2
View File
@@ -134,8 +134,8 @@ public class Server {
.addLast(new IdleStateHandler(60 * 30, 0, 0, TimeUnit.SECONDS))
.addLast(new HttpServerCodec())
.addLast(new ChunkedWriteHandler())
.addLast(new HttpObjectAggregator(8192))
.addLast("ws", new WebSocketServerProtocolHandler("/", null, true, 1048576))
.addLast(new HttpObjectAggregator(50 * 1024 * 1024))
.addLast(new WebSocketServerProtocolHandler("/", null, true, 50 * 1024 * 1024))
.addLast(new ServerHandler());
}
});
@@ -11,6 +11,7 @@ import BsK.server.Server;
import BsK.server.ServerDashboard;
import BsK.server.network.manager.SessionManager;
import BsK.server.network.util.UserUtil;
import BsK.server.network.entity.User;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
@@ -33,6 +34,7 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
@@ -48,6 +50,7 @@ import static BsK.server.Server.statement;
public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
private static final String IMAGE_DB_PATH = "img_db";
private static final String CHECKUP_MEDIA_BASE_DIR = "src/main/resources/image/checkup_media";
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame frame) {
@@ -276,7 +279,7 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
String sql = """
SELECT
C.checkup_date, C.checkup_id, C.suggestion, C.diagnosis, C.prescription_id, C.notes
C.checkup_date, C.checkup_id, C.suggestion, C.diagnosis, C.prescription_id, C.notes, C.checkup_type, C.conclusion
FROM Checkup C
WHERE C.status = ? AND C.customer_id = ?
ORDER BY C.checkup_date DESC
@@ -294,7 +297,7 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
} else {
ArrayList<String[]> resultList = new ArrayList<>();
while (rs.next()) {
String[] historyEntry = new String[6];
String[] historyEntry = new String[8];
String checkupDateStr = rs.getString("checkup_date");
try {
@@ -312,13 +315,15 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
historyEntry[3] = rs.getString("diagnosis");
historyEntry[4] = rs.getString("prescription_id");
historyEntry[5] = rs.getString("notes");
historyEntry[6] = rs.getString("checkup_type");
historyEntry[7] = rs.getString("conclusion");
resultList.add(historyEntry);
}
String[][] resultArray = resultList.toArray(new String[0][]);
UserUtil.sendPacket(currentUser.getSessionId(), new GetPatientHistoryResponse(resultArray));
log.info("Sent patient history for patientId: {}", getPatientHistoryRequest.getPatientId());
log.info("Sent patient history for patientId: {} the content: {}", getPatientHistoryRequest.getPatientId(), resultArray);
}
}
} catch (SQLException e) {
@@ -1148,12 +1153,12 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
}
}
if (packet instanceof UploadCheckupImageRequest request) {
log.info("Received UploadCheckupImageRequest for checkupId: {}", request.getCheckupId());
if (packet instanceof UploadCheckupImageRequest uploadCheckupImageRequest) {
log.info("Received UploadCheckupImageRequest for checkupId: {}", uploadCheckupImageRequest.getCheckupId());
String checkupId = request.getCheckupId();
String fileName = request.getFileName();
byte[] imageData = request.getImageData();
String checkupId = uploadCheckupImageRequest.getCheckupId();
String fileName = uploadCheckupImageRequest.getFileName();
byte[] imageData = uploadCheckupImageRequest.getImageData();
if (checkupId == null || checkupId.trim().isEmpty()) {
UserUtil.sendPacket(currentUser.getSessionId(), new UploadCheckupImageResponse(false, "CheckupID is null or empty. Cannot save image.", fileName));
@@ -1184,6 +1189,43 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
UserUtil.sendPacket(currentUser.getSessionId(), new UploadCheckupImageResponse(false, "Server failed to save image: " + e.getMessage(), fileName));
}
}
if (packet instanceof GetImagesByCheckupIdReq getImagesByCheckupIdReq) {
String checkupId = getImagesByCheckupIdReq.getCheckupId();
log.info("Received GetImagesByCheckupIdReq for checkupId: {}", checkupId);
List<String> imageNames = new ArrayList<>();
List<byte[]> imageDatas = new ArrayList<>();
Path checkupDir = Paths.get(IMAGE_DB_PATH, checkupId);
if (Files.exists(checkupDir) && Files.isDirectory(checkupDir)) {
try {
List<Path> imagePaths = Files.list(checkupDir)
.filter(path -> {
String filename = path.toString().toLowerCase();
return filename.endsWith(".jpg") || filename.endsWith(".jpeg") || filename.endsWith(".png") || filename.endsWith(".gif") || filename.endsWith(".bmp");
})
.collect(Collectors.toList());
for (Path imagePath : imagePaths) {
try {
imageDatas.add(Files.readAllBytes(imagePath));
imageNames.add(imagePath.getFileName().toString());
} catch (IOException e) {
log.error("Failed to read image file: {}", imagePath, e);
}
}
} catch (IOException e) {
log.error("Failed to list images for checkupId: {}", checkupId, e);
}
} else {
log.warn("Image directory not found for checkupId: {}", checkupId);
}
UserUtil.sendPacket(currentUser.getSessionId(), new GetImagesByCheckupIdRes(checkupId, imageNames, imageDatas));
log.info("Sent {} images for checkupId: {}", imageDatas.size(), checkupId);
}
}
}
Binary file not shown.