[update] Introduce shift management in the application by adding currentShift variable in LocalStorage. Update various components including CheckUpPage, DashboardPage, and NavBar to utilize shift information for queue and counter requests. Modify database request handling in ServerHandler to accommodate shift-based queries and responses. Update binary files for database to reflect changes.

This commit is contained in:
Lê Thành Đạt committed 2025-10-14 15:40:40 -04:00
1 parent 6939c02763
commit 8dac9f2a1b
19 files changed
+237 -158

No files matched your search

Binary file not shown.
Binary file not shown.
@@ -24,6 +24,7 @@ public class LocalStorage {
public static String pathToProject = "";
public static int currentMaxQueueNumber = 0;
public static int currentShift = 0; // 0 for morning, 1 for afternoon
public static String dataDialogSearchTerm = "";
public static Date dataDialogFromDate = new Date();
public static Date dataDialogToDate = new Date();
@@ -291,7 +291,7 @@ public class CheckUpPage extends JPanel {
public void updateQueue() {
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetCheckUpQueueRequest());
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetCheckUpQueueRequest(LocalStorage.currentShift));
}
@@ -300,7 +300,7 @@ public class CheckUpPage extends JPanel {
}
public void updateUpdateQueue() {
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetCheckUpQueueUpdateRequest());
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetCheckUpQueueUpdateRequest(LocalStorage.currentShift));
}
private String[][] preprocessPatientDataForTable(List<Patient> patients) {
@@ -602,8 +602,6 @@ public class CheckUpPage extends JPanel {
driveButton.setBackground(new Color(26, 115, 232)); // Google Blue color
driveButton.setForeground(Color.WHITE);
driveButton.setFocusPainted(false);
driveButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
driveButton.setEnabled(false); // Disabled by default
driveButton.setBorder(BorderFactory.createEmptyBorder(8, 15, 8, 15));
// Load and set the icon
@@ -4851,7 +4849,8 @@ public class CheckUpPage extends JPanel {
(Integer) patientHeartRateSpinner.getValue(),
bloodPressureSystolicSpinner.getValue() + "/" + bloodPressureDiastolicSpinner.getValue(),
medicinePrescription,
servicePrescription
servicePrescription,
LocalStorage.currentShift
);
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request);
saved = true;
@@ -53,7 +53,12 @@ public class DashboardPage extends JPanel {
private void handleGetCheckUpQueueUpdateResponse(GetCheckUpQueueUpdateResponse response) {
SwingUtilities.invokeLater(() -> {
log.info("Received dashboard queue update from server.");
if (response.getShift() != LocalStorage.currentShift) {
log.info("Ignored dashboard queue update for different shift. Current: {}, Received: {}", LocalStorage.currentShift, response.getShift());
return;
}
log.info("Received dashboard queue update from server for shift {}.", response.getShift());
this.patientQueue.clear();
if (response.getQueue() != null) {
for (String[] patientData : response.getQueue()) {
@@ -128,7 +133,7 @@ public class DashboardPage extends JPanel {
ClientHandler.addResponseListener(TodayPatientCountResponse.class, todayPatientCountListener);
ClientHandler.addResponseListener(RecheckCountResponse.class, recheckCountListener);
log.info("DashboardPage listeners registered. Requesting initial data.");
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetCheckUpQueueUpdateRequest());
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetCheckUpQueueUpdateRequest(LocalStorage.currentShift));
}
private void startRealTimeClock() {
@@ -78,6 +78,21 @@ public class LoginPage extends JPanel {
gbc.anchor = GridBagConstraints.WEST;
JPasswordField passwordField = new JPasswordField(20);
formPanel.add(passwordField, gbc);
gbc.gridx = 0;
gbc.gridy++;
gbc.anchor = GridBagConstraints.EAST;
JLabel shiftLabel = new JLabel("Chọn ca:");
shiftLabel.setForeground(Color.WHITE);
shiftLabel.setFont(new Font("Arial", Font.PLAIN, 14));
formPanel.add(shiftLabel, gbc);
gbc.gridx = 1;
gbc.anchor = GridBagConstraints.WEST;
String[] shifts = {"Chọn ca", "Ca sáng", "Ca chiều"};
JComboBox<String> shiftComboBox = new JComboBox<>(shifts);
formPanel.add(shiftComboBox, gbc);
// --- BUTTON PANEL START ---
gbc.gridx = 0;
@@ -120,10 +135,15 @@ public class LoginPage extends JPanel {
passwordField.addActionListener(e -> loginButton.doClick());
loginButton.addActionListener(e -> {
if (shiftComboBox.getSelectedIndex() == 0) {
JOptionPane.showMessageDialog(this, "Vui lòng chọn ca làm việc.", "Lỗi", JOptionPane.ERROR_MESSAGE);
return;
}
char[] passwordChars = passwordField.getPassword();
String password = new String(passwordChars);
String username = loginField.getText();
var loginRequest = new LoginRequest(username, password);
int shift = shiftComboBox.getSelectedIndex() - 1;
var loginRequest = new LoginRequest(username, password, shift);
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), loginRequest);
});
@@ -131,6 +151,7 @@ public class LoginPage extends JPanel {
log.info("Login successful, UserId: {}, Role: {}", response.getUserId(), response.getRole());
LocalStorage.username = loginField.getText();
LocalStorage.userId = response.getUserId();
LocalStorage.currentShift = shiftComboBox.getSelectedIndex() - 1; // 0 for morning, 1 for afternoon
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new ClinicInfoRequest());
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetDoctorGeneralInfo());
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetProvinceRequest());
@@ -644,8 +644,8 @@ public class AddDialog extends JDialog {
// Send -1 to let the server know to auto-increment the queue number
queueNumber = -1;
}
log.info("Sending AddCheckupRequest with queue number: {}", queueNumber);
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new AddCheckupRequest(patientId, doctorId, LocalStorage.userId, selectedCheckupType, "CHỜ KHÁM", queueNumber));
log.info("Sending AddCheckupRequest with queue number: {} for shift {}", queueNumber, LocalStorage.currentShift);
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new AddCheckupRequest(patientId, doctorId, LocalStorage.userId, selectedCheckupType, "CHỜ KHÁM", queueNumber, LocalStorage.currentShift));
});
checkupInfoPanel.add(saveButton, gbc);
@@ -1105,7 +1105,7 @@ private void setupDebounceSearch() {
// Now that the JOptionPane is closed, the connection is still alive.
// This packet will be sent successfully.
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetCheckUpQueueUpdateRequest());
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetCheckUpQueueUpdateRequest(LocalStorage.currentShift));
patientIdField.setText(String.valueOf(response.getPatientId()));
addPatientButton.setEnabled(false); // Disable add patient button
@@ -31,6 +31,7 @@ import java.awt.event.MouseEvent;
import java.awt.event.ActionListener;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Calendar;
@Slf4j
public class NavBar extends JPanel {
@@ -41,6 +42,7 @@ public class NavBar extends JPanel {
private RoundedPanel messageButtonPanel; // NEW: Reference to message button panel for repainting
private JSpinner counterSpinner; // NEW: Spinner for current number
private boolean isUpdatingSpinner = false; // Flag to prevent recursive updates
private int currentShift = 0; // 0 for morning, 1 for afternoon
// Listener for incoming emergency alerts
private final ResponseListener<EmergencyResponse> emergencyResponseListener = this::handleEmergencyResponse;
@@ -285,7 +287,9 @@ public class NavBar extends JPanel {
this.add(Box.createHorizontalGlue());
this.add(userPanel);
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetCounterRequest());
// Determine current shift and request counter
currentShift = LocalStorage.currentShift;
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetCounterRequest(currentShift));
}
private int findNavItemIndex(String text, String[] items) {
@@ -297,7 +301,7 @@ public class NavBar extends JPanel {
// NEW: Create counter spinner component
private Component createCounterSpinner() {
JPanel counterPanel = new JPanel(new BorderLayout(5, 0));
JPanel counterPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0));
counterPanel.setBackground(this.getBackground());
counterPanel.setAlignmentY(Component.CENTER_ALIGNMENT);
@@ -309,7 +313,7 @@ public class NavBar extends JPanel {
// Create spinner with initial value of 1, minimum 1, maximum 999, step 1
SpinnerNumberModel spinnerModel = new SpinnerNumberModel(1, 1, 999, 1);
counterSpinner = new JSpinner(spinnerModel);
counterSpinner.setPreferredSize(new Dimension(80, 30));
counterSpinner.setPreferredSize(new Dimension(60, 30));
counterSpinner.setFont(new Font("Arial", Font.BOLD, 14));
counterSpinner.setEnabled(false);
// Style the spinner
@@ -330,13 +334,21 @@ public class NavBar extends JPanel {
log.info("Counter spinner changed to: {}", newValue);
// Send SetCounterRequest to server
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new SetCounterRequest(newValue));
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new SetCounterRequest(newValue, currentShift));
}
}
});
counterPanel.add(counterLabel, BorderLayout.WEST);
counterPanel.add(counterSpinner, BorderLayout.EAST);
counterPanel.add(counterLabel);
counterPanel.add(counterSpinner);
String shiftText = (LocalStorage.currentShift == 0) ? "Ca sáng" : "Ca chiều";
JLabel shiftLabel = new JLabel(shiftText);
shiftLabel.setFont(new Font("Arial", Font.BOLD, 12));
shiftLabel.setForeground(new Color(60, 60, 60));
counterPanel.add(new JLabel("Ca:"));
counterPanel.add(shiftLabel);
return counterPanel;
}
@@ -344,16 +356,21 @@ public class NavBar extends JPanel {
// NEW: Handle SetCounterResponse
private void handleSetCounterResponse(SetCounterResponse response) {
SwingUtilities.invokeLater(() -> {
log.info("Received SetCounterResponse with value: {}", response.getCounter());
LocalStorage.currentMaxQueueNumber = response.getCounter();
// Update spinner value without triggering change event
isUpdatingSpinner = true;
try {
counterSpinner.setValue(response.getCounter());
} catch (Exception e) {
log.error("Error updating counter spinner: {}", e.getMessage());
} finally {
isUpdatingSpinner = false;
// Only update if the response is for the currently selected shift
if (response.getShift() == currentShift) {
log.info("Received SetCounterResponse with value: {} for shift {}", response.getCounter(), response.getShift());
LocalStorage.currentMaxQueueNumber = response.getCounter();
// Update spinner value without triggering change event
isUpdatingSpinner = true;
try {
counterSpinner.setValue(response.getCounter());
} catch (Exception e) {
log.error("Error updating counter spinner: {}", e.getMessage());
} finally {
isUpdatingSpinner = false;
}
} else {
log.info("Ignored SetCounterResponse for different shift. Current: {}, Received: {}", currentShift, response.getShift());
}
});
}
@@ -705,17 +722,21 @@ public class NavBar extends JPanel {
private void handleGetCounterResponse(GetCounterResponse response) {
SwingUtilities.invokeLater(() -> {
log.info("Received GetCounterResponse with initial value: {}", response.getCounter());
LocalStorage.currentMaxQueueNumber = response.getCounter();
// Update spinner value without triggering change event
isUpdatingSpinner = true;
try {
counterSpinner.setValue(response.getCounter());
log.info("Counter spinner initialized to: {}", response.getCounter());
} catch (Exception e) {
log.error("Error initializing counter spinner: {}", e.getMessage());
} finally {
isUpdatingSpinner = false;
if (response.getShift() == currentShift) {
log.info("Received GetCounterResponse with initial value: {} for shift {}", response.getCounter(), response.getShift());
LocalStorage.currentMaxQueueNumber = response.getCounter();
// Update spinner value without triggering change event
isUpdatingSpinner = true;
try {
counterSpinner.setValue(response.getCounter());
log.info("Counter spinner initialized to: {} for shift {}", response.getCounter(), response.getShift());
} catch (Exception e) {
log.error("Error initializing counter spinner: {}", e.getMessage());
} finally {
isUpdatingSpinner = false;
}
} else {
log.info("Ignored GetCounterResponse for different shift. Current: {}, Received: {}", currentShift, response.getShift());
}
});
}
@@ -14,4 +14,5 @@ public class AddCheckupRequest implements Packet {
private String checkupType;
private String status;
private int queueNumber;
private int shift; // 0 for morning, 1 for afternoon
}
@@ -6,9 +6,11 @@ import lombok.Data;
@Data
public class DeleteCheckupRequest implements Packet {
private long checkupId;
private int shift;
public DeleteCheckupRequest(long checkupId) {
public DeleteCheckupRequest(long checkupId, int shift) {
this.checkupId = checkupId;
this.shift = shift;
}
}
@@ -1,5 +1,11 @@
package BsK.common.packet.req;
import BsK.common.packet.Packet;
import lombok.AllArgsConstructor;
import lombok.Data;
public class GetCheckUpQueueRequest implements Packet {}
@AllArgsConstructor
@Data
public class GetCheckUpQueueRequest implements Packet {
private int shift;
}
@@ -1,6 +1,11 @@
package BsK.common.packet.req;
import BsK.common.packet.Packet;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public class GetCheckUpQueueUpdateRequest implements Packet {
private int shift;
}
@@ -2,7 +2,10 @@ package BsK.common.packet.req;
import BsK.common.packet.Packet;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
public class GetCounterRequest implements Packet{
@Getter
public class GetCounterRequest implements Packet {
private int shift; // 0 for morning, 1 for afternoon
}
@@ -40,4 +40,5 @@ public class SaveCheckupRequest implements Packet {
// Prescriptions
String[][] medicinePrescription;
String[][] servicePrescription;
int shift;
}
@@ -8,4 +8,5 @@ import lombok.Data;
@AllArgsConstructor
public class SetCounterRequest implements Packet {
private int counter;
private int shift; // 0 for morning, 1 for afternoon
}
@@ -8,4 +8,5 @@ import lombok.Data;
@Data
public class GetCheckUpQueueUpdateResponse implements Packet {
private String[][] queue;
private int shift;
}
@@ -8,4 +8,5 @@ import lombok.Data;
@Data
public class GetCounterResponse implements Packet{
private int counter;
private int shift;
}
@@ -8,4 +8,5 @@ import lombok.Data;
@AllArgsConstructor
public class SetCounterResponse implements Packet{
private int counter;
private int shift; // 0 for morning, 1 for afternoon
}
@@ -119,9 +119,9 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
return;
}
if (packet instanceof GetCheckUpQueueRequest) {
if (packet instanceof GetCheckUpQueueRequest getCheckUpQueueRequest) {
log.debug("Received GetCheckUpQueueRequest");
int shift = getCheckUpQueueRequest.getShift();
String sql = """
SELECT
a.checkup_id, a.checkup_date, c.customer_last_name, c.customer_first_name,
@@ -137,11 +137,12 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
JOIN
Doctor D ON a.doctor_id = D.doctor_id
WHERE
date(a.checkup_date / 1000, 'unixepoch', '+7 hours') = date('now', '+7 hours')""";
date(a.checkup_date / 1000, 'unixepoch', '+7 hours') = date('now', '+7 hours') AND a.shift = ?""";
try (Connection conn = DatabaseManager.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery()) {
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, shift);
ResultSet rs = stmt.executeQuery();
if (!rs.isBeforeFirst()) {
log.warn("No data found in the checkup table for today.");
@@ -209,9 +210,9 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
}
}
if (packet instanceof GetCheckUpQueueUpdateRequest) {
if (packet instanceof GetCheckUpQueueUpdateRequest getCheckUpQueueUpdateRequest) {
log.debug("Received GetCheckUpQueueUpdateRequest");
broadcastQueueUpdate();
broadcastQueueUpdate(getCheckUpQueueUpdateRequest.getShift());
}
if (packet instanceof GetDoctorGeneralInfo) {
@@ -627,6 +628,7 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
try (Connection conn = DatabaseManager.getConnection()) {
int generatedCheckupId = 0;
int queueNumber = addCheckupRequest.getQueueNumber();
int shift = addCheckupRequest.getShift();
boolean shouldBroadcastCounter = false;
try {
// 1. Bắt đầu một transaction trên kết nối CỤC BỘ (conn) này
@@ -635,14 +637,15 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
// 2. LẤY SỐ THỨ TỰ (QUEUE NUMBER)
if (queueNumber == -1) { // if queue number is -1, means the queue number is not provided, so we need to get the queue number from the database
String queueSql = """
INSERT INTO DailyQueueCounter (date, current_count)
VALUES (date('now', 'localtime'), 1)
ON CONFLICT(date) DO UPDATE SET current_count = current_count + 1
INSERT INTO DailyQueueCounter (date, shift, current_count)
VALUES (date('now', 'localtime'), ?, 1)
ON CONFLICT(date, shift) DO UPDATE SET current_count = current_count + 1
RETURNING current_count
""";
try (PreparedStatement queueStmt = conn.prepareStatement(queueSql);
ResultSet rs = queueStmt.executeQuery()) {
try (PreparedStatement queueStmt = conn.prepareStatement(queueSql)) {
queueStmt.setInt(1, shift);
ResultSet rs = queueStmt.executeQuery();
if (rs.next()) {
queueNumber = rs.getInt(1);
shouldBroadcastCounter = true; // Auto-increment always gives new max
@@ -652,8 +655,10 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
}
} else { // Custom queue number provided
int currentMax = 0;
String getMaxSql = "SELECT current_count FROM DailyQueueCounter WHERE date = date('now', 'localtime')";
try (PreparedStatement stmt = conn.prepareStatement(getMaxSql); ResultSet rs = stmt.executeQuery()) {
String getMaxSql = "SELECT current_count FROM DailyQueueCounter WHERE date = date('now', 'localtime') AND shift = ?";
try (PreparedStatement stmt = conn.prepareStatement(getMaxSql)) {
stmt.setInt(1, shift);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
currentMax = rs.getInt(1);
}
@@ -661,13 +666,14 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
if (queueNumber > currentMax) {
String upsertSql = """
INSERT INTO DailyQueueCounter (date, current_count)
VALUES (date('now', 'localtime'), ?)
ON CONFLICT(date) DO UPDATE SET current_count = ?
INSERT INTO DailyQueueCounter (date, shift, current_count)
VALUES (date('now', 'localtime'), ?, ?)
ON CONFLICT(date, shift) DO UPDATE SET current_count = ?
""";
try (PreparedStatement stmt = conn.prepareStatement(upsertSql)) {
stmt.setInt(1, queueNumber);
stmt.setInt(1, shift);
stmt.setInt(2, queueNumber);
stmt.setInt(3, queueNumber);
stmt.executeUpdate();
}
shouldBroadcastCounter = true;
@@ -675,13 +681,14 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
}
// 3. CHÈN VÀO BẢNG CHECKUP VÀ LẤY LẠI CHECKUP_ID
String checkupSql = "INSERT INTO Checkup (customer_id, doctor_id, checkup_type, status, queue_number) VALUES (?, ?, ?, ?, ?)";
String checkupSql = "INSERT INTO Checkup (customer_id, doctor_id, checkup_type, status, queue_number, shift) VALUES (?, ?, ?, ?, ?, ?)";
try (PreparedStatement checkupStmt = conn.prepareStatement(checkupSql, PreparedStatement.RETURN_GENERATED_KEYS)) {
checkupStmt.setInt(1, addCheckupRequest.getCustomerId());
checkupStmt.setInt(2, addCheckupRequest.getDoctorId());
checkupStmt.setString(3, addCheckupRequest.getCheckupType());
checkupStmt.setString(4, addCheckupRequest.getStatus());
checkupStmt.setInt(5, queueNumber);
checkupStmt.setInt(6, shift);
checkupStmt.executeUpdate();
try (ResultSet generatedKeys = checkupStmt.getGeneratedKeys()) {
@@ -724,11 +731,11 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
// Gửi phản hồi thành công và broadcast cập nhật
UserUtil.sendPacket(currentUser.getSessionId(), new AddCheckupResponse(true, "Thêm bệnh nhân thành công", queueNumber));
broadcastQueueUpdate();
broadcastQueueUpdate(shift);
if (shouldBroadcastCounter) { // Nếu STT không chỉ định, thì không cần gửi cho các client
int maxCurId = SessionManager.getMaxSessionId();
for (int sessionId = 1; sessionId <= maxCurId; sessionId++) {
UserUtil.sendPacket(sessionId, new SetCounterResponse(queueNumber));
UserUtil.sendPacket(sessionId, new SetCounterResponse(queueNumber, shift));
}
}
// Tạo thư mục Google Drive bất đồng bộ
@@ -755,11 +762,14 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
if (packet instanceof SetCounterRequest setCounterRequest) {
log.debug("Received SetCounterRequest to set counter to {}", setCounterRequest.getCounter());
int shift = setCounterRequest.getShift();
try (Connection conn = DatabaseManager.getConnection()) {
int currentMax = 0;
String getMaxSql = "SELECT current_count FROM DailyQueueCounter WHERE date = date('now', 'localtime')";
try (PreparedStatement stmt = conn.prepareStatement(getMaxSql); ResultSet rs = stmt.executeQuery()) {
String getMaxSql = "SELECT current_count FROM DailyQueueCounter WHERE date = date('now', 'localtime') AND shift = ?";
try (PreparedStatement stmt = conn.prepareStatement(getMaxSql)) {
stmt.setInt(1, shift);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
currentMax = rs.getInt(1);
}
@@ -768,19 +778,20 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
int newCounter = setCounterRequest.getCounter();
if (newCounter > currentMax) {
String upsertSql = """
INSERT INTO DailyQueueCounter (date, current_count)
VALUES (date('now', 'localtime'), ?)
ON CONFLICT(date) DO UPDATE SET current_count = ?
INSERT INTO DailyQueueCounter (date, shift, current_count)
VALUES (date('now', 'localtime'), ?, ?)
ON CONFLICT(date, shift) DO UPDATE SET current_count = ?
""";
try (PreparedStatement stmt = conn.prepareStatement(upsertSql)) {
stmt.setInt(1, newCounter);
stmt.setInt(1, shift);
stmt.setInt(2, newCounter);
stmt.setInt(3, newCounter);
stmt.executeUpdate();
}
int maxCurId = SessionManager.getMaxSessionId();
for (int sessionId = 1; sessionId <= maxCurId; sessionId++) {
UserUtil.sendPacket(sessionId, new SetCounterResponse(newCounter));
UserUtil.sendPacket(sessionId, new SetCounterResponse(newCounter, shift));
}
}
} catch (SQLException e) {
@@ -791,13 +802,18 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
if (packet instanceof GetCounterRequest getCounterRequest) {
log.debug("Received GetCounterRequest");
String sql = "SELECT current_count FROM DailyQueueCounter WHERE date = date('now', 'localtime');";
int shift = getCounterRequest.getShift();
String sql = "SELECT current_count FROM DailyQueueCounter WHERE date = date('now', 'localtime') AND shift = ?;";
try (Connection conn = DatabaseManager.getConnection()) {
try (PreparedStatement preparedStatement = conn.prepareStatement(sql)) {
preparedStatement.setInt(1, shift);
ResultSet rs = preparedStatement.executeQuery();
if (rs.next()) {
int counter = rs.getInt(1);
UserUtil.sendPacket(currentUser.getSessionId(), new GetCounterResponse(counter));
UserUtil.sendPacket(currentUser.getSessionId(), new GetCounterResponse(counter, shift));
} else {
// No counter for this shift today, so send 0
UserUtil.sendPacket(currentUser.getSessionId(), new GetCounterResponse(0, shift));
}
}
} catch (SQLException e) {
@@ -970,7 +986,7 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
// 6. Hoàn tất và COMMIT giao dịch
conn.commit();
log.info("Successfully saved checkup transaction for checkup_id: {}", saveCheckupRequest.getCheckupId());
broadcastQueueUpdate();
broadcastQueueUpdate(saveCheckupRequest.getShift());
UserUtil.sendPacket(currentUser.getSessionId(), new SaveCheckupRes(true, "Lưu thông tin khám bệnh thành công."));
} catch (SQLException e) {
@@ -1374,7 +1390,7 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
// 5. If all statements executed without error, commit the transaction
connection.commit();
log.info("Successfully deleted checkup " + checkupId + " and all associated data.");
broadcastQueueUpdate();
broadcastQueueUpdate(deleteCheckupRequest.getShift());
UserUtil.sendPacket(currentUser.getSessionId(), new DeleteCheckupResponse(true, "Xóa phiếu khám thành công."));
} catch (SQLException e) {
@@ -2643,87 +2659,85 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
}
private void broadcastQueueUpdate() {
// SỬA ĐỔI: Sử dụng một kết nối duy nhất, an toàn cho tất cả các truy vấn bên trong
try (Connection conn = DatabaseManager.getConnection()) {
private void broadcastQueueUpdate(int shift) {
String sql = """
SELECT
a.checkup_id, a.checkup_date, c.customer_last_name, c.customer_first_name,
d.doctor_first_name, d.doctor_last_name, a.suggestion, a.diagnosis, a.notes,
a.status, a.customer_id, c.customer_number, c.customer_address,
a.customer_weight, a.customer_height, c.customer_gender, c.customer_dob,
a.checkup_type, a.conclusion, a.reCheckupDate, c.cccd_ddcn, a.heart_beat,
a.blood_pressure, c.drive_url, a.doctor_ultrasound_id, a.queue_number
FROM
checkup AS a
JOIN
customer AS c ON a.customer_id = c.customer_id
JOIN
Doctor D ON a.doctor_id = D.doctor_id
WHERE
date(a.checkup_date / 1000, 'unixepoch', '+7 hours') = date('now', '+7 hours') AND a.shift = ?""";
try (Connection conn = DatabaseManager.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, shift);
ResultSet rs = stmt.executeQuery();
// --- LOGIC GỐC CHO HÀNG CHỜ (KHÔNG THAY ĐỔI THUẬT TOÁN) ---
String[][] resultArray;
String queueSql = """
SELECT
a.checkup_id, a.checkup_date, c.customer_last_name, c.customer_first_name,
d.doctor_first_name, d.doctor_last_name, a.suggestion, a.diagnosis, a.notes,
a.status, a.customer_id, c.customer_number, c.customer_address,
a.customer_weight, a.customer_height, c.customer_gender, c.customer_dob,
a.checkup_type, a.conclusion, a.reCheckupDate, c.cccd_ddcn, a.heart_beat,
a.blood_pressure, c.drive_url, a.doctor_ultrasound_id, a.queue_number
FROM
checkup AS a
JOIN
customer AS c ON a.customer_id = c.customer_id
JOIN
Doctor D ON a.doctor_id = D.doctor_id
WHERE
date(a.checkup_date / 1000, 'unixepoch', '+7 hours') = date('now', '+7 hours')
""";
// SỬA ĐỔI: Thay thế statement.executeQuery bằng PreparedStatement an toàn
try (PreparedStatement stmt = conn.prepareStatement(queueSql);
ResultSet rs = stmt.executeQuery()) {
if (!rs.isBeforeFirst()) {
System.out.println("No data found in the checkup table.");
resultArray = new String[0][0];
} else {
ArrayList<String> resultList = new ArrayList<>();
while (rs.next()) {
String checkupId = rs.getString("checkup_id");
String checkupDate = rs.getString("checkup_date");
long checkupDateLong = Long.parseLong(checkupDate);
Timestamp timestamp = new Timestamp(checkupDateLong);
Date date = new Date(timestamp.getTime());
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
checkupDate = sdf.format(date);
String customerLastName = rs.getString("customer_last_name");
String customerFirstName = rs.getString("customer_first_name");
String doctorFirstName = rs.getString("doctor_first_name");
String doctorLastName = rs.getString("doctor_last_name");
String suggestion = rs.getString("suggestion");
String diagnosis = rs.getString("diagnosis");
String notes = rs.getString("notes");
String status = rs.getString("status");
String customerId = rs.getString("customer_id");
String customerNumber = rs.getString("customer_number");
String customerAddress = rs.getString("customer_address");
String customerWeight = rs.getString("customer_weight");
String customerHeight = rs.getString("customer_height");
String customerGender = rs.getString("customer_gender");
String customerDob = rs.getString("customer_dob");
String checkupType = rs.getString("checkup_type");
String conclusion = rs.getString("conclusion");
String reCheckupDate = rs.getString("reCheckupDate");
String cccdDdcn = rs.getString("cccd_ddcn");
String heartBeat = rs.getString("heart_beat");
String bloodPressure = rs.getString("blood_pressure");
String driveUrl = rs.getString("drive_url");
String doctorUltrasoundId = rs.getString("doctor_ultrasound_id");
String queueNumber = String.format("%02d", rs.getInt("queue_number"));
if (driveUrl == null) { driveUrl = ""; }
String result = String.join("|", checkupId, checkupDate, customerLastName, customerFirstName,
doctorLastName + " " + doctorFirstName, suggestion, diagnosis, notes, status, customerId, customerNumber, customerAddress, customerWeight, customerHeight,
customerGender, customerDob, checkupType, conclusion, reCheckupDate, cccdDdcn, heartBeat, bloodPressure,
driveUrl, doctorUltrasoundId, queueNumber
);
resultList.add(result);
}
String[] resultString = resultList.toArray(new String[0]);
resultArray = new String[resultString.length][];
for (int i = 0; i < resultString.length; i++) {
resultArray[i] = resultString[i].split("\\|");
if (!rs.isBeforeFirst()) {
log.warn("No data found in the checkup table for today's shift {}.", shift);
resultArray = new String[0][0];
} else {
// ... (rest of the logic is the same)
ArrayList<String> resultList = new ArrayList<>();
while (rs.next()) {
String checkupId = rs.getString("checkup_id");
String checkupDate = rs.getString("checkup_date");
long checkupDateLong = Long.parseLong(checkupDate);
Timestamp timestamp = new Timestamp(checkupDateLong);
Date date = new Date(timestamp.getTime());
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
checkupDate = sdf.format(date);
String customerLastName = rs.getString("customer_last_name");
String customerFirstName = rs.getString("customer_first_name");
String doctorFirstName = rs.getString("doctor_first_name");
String doctorLastName = rs.getString("doctor_last_name");
String suggestion = rs.getString("suggestion");
String diagnosis = rs.getString("diagnosis");
String notes = rs.getString("notes");
String status = rs.getString("status");
String customerId = rs.getString("customer_id");
String customerNumber = rs.getString("customer_number");
String customerAddress = rs.getString("customer_address");
String customerWeight = rs.getString("customer_weight");
String customerHeight = rs.getString("customer_height");
String customerGender = rs.getString("customer_gender");
String customerDob = rs.getString("customer_dob");
String checkupType = rs.getString("checkup_type");
String conclusion = rs.getString("conclusion");
String reCheckupDate = rs.getString("reCheckupDate");
String cccdDdcn = rs.getString("cccd_ddcn");
String heartBeat = rs.getString("heart_beat");
String bloodPressure = rs.getString("blood_pressure");
String driveUrl = rs.getString("drive_url");
String doctorUltrasoundId = rs.getString("doctor_ultrasound_id");
String queueNumber = String.format("%02d", rs.getInt("queue_number"));
if (driveUrl == null) {
driveUrl = "";
}
String result = String.join("|", checkupId, checkupDate, customerLastName, customerFirstName,
doctorLastName + " " + doctorFirstName, suggestion, diagnosis, notes, status, customerId, customerNumber, customerAddress, customerWeight, customerHeight,
customerGender, customerDob, checkupType, conclusion, reCheckupDate, cccdDdcn, heartBeat, bloodPressure,
driveUrl, doctorUltrasoundId, queueNumber
);
resultList.add(result);
}
String[] resultString = resultList.toArray(new String[0]);
resultArray = new String[resultString.length][];
for (int i = 0; i < resultString.length; i++) {
resultArray[i] = resultString[i].split("\\|");
}
}
// --- LOGIC GỐC ĐẾM SỐ BỆNH NHÂN TRONG NGÀY ---
ZoneId zoneId = ZoneId.of("Asia/Ho_Chi_Minh");
LocalDate today = LocalDate.now(zoneId);
@@ -2733,7 +2747,6 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
int totalPatientsToday = 0;
String countQuery = "SELECT COUNT(DISTINCT a.customer_id) as patient_count FROM checkup as a WHERE a.checkup_date >= ? AND a.checkup_date <= ?";
// SỬA ĐỔI: Thay thế String.format và statement.executeQuery bằng PreparedStatement an toàn
try (PreparedStatement countStmt = conn.prepareStatement(countQuery)) {
countStmt.setLong(1, startOfTodayMillis);
countStmt.setLong(2, endOfTodayMillis);
@@ -2748,7 +2761,6 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
int totalRecheckToday = 0;
String recheckQuery = "SELECT COUNT(*) as recheck_count FROM checkup WHERE reCheckupDate >= ? AND reCheckupDate <= ?";
// SỬA ĐỔI: Thay thế String.format và statement.executeQuery bằng PreparedStatement an toàn
try (PreparedStatement recheckStmt = conn.prepareStatement(recheckQuery)) {
recheckStmt.setLong(1, startOfTodayMillis);
recheckStmt.setLong(2, endOfTodayMillis);
@@ -2758,11 +2770,10 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
}
}
}
// --- LOGIC GỐC GỬI GÓI TIN ---
int maxCurId = SessionManager.getMaxSessionId();
for (int sessionId = 1; sessionId <= maxCurId; sessionId++) {
UserUtil.sendPacket(sessionId, new GetCheckUpQueueUpdateResponse(resultArray));
UserUtil.sendPacket(sessionId, new GetCheckUpQueueUpdateResponse(resultArray, shift));
UserUtil.sendPacket(sessionId, new TodayPatientCountResponse(totalPatientsToday));
UserUtil.sendPacket(sessionId, new RecheckCountResponse(totalRecheckToday));
}
@@ -2771,9 +2782,8 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
log.info("send recheck count response to all clients");
} catch (SQLException e) {
// Giữ nguyên cách xử lý lỗi gốc
throw new RuntimeException(e);
log.error("Error during broadcastQueueUpdate", e);
}
}
}
}