[update] Enhance DataDialog and NavBar components to support doctor management features, including adding and editing doctors. Introduce client and server version display in the UI. Update GetCheckupDataRequest and LoginRequest to include shift information. Modify ultrasound result report for improved layout and clarity. Update binary database files to reflect changes.

This commit is contained in:
Lê Thành Đạt committed 2025-11-02 01:10:41 -05:00
1 parent 8dac9f2a1b
commit 46a1cf2dcc
18 files changed
+665 -27

No files matched your search

+2 -1
View File
@@ -1,4 +1,5 @@
{
"java.configuration.updateBuildConfiguration": "automatic",
"java.debug.settings.onBuildFailureProceed": true
"java.debug.settings.onBuildFailureProceed": true,
"java.compile.nullAnalysis.mode": "automatic"
}
Binary file not shown.
Binary file not shown.
@@ -12,6 +12,7 @@ import BsK.common.packet.res.DeleteCheckupResponse;
import BsK.common.packet.res.GetCheckupDataResponse;
import BsK.common.util.network.NetworkUtil;
import BsK.client.ui.component.CheckUpPage.CheckUpPage;
import BsK.common.entity.CheckupData;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
@@ -28,7 +29,9 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DataDialog extends JDialog {
//<editor-fold desc="Properties for Checkup Data Tab">
@@ -57,6 +60,7 @@ public class DataDialog extends JDialog {
// --- Panels for the other tabs
private MedicineManagementPanel medicinePanel;
private ServiceManagementPanel servicePanel;
private DoctorManagementPanel doctorPanel;
private UserManagementPanel userPanel;
public DataDialog(JFrame parent, CheckUpPage checkUpPage) {
@@ -100,6 +104,10 @@ public class DataDialog extends JDialog {
userPanel = new UserManagementPanel();
tabbedPane.addTab("<html><body style='padding: 5px 10px;'>Quản Lý Người Dùng</body></html>", userPanel);
// --- Tab 5: Doctor Management ---
doctorPanel = new DoctorManagementPanel();
tabbedPane.addTab("<html><body style='padding: 5px 10px;'>Quản Lý Bác Sĩ</body></html>", doctorPanel);
add(tabbedPane, BorderLayout.CENTER);
loadFiltersFromLocalStorage();
@@ -124,6 +132,9 @@ public class DataDialog extends JDialog {
if (userPanel != null) {
userPanel.cleanup();
}
if (doctorPanel != null) {
doctorPanel.cleanup();
}
super.windowClosing(e);
}
});
@@ -285,7 +296,7 @@ public class DataDialog extends JDialog {
});
getAllButton.addActionListener(e -> {
GetCheckupDataRequest request = new GetCheckupDataRequest(null, null,null, null, null, 1, recordsPerPage);
GetCheckupDataRequest request = new GetCheckupDataRequest(null, null,null, null, null, 1, recordsPerPage, LocalStorage.currentShift);
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request);
resultCountLabel.setText("Đang tải tất cả dữ liệu...");
});
@@ -380,7 +391,7 @@ public class DataDialog extends JDialog {
}
}
GetCheckupDataRequest request = new GetCheckupDataRequest(searchTerm, checkupIdSearch, fromTimestamp, toTimestamp, doctorId, page, recordsPerPage);
GetCheckupDataRequest request = new GetCheckupDataRequest(searchTerm, checkupIdSearch, fromTimestamp, toTimestamp, doctorId, page, recordsPerPage, LocalStorage.currentShift);
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request);
resultCountLabel.setText("Đang tải dữ liệu cho trang " + page + "...");
@@ -679,7 +690,7 @@ public class DataDialog extends JDialog {
if (confirmation == JOptionPane.YES_OPTION) {
try {
long checkupId = Long.parseLong(checkupIdStr);
DeleteCheckupRequest deleteRequest = new DeleteCheckupRequest(checkupId);
DeleteCheckupRequest deleteRequest = new DeleteCheckupRequest(checkupId, LocalStorage.currentShift);
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), deleteRequest);
} catch (NumberFormatException ex) {
JOptionPane.showMessageDialog(parentDialog, "Mã khám không hợp lệ: " + checkupIdStr, "Lỗi", JOptionPane.ERROR_MESSAGE);
@@ -0,0 +1,338 @@
package BsK.client.ui.component.DataDialog;
import BsK.client.LocalStorage;
import BsK.client.network.handler.ClientHandler;
import BsK.client.network.handler.ResponseListener;
import BsK.common.entity.Doctor;
import BsK.common.entity.DoctorItem;
import BsK.common.packet.req.AddDoctorRequest;
import BsK.common.packet.req.EditDoctorRequest;
import BsK.common.packet.req.GetDoctorInfoRequest;
import BsK.common.packet.res.GetDoctorInfoResponse;
import BsK.common.util.network.NetworkUtil;
import BsK.common.util.text.TextUtils;
import lombok.extern.slf4j.Slf4j;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class DoctorManagementPanel extends JPanel {
private JTable doctorTable;
private DefaultTableModel doctorTableModel;
private JTextField doctorSearchField;
private JTextField hoField;
private JTextField tenField;
private JCheckBox chkIsDeleted;
private JButton btnAdd, btnEdit, btnClear;
private List<Doctor> allDoctors = new ArrayList<>();
private String selectedDoctorId = null;
// --- Networking ---
private final ResponseListener<GetDoctorInfoResponse> getDoctorInfoResponseListener = this::handleGetDoctorInfoResponse;
public DoctorManagementPanel() {
super(new BorderLayout(10, 10));
initComponents();
setupNetworking();
}
private void setupNetworking() {
ClientHandler.addResponseListener(GetDoctorInfoResponse.class, getDoctorInfoResponseListener);
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetDoctorInfoRequest());
}
private void initComponents() {
this.setBorder(new EmptyBorder(15, 15, 15, 15));
this.add(createDoctorInputPanel(), BorderLayout.WEST);
this.add(createDoctorListPanel(), BorderLayout.CENTER);
}
private void handleGetDoctorInfoResponse(GetDoctorInfoResponse response) {
SwingUtilities.invokeLater(() -> {
if (response != null && response.getDoctors() != null) {
allDoctors = response.getDoctors();
populateDoctorTable();
// Update LocalStorage.doctorsName with only non-deleted doctors
LocalStorage.doctorsName.clear();
for (Doctor doctor : allDoctors) {
if ("0".equals(doctor.getDeleted())) {
LocalStorage.doctorsName.add(new DoctorItem(
doctor.getId(),
doctor.getFullName()
));
}
}
log.info("Updated LocalStorage.doctorsName with {} active doctors", LocalStorage.doctorsName.size());
}
});
}
private JPanel createDoctorInputPanel() {
JPanel mainInputPanel = new JPanel(new GridBagLayout());
mainInputPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
GridBagConstraints mainGbc = new GridBagConstraints();
mainGbc.fill = GridBagConstraints.HORIZONTAL;
mainGbc.anchor = GridBagConstraints.NORTHWEST;
mainGbc.insets = new Insets(5, 5, 5, 5);
mainGbc.weightx = 1.0;
Font titleFont = new Font("Arial", Font.BOLD, 16);
Font labelFont = new Font("Arial", Font.BOLD, 15);
Font textFont = new Font("Arial", Font.PLAIN, 15);
Dimension textFieldSize = new Dimension(100, 30);
JPanel doctorInfoPanel = new JPanel(new GridBagLayout());
doctorInfoPanel.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(), "Thông tin bác sĩ",
TitledBorder.LEADING, TitledBorder.TOP,
titleFont, new Color(50, 50, 50)
));
mainGbc.gridx = 0;
mainGbc.gridy = 0;
mainInputPanel.add(doctorInfoPanel, mainGbc);
mainGbc.gridy = 1;
mainInputPanel.add(createDoctorButtonPanel(), mainGbc);
mainGbc.gridy = 2;
mainGbc.weighty = 1.0;
mainInputPanel.add(new JPanel(), mainGbc);
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(5, 5, 5, 5);
gbc.weightx = 0.0;
gbc.weighty = 0.0;
gbc.gridy = 0;
gbc.gridx = 0;
JLabel hoLabel = new JLabel("Họ:");
hoLabel.setFont(labelFont);
doctorInfoPanel.add(hoLabel, gbc);
gbc.gridx = 1;
gbc.weightx = 1.0;
hoField = new JTextField(20);
hoField.setFont(textFont);
hoField.setPreferredSize(textFieldSize);
doctorInfoPanel.add(hoField, gbc);
gbc.gridy++;
gbc.gridx = 0;
gbc.weightx = 0.0;
JLabel tenLabel = new JLabel("Tên:");
tenLabel.setFont(labelFont);
doctorInfoPanel.add(tenLabel, gbc);
gbc.gridx = 1;
gbc.weightx = 1.0;
tenField = new JTextField(10);
tenField.setFont(textFont);
tenField.setPreferredSize(textFieldSize);
doctorInfoPanel.add(tenField, gbc);
return mainInputPanel;
}
private JPanel createDoctorButtonPanel() {
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 5));
btnAdd = new JButton("Thêm mới");
btnEdit = new JButton("Chỉnh sửa");
btnClear = new JButton("Làm mới");
chkIsDeleted = new JCheckBox("Ẩn (Xoá)");
chkIsDeleted.setFont(new Font("Arial", Font.BOLD, 13));
Dimension btnSize = new Dimension(100, 35);
btnAdd.setPreferredSize(btnSize);
btnEdit.setPreferredSize(btnSize);
btnClear.setPreferredSize(btnSize);
buttonPanel.add(chkIsDeleted);
buttonPanel.add(btnAdd);
buttonPanel.add(btnEdit);
buttonPanel.add(btnClear);
btnClear.addActionListener(e -> clearDoctorFields());
// --- ADD DOCTOR ACTION ---
btnAdd.addActionListener(e -> {
// 1. Validate required fields
String ho = hoField.getText().trim();
String ten = tenField.getText().trim();
if (ho.isEmpty() || ten.isEmpty()) {
JOptionPane.showMessageDialog(this, "Họ và tên không được để trống.", "Lỗi", JOptionPane.ERROR_MESSAGE);
if (ho.isEmpty()) {
hoField.requestFocusInWindow();
} else {
tenField.requestFocusInWindow();
}
return;
}
// 2. Create the request packet and send it to the server
AddDoctorRequest request = new AddDoctorRequest(ho, ten, false);
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request);
// 3. Provide feedback and reset the form
JOptionPane.showMessageDialog(this, "Yêu cầu thêm bác sĩ '" + ho + " " + ten + "' đã được gửi.", "Thành công", JOptionPane.INFORMATION_MESSAGE);
clearDoctorFields();
});
// --- EDIT DOCTOR ACTION ---
btnEdit.addActionListener(e -> {
// 1. Check if a doctor has been selected
if (selectedDoctorId == null) {
JOptionPane.showMessageDialog(this, "Vui lòng chọn một bác sĩ để chỉnh sửa.", "Chưa chọn bác sĩ", JOptionPane.WARNING_MESSAGE);
return;
}
// 2. Validate required fields
String ho = hoField.getText().trim();
String ten = tenField.getText().trim();
if (ho.isEmpty() || ten.isEmpty()) {
JOptionPane.showMessageDialog(this, "Họ và tên không được để trống.", "Lỗi", JOptionPane.ERROR_MESSAGE);
if (ho.isEmpty()) {
hoField.requestFocusInWindow();
} else {
tenField.requestFocusInWindow();
}
return;
}
// 3. Gather data from all fields
String id = selectedDoctorId;
Boolean isDeleted = chkIsDeleted.isSelected();
// 4. Create the request packet and send it
EditDoctorRequest request = new EditDoctorRequest(id, ho, ten, isDeleted);
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request);
// 5. Provide user feedback and clear the form
JOptionPane.showMessageDialog(this, "Yêu cầu chỉnh sửa bác sĩ '" + ho + " " + ten + "' đã được gửi.", "Thành công", JOptionPane.INFORMATION_MESSAGE);
clearDoctorFields();
});
btnEdit.setEnabled(false);
chkIsDeleted.setEnabled(false);
return buttonPanel;
}
private JPanel createDoctorListPanel() {
JPanel listPanel = new JPanel(new BorderLayout(10, 10));
listPanel.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(), "Danh Sách Bác Sĩ",
TitledBorder.LEFT, TitledBorder.TOP, new Font("Arial", Font.BOLD, 14)));
String[] doctorColumns = {"ID", "Họ", "Tên", "Trạng thái"};
doctorTableModel = new DefaultTableModel(doctorColumns, 0) {
@Override
public boolean isCellEditable(int row, int column) { return false; }
};
doctorTable = new JTable(doctorTableModel);
doctorTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
doctorTable.setFont(new Font("Arial", Font.PLAIN, 12));
doctorTable.setRowHeight(28);
doctorTable.getSelectionModel().addListSelectionListener(e -> {
if (!e.getValueIsAdjusting() && doctorTable.getSelectedRow() != -1) {
int viewRow = doctorTable.getSelectedRow();
String doctorId = doctorTableModel.getValueAt(viewRow, 0).toString();
allDoctors.stream()
.filter(doctor -> doctor.getId().equals(doctorId))
.findFirst()
.ifPresent(this::populateDoctorFields);
}
});
JPanel searchPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
searchPanel.add(new JLabel("Tìm kiếm bác sĩ:"));
doctorSearchField = new JTextField(25);
searchPanel.add(doctorSearchField);
doctorSearchField.getDocument().addDocumentListener(new DocumentListener() {
@Override public void insertUpdate(DocumentEvent e) { filterDoctorTable(); }
@Override public void removeUpdate(DocumentEvent e) { filterDoctorTable(); }
@Override public void changedUpdate(DocumentEvent e) { filterDoctorTable(); }
});
listPanel.add(searchPanel, BorderLayout.NORTH);
listPanel.add(new JScrollPane(doctorTable), BorderLayout.CENTER);
return listPanel;
}
private void populateDoctorTable() {
filterDoctorTable();
}
private void populateDoctorFields(Doctor doctor) {
if (doctor == null) return;
selectedDoctorId = doctor.getId();
hoField.setText(doctor.getLastName());
tenField.setText(doctor.getFirstName());
chkIsDeleted.setSelected("1".equals(doctor.getDeleted()));
btnAdd.setEnabled(false);
btnEdit.setEnabled(true);
chkIsDeleted.setEnabled(true);
}
private void clearDoctorFields() {
selectedDoctorId = null;
hoField.setText("");
tenField.setText("");
chkIsDeleted.setSelected(false);
doctorTable.clearSelection();
hoField.requestFocusInWindow();
btnAdd.setEnabled(true);
btnEdit.setEnabled(false);
chkIsDeleted.setEnabled(false);
}
private void filterDoctorTable() {
String filterText = doctorSearchField.getText().trim();
String lowerCaseFilterText = TextUtils.removeAccents(filterText.toLowerCase());
doctorTableModel.setRowCount(0);
for (Doctor doctor : allDoctors) {
String fullName = doctor.getFullName();
if (filterText.isEmpty() || TextUtils.removeAccents(fullName.toLowerCase()).contains(lowerCaseFilterText)) {
String status = "0".equals(doctor.getDeleted()) ? "Hoạt động" : "Đã ẩn";
doctorTableModel.addRow(new Object[]{
doctor.getId(),
doctor.getLastName(),
doctor.getFirstName(),
status
});
}
}
}
public void cleanup() {
ClientHandler.deleteListener(GetDoctorInfoResponse.class, getDoctorInfoResponseListener);
}
}
@@ -32,6 +32,11 @@ import java.awt.event.ActionListener;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Calendar;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
public class NavBar extends JPanel {
@@ -281,6 +286,15 @@ public class NavBar extends JPanel {
// --- END MODIFIED SEPARATOR ---
}
String version = getClientVersion();
if (!"N/A".equals(version)) {
JLabel versionLabel = new JLabel("Phiên bản: " + version);
versionLabel.setFont(new Font("Arial", Font.PLAIN, 10));
versionLabel.setForeground(new Color(120, 120, 120));
versionLabel.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 0));
navItemsPanel.add(versionLabel);
}
this.add(navItemsPanel);
this.add(Box.createHorizontalGlue());
this.add(titlePanel);
@@ -752,4 +766,18 @@ public class NavBar extends JPanel {
ClientHandler.deleteListener(SetCounterResponse.class, setCounterResponseListener); // NEW: Clean up counter listener
log.info("All NavBar listeners removed.");
}
private String getClientVersion() {
try {
String content = new String(Files.readAllBytes(Paths.get("version_client.json")));
Pattern pattern = Pattern.compile("\"currentVersion\":\\s*\"([^\"]*)\"");
Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
return matcher.group(1);
}
} catch (IOException e) {
log.error("Error reading client version file", e);
}
return "N/A";
}
}
@@ -0,0 +1,49 @@
package BsK.common.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Entity class for Doctor
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Doctor {
private String id;
private String lastName;
private String firstName;
private String deleted; // 0: active, 1: deleted
/**
* Constructor to create Doctor from array data
* @param data Array of doctor data from backend [id, lastName, firstName, deleted]
*/
public Doctor(String[] data) {
if (data.length < 4) {
throw new IllegalArgumentException("Doctor data array must contain at least 4 elements");
}
this.id = data[0];
this.lastName = data[1];
this.firstName = data[2];
this.deleted = data[3];
}
/**
* Get the full name of the doctor
* @return Full name (lastName + firstName)
*/
public String getFullName() {
return (lastName + " " + firstName).trim();
}
/**
* Convert the Doctor entity to a String array
* @return String array representation of Doctor
*/
public String[] toStringArray() {
return new String[]{id, lastName, firstName, deleted};
}
}
@@ -8,7 +8,7 @@ import lombok.Getter;
public class DoctorItem {
private String id;
private String name;
/**
* This is the crucial part. JComboBox will use this method to display the
* item in the dropdown.
@@ -0,0 +1,13 @@
package BsK.common.packet.req;
import BsK.common.packet.Packet;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class AddDoctorRequest implements Packet {
private String lastName;
private String firstName;
private Boolean deleted;
}
@@ -0,0 +1,14 @@
package BsK.common.packet.req;
import BsK.common.packet.Packet;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class EditDoctorRequest implements Packet {
private String id;
private String lastName;
private String firstName;
private Boolean deleted;
}
@@ -16,4 +16,5 @@ public class GetCheckupDataRequest implements Packet {
private Integer doctorId;
private int page = 1;
private int pageSize = 20;
private int shift;
}
@@ -0,0 +1,11 @@
package BsK.common.packet.req;
import BsK.common.packet.Packet;
import lombok.AllArgsConstructor;
import lombok.Data;
@AllArgsConstructor
@Data
public class GetDoctorInfoRequest implements Packet {
}
@@ -12,4 +12,5 @@ import java.time.LocalDateTime;
public class LoginRequest implements Packet {
String username;
String password;
int shift;
}
@@ -0,0 +1,50 @@
package BsK.common.packet.res;
import BsK.common.entity.Doctor;
import BsK.common.packet.Packet;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
@AllArgsConstructor
public class GetDoctorInfoResponse implements Packet {
private String[][] doctorInfo;
private transient List<Doctor> doctors;
public GetDoctorInfoResponse(String[][] doctorInfo) {
this.doctorInfo = doctorInfo;
this.doctors = convertToDoctorList(doctorInfo);
}
/**
* Converts the raw string array data to a list of Doctor objects
* @param doctorInfo Raw doctor data as string arrays
* @return List of Doctor objects
*/
private List<Doctor> convertToDoctorList(String[][] doctorInfo) {
List<Doctor> result = new ArrayList<>();
if (doctorInfo != null) {
for (String[] data : doctorInfo) {
if (data.length >= 4) {
result.add(new Doctor(data));
}
}
}
return result;
}
/**
* Returns the doctor list, converting from raw data if needed
* @return List of Doctor objects
*/
public List<Doctor> getDoctors() {
if (doctors == null && doctorInfo != null) {
doctors = convertToDoctorList(doctorInfo);
}
return doctors;
}
}
+25 -1
View File
@@ -7,12 +7,17 @@ import javax.swing.text.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.OperatingSystemMXBean;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import BsK.server.network.entity.ClientConnection;
import BsK.server.network.manager.SessionManager;
import lombok.extern.slf4j.Slf4j;
@@ -55,7 +60,12 @@ public class ServerDashboard extends JFrame {
}
private ServerDashboard() {
setTitle("Bảng Điều Khiển Máy Chủ BSK");
String version = getServerVersion();
String title = "Bảng Điều Khiển Máy Chủ BSK";
if (!"N/A".equals(version)) {
title += " - Phiên bản " + version;
}
setTitle(title);
setSize(1400, 900);
setLocationRelativeTo(null);
@@ -650,5 +660,19 @@ public class ServerDashboard extends JFrame {
}
// If the user chooses "No", the dialog closes and nothing else happens.
}
private String getServerVersion() {
try {
String content = new String(Files.readAllBytes(Paths.get("version_server.json")));
Pattern pattern = Pattern.compile("\"currentVersion\":\\s*\"([^\"]*)\"");
Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
return matcher.group(1);
}
} catch (IOException e) {
log.error("Error reading server version file", e);
}
return "N/A";
}
// --- MODIFICATION END ---
}
@@ -1868,7 +1868,7 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
}
}
if (packet instanceof EditServiceRequest editServiceRequest) {
if (packet instanceof EditServiceRequest editServiceRequest) {
if (!currentUser.getRole().equals(Role.ADMIN)) {
UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.ACCESS_DENIED));
return;
@@ -1898,6 +1898,66 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
}
}
if (packet instanceof GetDoctorInfoRequest getDoctorInfoRequest) {
log.debug("Received GetDoctorInfoRequest");
getDoctorInfo(currentUser.getSessionId());
}
if (packet instanceof AddDoctorRequest addDoctorRequest) {
if (!currentUser.getRole().equals(Role.ADMIN)) {
UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.ACCESS_DENIED));
return;
}
log.info("Received AddDoctorRequest for doctor: {} {}", addDoctorRequest.getLastName(), addDoctorRequest.getFirstName());
String sql = "INSERT INTO Doctor (doctor_last_name, doctor_first_name, deleted) VALUES (?, ?, ?)";
try (Connection conn = DatabaseManager.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, addDoctorRequest.getLastName());
stmt.setString(2, addDoctorRequest.getFirstName());
stmt.setBoolean(3, addDoctorRequest.getDeleted());
stmt.executeUpdate();
log.info("Added doctor successfully: {} {}", addDoctorRequest.getLastName(), addDoctorRequest.getFirstName());
getDoctorInfo(currentUser.getSessionId());
} catch (SQLException e) {
log.error("Error adding doctor", e);
UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
}
}
if (packet instanceof EditDoctorRequest editDoctorRequest) {
if (!currentUser.getRole().equals(Role.ADMIN)) {
UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.ACCESS_DENIED));
return;
}
log.info("Received EditDoctorRequest for doctor ID: {}", editDoctorRequest.getId());
String sql = "UPDATE Doctor SET doctor_last_name = ?, doctor_first_name = ?, deleted = ? WHERE doctor_id = ?";
try (Connection conn = DatabaseManager.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, editDoctorRequest.getLastName());
stmt.setString(2, editDoctorRequest.getFirstName());
stmt.setBoolean(3, editDoctorRequest.getDeleted());
stmt.setString(4, editDoctorRequest.getId());
int affectedRows = stmt.executeUpdate();
if (affectedRows > 0) {
log.info("Updated doctor successfully: {} {}", editDoctorRequest.getLastName(), editDoctorRequest.getFirstName());
getDoctorInfo(currentUser.getSessionId());
} else {
log.warn("No doctor found with ID {} to update.", editDoctorRequest.getId());
}
} catch (SQLException e) {
log.error("Error updating doctor", e);
UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
}
}
if (packet instanceof GetAllUserInfoRequest) {
if (!currentUser.getRole().equals(Role.ADMIN)) {
UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.ACCESS_DENIED));
@@ -2159,6 +2219,55 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
}
}
/**
* Lấy danh sách tất cả bác sĩ và gửi cho một session cụ thể.
*
* @param sessionId ID của session sẽ nhận dữ liệu.
*/
private void getDoctorInfo(int sessionId) {
log.debug("Fetching all doctors for session: {}", sessionId);
String sql = """
SELECT doctor_id, doctor_last_name, doctor_first_name, deleted
FROM Doctor
ORDER BY doctor_id
""";
try (Connection conn = DatabaseManager.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery()) {
ArrayList<String> resultList = new ArrayList<>();
while (rs.next()) {
String doctorId = rs.getString("doctor_id");
String doctorLastName = rs.getString("doctor_last_name");
String doctorFirstName = rs.getString("doctor_first_name");
// Lấy giá trị boolean một cách an toàn và chuyển thành String "1" hoặc "0"
String deleted = rs.getBoolean("deleted") ? "1" : "0";
String result = String.join("|",
doctorId,
doctorLastName != null ? doctorLastName : "",
doctorFirstName != null ? doctorFirstName : "",
deleted
);
resultList.add(result);
}
String[] resultString = resultList.toArray(new String[0]);
String[][] resultArray = new String[resultString.length][];
for (int i = 0; i < resultString.length; i++) {
resultArray[i] = resultString[i].split("\\|", -1);
}
UserUtil.sendPacket(sessionId, new GetDoctorInfoResponse(resultArray));
} catch (SQLException e) {
log.error("Error fetching doctors", e);
UserUtil.sendPacket(sessionId, new ErrorResponse(Error.SQL_EXCEPTION));
}
}
/**
* Lấy danh sách bệnh nhân cần tái khám trong vòng 7 ngày tới và gửi cho một session cụ thể.
*
@@ -66,13 +66,13 @@
<element kind="staticText" uuid="bcbb614e-7a4d-48a3-83c2-198e4fb62c5f" x="1" y="100" width="49" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
<text><![CDATA[Địa chỉ:]]></text>
</element>
<element kind="staticText" uuid="45fe4be1-9e21-459b-aeee-2efc8354f372" x="201" y="69" width="80" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
<text><![CDATA[Bác sĩ chỉ định:]]></text>
<element kind="staticText" uuid="45fe4be1-9e21-459b-aeee-2efc8354f372" x="216" y="69" width="80" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
<text><![CDATA[BS chỉ định:]]></text>
</element>
<element kind="staticText" uuid="f2ff4b17-63cc-4e8c-a3b0-2523f534f21a" x="217" y="84" width="70" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
<text><![CDATA[Ngày khám:]]></text>
</element>
<element kind="textField" uuid="1511f53a-b282-41b1-b233-612b6106c07a" x="57" y="69" width="143" height="15" fontName="Times New Roman" fontSize="12.0">
<element kind="textField" uuid="1511f53a-b282-41b1-b233-612b6106c07a" x="57" y="69" width="160" height="15" fontName="Times New Roman" fontSize="12.0">
<expression><![CDATA[$P{patientName}]]></expression>
</element>
<element kind="textField" uuid="5bdac267-523d-4931-926b-76233ce9b84d" x="57" y="84" width="74" height="15" fontName="Times New Roman" fontSize="12.0">
@@ -102,9 +102,6 @@
<element kind="textField" uuid="6d652a25-b701-45bd-91ba-750842650004" x="0" y="177" width="398" height="376" markup="rtf" fontName="Times New Roman" fontSize="16.0">
<expression><![CDATA[$P{checkupNote}]]></expression>
</element>
<element kind="staticText" uuid="8f720f6a-0257-4556-b3bc-3b81ad4f8099" x="297" y="145" width="59" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
<text><![CDATA[Chiều cao:]]></text>
</element>
<element kind="staticText" uuid="a57e7a9d-4ac7-4219-b268-cd08ce943327" x="124" y="145" width="59" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
<text><![CDATA[Cân nặng:]]></text>
</element>
@@ -114,9 +111,6 @@
<element kind="staticText" uuid="c1356370-11ce-4b68-97d6-ce6e18e306c5" x="0" y="145" width="51" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
<text><![CDATA[Huyết áp:]]></text>
</element>
<element kind="textField" uuid="3e1069a6-daea-45c2-b21f-935b49baba45" x="352" y="145" width="46" height="16" fontName="Times New Roman" fontSize="12.0">
<expression><![CDATA[$P{checkupHeight} +" cm"]]></expression>
</element>
<element kind="textField" uuid="fb080012-3196-4535-bcd2-e87274c66fa5" x="178" y="145" width="43" height="16" fontName="Times New Roman" fontSize="12.0">
<expression><![CDATA[$P{checkupWeight} + " kg"]]></expression>
</element>
@@ -258,7 +252,7 @@
<element kind="image" uuid="14220495-3e07-4322-9c5f-ebf98a8083c8" x="-50" y="3" width="48" height="48">
<expression><![CDATA[$P{driveURL}]]></expression>
</element>
<element kind="staticText" uuid="fbffa5e9-b68c-4bd9-be86-34d3537fbfa5" x="2" y="538" width="196" height="15" fontName="Times New Roman" fontSize="12.0" italic="true" bold="true" hTextAlign="Left">
<element kind="staticText" uuid="fbffa5e9-b68c-4bd9-be86-34d3537fbfa5" x="2" y="538" width="196" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" italic="true" hTextAlign="Left">
<text><![CDATA[Lưu ý: Mang phiếu khi tái khám]]></text>
</element>
</element>
@@ -71,13 +71,13 @@
<element kind="staticText" uuid="bcbb614e-7a4d-48a3-83c2-198e4fb62c5f" x="1" y="101" width="49" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
<text><![CDATA[Địa chỉ:]]></text>
</element>
<element kind="staticText" uuid="45fe4be1-9e21-459b-aeee-2efc8354f372" x="221" y="67" width="89" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
<element kind="staticText" uuid="45fe4be1-9e21-459b-aeee-2efc8354f372" x="247" y="67" width="89" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
<text><![CDATA[Bác sĩ chỉ định:]]></text>
</element>
<element kind="staticText" uuid="f2ff4b17-63cc-4e8c-a3b0-2523f534f21a" x="221" y="83" width="69" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
<text><![CDATA[Ngày khám:]]></text>
</element>
<element kind="textField" uuid="1511f53a-b282-41b1-b233-612b6106c07a" x="56" y="67" width="165" height="15" fontName="Times New Roman" fontSize="12.0">
<element kind="textField" uuid="1511f53a-b282-41b1-b233-612b6106c07a" x="56" y="67" width="194" height="15" fontName="Times New Roman" fontSize="12.0">
<expression><![CDATA[$P{patientName}]]></expression>
</element>
<element kind="textField" uuid="5bdac267-523d-4931-926b-76233ce9b84d" x="60" y="83" width="70" height="15" fontName="Times New Roman" fontSize="12.0">
@@ -89,7 +89,7 @@
<element kind="textField" uuid="3b8ac40e-85aa-4ab8-8445-64bcddb8b267" x="50" y="101" width="350" height="15" fontName="Times New Roman" fontSize="12.0">
<expression><![CDATA[$P{patientAddress}]]></expression>
</element>
<element kind="textField" uuid="db40cdb0-2850-4d66-8430-addc54f90321" x="301" y="67" width="128" height="15" fontName="Times New Roman" fontSize="12.0">
<element kind="textField" uuid="db40cdb0-2850-4d66-8430-addc54f90321" x="326" y="67" width="180" height="15" fontName="Times New Roman" fontSize="12.0">
<expression><![CDATA[$P{doctorName}]]></expression>
</element>
<element kind="textField" uuid="eb9cf54a-3b50-43c5-8696-cfc1e21bddfe" x="290" y="83" width="110" height="15" fontName="Times New Roman" fontSize="12.0">
@@ -107,9 +107,6 @@
<element kind="textField" uuid="6d652a25-b701-45bd-91ba-750842650004" x="0" y="177" width="400" height="421" markup="rtf" fontName="Times New Roman" fontSize="16.0">
<expression><![CDATA[$P{checkupNote}]]></expression>
</element>
<element kind="staticText" uuid="8f720f6a-0257-4556-b3bc-3b81ad4f8099" x="297" y="150" width="59" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
<text><![CDATA[Chiều cao:]]></text>
</element>
<element kind="staticText" uuid="a57e7a9d-4ac7-4219-b268-cd08ce943327" x="123" y="150" width="59" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
<text><![CDATA[Cân nặng:]]></text>
</element>
@@ -119,9 +116,6 @@
<element kind="staticText" uuid="c1356370-11ce-4b68-97d6-ce6e18e306c5" x="0" y="150" width="59" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
<text><![CDATA[Huyết áp:]]></text>
</element>
<element kind="textField" uuid="3e1069a6-daea-45c2-b21f-935b49baba45" x="352" y="150" width="53" height="16" fontName="Times New Roman" fontSize="12.0">
<expression><![CDATA[$P{checkupHeight} +" cm"]]></expression>
</element>
<element kind="textField" uuid="fb080012-3196-4535-bcd2-e87274c66fa5" x="178" y="150" width="45" height="16" fontName="Times New Roman" fontSize="12.0">
<expression><![CDATA[$P{checkupWeight} + " kg"]]></expression>
</element>
@@ -191,7 +185,7 @@
<element kind="textField" uuid="4d8aeb33-ab41-4a5b-a5d1-52bab4043246" x="48" y="155" width="150" height="15" fontName="Times New Roman" fontSize="12.0">
<expression><![CDATA[$P{reCheckupDate}]]></expression>
</element>
<element kind="staticText" uuid="f1223127-533c-4ecf-a950-7324b8aae0fa" x="0" y="170" width="196" height="15" fontName="Times New Roman" fontSize="12.0" italic="true" bold="true" hTextAlign="Left">
<element kind="staticText" uuid="f1223127-533c-4ecf-a950-7324b8aae0fa" x="0" y="170" width="196" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" italic="true" hTextAlign="Left">
<text><![CDATA[Lưu ý: Mang phiếu khi tái khám]]></text>
</element>
</summary>