diff --git a/database/BSK.db b/database/BSK.db
index 39b3566..22cc969 100644
Binary files a/database/BSK.db and b/database/BSK.db differ
diff --git a/database/BSK.db-shm b/database/BSK.db-shm
new file mode 100644
index 0000000..6cccc9a
Binary files /dev/null and b/database/BSK.db-shm differ
diff --git a/database/BSK.db-wal b/database/BSK.db-wal
new file mode 100644
index 0000000..75d3cd8
Binary files /dev/null and b/database/BSK.db-wal differ
diff --git a/image/checkup_media/3/ultrasound_3_20250804_120429_739.jpg b/image/checkup_media/3/ultrasound_3_20250804_120429_739.jpg
new file mode 100644
index 0000000..95403d9
Binary files /dev/null and b/image/checkup_media/3/ultrasound_3_20250804_120429_739.jpg differ
diff --git a/image/checkup_media/3/ultrasound_3_20250804_121459_607.jpg b/image/checkup_media/3/ultrasound_3_20250804_121459_607.jpg
new file mode 100644
index 0000000..772cc07
Binary files /dev/null and b/image/checkup_media/3/ultrasound_3_20250804_121459_607.jpg differ
diff --git a/image/checkup_media/4/IMG_4_20250804_134627.jpg b/image/checkup_media/4/IMG_4_20250804_134627.jpg
new file mode 100644
index 0000000..d241a9d
Binary files /dev/null and b/image/checkup_media/4/IMG_4_20250804_134627.jpg differ
diff --git a/image/checkup_media/4/IMG_4_20250804_134628.jpg b/image/checkup_media/4/IMG_4_20250804_134628.jpg
new file mode 100644
index 0000000..6a60a78
Binary files /dev/null and b/image/checkup_media/4/IMG_4_20250804_134628.jpg differ
diff --git a/image/checkup_media/4/IMG_4_20250804_134630.jpg b/image/checkup_media/4/IMG_4_20250804_134630.jpg
new file mode 100644
index 0000000..a27e117
Binary files /dev/null and b/image/checkup_media/4/IMG_4_20250804_134630.jpg differ
diff --git a/image/checkup_media/4/IMG_4_20250804_134631.jpg b/image/checkup_media/4/IMG_4_20250804_134631.jpg
new file mode 100644
index 0000000..29896f9
Binary files /dev/null and b/image/checkup_media/4/IMG_4_20250804_134631.jpg differ
diff --git a/pom.xml b/pom.xml
index 3154827..c1073c2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -236,5 +236,15 @@
poi-ooxml
5.2.5
+
+ com.zaxxer
+ HikariCP
+ 5.1.0
+
+
+ org.xerial
+ sqlite-jdbc
+ 3.45.1.0
+
\ No newline at end of file
diff --git a/src/main/java/BsK/client/Client.java b/src/main/java/BsK/client/Client.java
index e213a50..0b2117b 100644
--- a/src/main/java/BsK/client/Client.java
+++ b/src/main/java/BsK/client/Client.java
@@ -13,10 +13,8 @@ import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
-import io.netty.handler.timeout.IdleStateHandler;
import java.net.URI;
import java.net.URISyntaxException;
-import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import java.io.InputStream;
import java.util.Properties;
@@ -178,10 +176,8 @@ public class Client {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline()
- .addLast(new IdleStateHandler(60 * 30,
- 0, 0, TimeUnit.SECONDS))
.addLast(new HttpClientCodec())
- .addLast(new HttpObjectAggregator(50 * 1024 * 1024))
+ .addLast(new HttpObjectAggregator(10 * 1024 * 1024))
.addLast(
new WebSocketClientProtocolHandler(
finalUri,
@@ -189,7 +185,7 @@ public class Client {
null,
true,
new DefaultHttpHeaders(),
- 50 * 1024 * 1024))
+ 10 * 1024 * 1024))
.addLast("ws", ClientHandler.INSTANCE);
}
});
diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java
index 313630c..7200f7a 100644
--- a/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java
+++ b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java
@@ -226,7 +226,7 @@ public class CheckUpPage extends JPanel {
private final Map> uploadTimeoutTasks = new ConcurrentHashMap<>();
// Ultrasound folder monitoring
- private static final String ULTRASOUND_FOLDER_PATH = "ANH SIEU AM";
+ private static final String ULTRASOUND_FOLDER_PATH = LocalStorage.ULTRASOUND_FOLDER_PATH;
private WatchService watchService;
private ExecutorService folderWatchExecutor;
private volatile boolean isWatchingFolder = false;
@@ -1821,27 +1821,25 @@ public class CheckUpPage extends JPanel {
// First, show the print preview to the user
try {
medicineInvoice.showDirectJasperViewer();
+ medicineInvoice.generatePdfBytesAsync().thenAccept(pdfBytes -> {
+ if (pdfBytes != null && pdfBytes.length > 0) {
+ String checkupId = checkupIdField.getText();
+ String fileName = "medserinvoice.pdf";
+ String pdfType = "medserinvoice";
- // Then, asynchronously generate the PDF bytes and upload them
- // medicineInvoice.generatePdfBytesAsync().thenAccept(pdfBytes -> {
- // if (pdfBytes != null && pdfBytes.length > 0) {
- // String checkupId = checkupIdField.getText();
- // String fileName = "medserinvoice.pdf";
- // String pdfType = "medserinvoice";
+ log.info("Uploading {} ({}) for checkupId: {}", fileName, pdfType, checkupId);
- // log.info("Uploading {} ({}) for checkupId: {}", fileName, pdfType, checkupId);
-
- // UploadCheckupPdfRequest request = new UploadCheckupPdfRequest(checkupId, pdfBytes, fileName, pdfType);
- // NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request);
- // }
- // }).exceptionally(ex -> {
- // log.error("Failed to generate or upload medicine invoice PDF", ex);
- // // Show error in the UI thread
- // SwingUtilities.invokeLater(() ->
- // JOptionPane.showMessageDialog(this, "Lỗi khi tạo hoặc tải lên file PDF hóa đơn: " + ex.getMessage(), "Lỗi PDF", JOptionPane.ERROR_MESSAGE)
- // );
- // return null;
- // });
+ UploadCheckupPdfRequest request = new UploadCheckupPdfRequest(checkupId, pdfBytes, fileName, pdfType);
+ NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request);
+ }
+ }).exceptionally(ex -> {
+ log.error("Failed to generate or upload medicine invoice PDF", ex);
+ // Show error in the UI thread
+ SwingUtilities.invokeLater(() ->
+ JOptionPane.showMessageDialog(this, "Lỗi khi tạo hoặc tải lên file PDF hóa đơn: " + ex.getMessage(), "Lỗi PDF", JOptionPane.ERROR_MESSAGE)
+ );
+ return null;
+ });
} catch (Exception e) {
log.error("Failed to show direct JasperViewer", e);
JOptionPane.showMessageDialog(this, "Lỗi khi hiển thị hộp thoại in: " + e.getMessage(), "Lỗi In", JOptionPane.ERROR_MESSAGE);
@@ -2015,7 +2013,7 @@ public class CheckUpPage extends JPanel {
callingStatusLabel.setText(message);
callingStatusLabel.setBackground(new Color(230, 255, 230));
callingStatusLabel.setForeground(new Color(0, 100, 0));
- updateUpdateQueue();
+ // updateUpdateQueue();
// If patient is marked as "ĐÃ KHÁM", clear the selection and details immediately.
if ("ĐÃ KHÁM".equals(statusToSave)) {
@@ -3597,7 +3595,7 @@ public class CheckUpPage extends JPanel {
SwingUtilities.invokeLater(() -> {
try {
// Create media directory for this checkup if it doesn't exist
- java.io.File mediaDir = new java.io.File("image/checkup_media", response.getCheckupId());
+ java.io.File mediaDir = new java.io.File(LocalStorage.checkupMediaBaseDir, response.getCheckupId());
if (!mediaDir.exists()) {
mediaDir.mkdirs();
}
@@ -4426,7 +4424,7 @@ public class CheckUpPage extends JPanel {
// Find the patient in the patientQueue
if (checkupId == null) return -1;
for (int i = 0; i < queueTableModel.getRowCount(); i++) {
- if (checkupId.equals(queueTableModel.getValueAt(i, 0))) {
+ if (checkupId.equals(queueTableModel.getValueAt(i, 1))) {
return i;
}
}
@@ -4450,10 +4448,15 @@ public class CheckUpPage extends JPanel {
queueTable.setRowSelectionInterval(rowToSelect, rowToSelect);
queueTable.scrollRectToVisible(queueTable.getCellRect(rowToSelect, 0, true));
} else {
- // The previously selected patient is no longer in the queue (e.g., status changed to "ĐÃ KHÁM")
- // Clear the details panel to avoid confusion
- clearPatientDetails();
- // Auto-select first row if available
+ JOptionPane.showMessageDialog(
+ mainFrame,
+ "Trạng thái của bệnh nhân bạn đang chọn đã thay đổi và không còn trong hàng chờ.\n" +
+ "Giao diện của bạn sẽ được làm mới.",
+ "Cập Nhật Trạng Thái Bệnh Nhân",
+ JOptionPane.INFORMATION_MESSAGE
+ );
+
+ clearPatientDetails(); // Now clear the details
if (queueTable.getRowCount() > 0) {
queueTable.setRowSelectionInterval(0, 0);
}
diff --git a/src/main/java/BsK/common/Error.java b/src/main/java/BsK/common/Error.java
index 43164b5..f579245 100644
--- a/src/main/java/BsK/common/Error.java
+++ b/src/main/java/BsK/common/Error.java
@@ -5,7 +5,9 @@ public enum Error {
UNKNOWN,
USERNAME_NOT_FOUND,
PASSWORD_INCORRECT,
- USER_ALREADY_EXISTS,INVALID_CREDENTIALS,
+ USERNAME_EXISTS,
+ INVALID_CREDENTIALS,
SQL_EXCEPTION,
- ACCESS_DENIED
+ ACCESS_DENIED,
+ INVALID_INPUT
}
\ No newline at end of file
diff --git a/src/main/java/BsK/common/packet/res/ErrorResponse.java b/src/main/java/BsK/common/packet/res/ErrorResponse.java
index bd32144..239ae3c 100644
--- a/src/main/java/BsK/common/packet/res/ErrorResponse.java
+++ b/src/main/java/BsK/common/packet/res/ErrorResponse.java
@@ -27,12 +27,15 @@ public class ErrorResponse implements Packet {
if (error.equals(Error.PASSWORD_INCORRECT)) {
return "Mật khẩu không chính xác, vui lòng kiểm tra lại mật khẩu";
}
- if (error.equals(Error.USER_ALREADY_EXISTS)) {
- return "Tên đăng nhập đã tồn tại, vui lòng chọn tên đăng nhập khác";
- }
if (error.equals(Error.INVALID_CREDENTIALS)) {
return "Tên đăng nhập hoặc mật khẩu không chính xác, vui lòng kiểm tra lại";
}
+ if (error.equals(Error.INVALID_INPUT)) {
+ return "Dữ liệu nhập không hợp lệ, vui lòng kiểm tra lại";
+ }
+ if (error.equals(Error.USERNAME_EXISTS)) {
+ return "Tên đăng nhập đã tồn tại, vui lòng chọn tên đăng nhập khác";
+ }
return error.toString();
}
diff --git a/src/main/java/BsK/server/Server.java b/src/main/java/BsK/server/Server.java
index 6058cd6..814e5de 100644
--- a/src/main/java/BsK/server/Server.java
+++ b/src/main/java/BsK/server/Server.java
@@ -1,5 +1,6 @@
package BsK.server;
+import BsK.server.database.DatabaseManager;
import BsK.server.network.handler.ServerHandler;
import BsK.server.service.GoogleDriveServiceOAuth;
import io.netty.bootstrap.ServerBootstrap;
@@ -16,7 +17,6 @@ import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
-import io.netty.handler.timeout.IdleStateHandler;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
@@ -36,7 +36,7 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Server {
private static int PORT;
- public static Connection connection;
+ // public static Connection connection;
public static GoogleDriveServiceOAuth googleDriveService;
private static boolean googleDriveEnabled = true; // Can be configured
private static String googleDriveRootFolderName = "BSK_Clinic_Patient_Files"; // Default
@@ -116,8 +116,8 @@ public class Server {
log.info("Checkup media base dir: {}", checkupMediaBaseDir);
// Directly use the provided database path
- String dbPath = "database/BSK.db";
- connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath);
+ //String dbPath = "database/BSK.db";
+ //connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath);
// Initialize Google Drive service if enabled
initializeGoogleDriveService();
@@ -251,13 +251,14 @@ public class Server {
throw new UnsupportedOperationException("extractDatabaseFile is not used anymore. Database is read directly from the specified path.");
}
- public static Statement statement;
+ //public static Statement statement;
static {
try {
- statement = connection.createStatement();
- } catch (SQLException e) {
- throw new RuntimeException(e);
+ // KHỞI TẠO CONNECTION POOL
+ DatabaseManager.initialize();
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to initialize server", e);
}
}
@@ -294,11 +295,10 @@ public class Server {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
- .addLast(new IdleStateHandler(60 * 30, 0, 0, TimeUnit.SECONDS))
.addLast(new HttpServerCodec())
.addLast(new ChunkedWriteHandler())
- .addLast(new HttpObjectAggregator(50 * 1024 * 1024))
- .addLast(new WebSocketServerProtocolHandler("/", null, true, 50 * 1024 * 1024))
+ .addLast(new HttpObjectAggregator(10 * 1024 * 1024))
+ .addLast(new WebSocketServerProtocolHandler("/", null, true, 10 * 1024 * 1024))
.addLast(new ServerHandler());
}
});
diff --git a/src/main/java/BsK/server/database/DatabaseManager.java b/src/main/java/BsK/server/database/DatabaseManager.java
new file mode 100644
index 0000000..32a32ef
--- /dev/null
+++ b/src/main/java/BsK/server/database/DatabaseManager.java
@@ -0,0 +1,50 @@
+package BsK.server.database;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import lombok.extern.slf4j.Slf4j;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+@Slf4j
+public class DatabaseManager {
+
+ private static HikariDataSource dataSource;
+
+ public static void initialize() {
+ log.info("Initializing database connection pool...");
+ HikariConfig config = new HikariConfig();
+
+ // Sử dụng đường dẫn tương đối đến file DB
+ String dbPath = "database/BSK.db";
+ config.setJdbcUrl("jdbc:sqlite:" + dbPath);
+
+ // Cấu hình cho SQLite
+ config.addDataSourceProperty("journal_mode", "WAL"); // Rất quan trọng để tăng khả năng ghi đồng thời
+ config.addDataSourceProperty("busy_timeout", "5000"); // Chờ 5 giây nếu DB bị lock
+ config.setConnectionTestQuery("SELECT 1");
+
+ // Cấu hình pool
+ config.setPoolName("BSK-Clinic-Pool");
+ config.setMaximumPoolSize(10); // 10 kết nối đồng thời là quá đủ cho ứng dụng phòng khám
+ config.setMinimumIdle(2);
+ config.setConnectionTimeout(30000); // 30 giây
+ config.setIdleTimeout(600000); // 10 phút
+ config.setMaxLifetime(1800000); // 30 phút
+
+ dataSource = new HikariDataSource(config);
+ log.info("Database connection pool initialized successfully.");
+ }
+
+ public static Connection getConnection() throws SQLException {
+ return dataSource.getConnection();
+ }
+
+ public static void close() {
+ if (dataSource != null) {
+ dataSource.close();
+ log.info("Database connection pool closed.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/BsK/server/network/entity/User.java b/src/main/java/BsK/server/network/entity/User.java
index 81e3a3d..7f07b23 100644
--- a/src/main/java/BsK/server/network/entity/User.java
+++ b/src/main/java/BsK/server/network/entity/User.java
@@ -8,7 +8,9 @@ import lombok.extern.slf4j.Slf4j;
import java.sql.ResultSet;
import java.sql.SQLException;
-import static BsK.server.Server.statement;
+import BsK.server.database.DatabaseManager;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
@Slf4j
@Data
@@ -24,28 +26,52 @@ public class User {
this.sessionId = sessionId;
}
+ /**
+ * Xác thực người dùng.
+ * PHIÊN BẢN NÀY ĐÃ SỬA LỖI SQL INJECTION VÀ LỖI KỸ THUẬT,
+ * NHƯNG VẪN CÓ RỦI RO BẢO MẬT VÌ DÙNG MẬT KHẨU PLAINTEXT.
+ */
public void authenticate(String username, String password) {
- try {
- ResultSet rs = statement.executeQuery("select * from User where user_name = '" + username + "' and password = '" + password + "'");
- if (!rs.isBeforeFirst()) {
- System.out.println("No data found in the User table.");
- } else {
- while(rs.next())
- {
- log.info(rs.getString("user_name") + " logged in");
- role = Role.valueOf(rs.getString("role_name"));
- userId = rs.getInt("user_id");
- // Update the client connection with the new role
- Boolean deleted = rs.getBoolean("deleted");
- if (deleted) {
- role = Role.GUEST;
- }
- SessionManager.updateUserRole(channel.id().asLongText(), role.toString(), userId);
- }
- }
- } catch (SQLException e) {
- throw new RuntimeException(e);
- }
+ // Sử dụng PreparedStatement để chống SQL Injection
+ String sql = "SELECT user_id, role_name, deleted FROM User WHERE user_name = ? AND password = ?";
+
+ // Sử dụng try-with-resources để quản lý kết nối và tài nguyên an toàn
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+
+ stmt.setString(1, username);
+ stmt.setString(2, password); // Vẫn so sánh plaintext, cần được thay đổi
+
+ try (ResultSet rs = stmt.executeQuery()) {
+ // Tên đăng nhập là duy nhất, chỉ nên có 1 kết quả. Dùng if thay vì while.
+ if (rs.next()) {
+ boolean isDeleted = rs.getBoolean("deleted");
+ if (isDeleted) {
+ log.warn("Login attempt for deleted user: {}", username);
+ // Không xác thực tài khoản đã bị xóa
+ this.role = Role.GUEST;
+ this.userId = -1;
+ } else {
+ log.info("User '{}' logged in successfully.", username);
+ this.userId = rs.getInt("user_id");
+ this.role = Role.valueOf(rs.getString("role_name"));
+ // Việc cập nhật session/channel nên được thực hiện ở ServerHandler
+ // sau khi kiểm tra trạng thái xác thực của đối tượng User này.
+ }
+ } else {
+ log.warn("Failed login attempt for username: {}", username);
+ // Không tìm thấy user hoặc sai mật khẩu
+ this.role = Role.GUEST;
+ this.userId = -1;
+ }
+ }
+
+ } catch (SQLException e) {
+ log.error("Database error during authentication for user '{}'", username, e);
+ // Đảm bảo người dùng không được xác thực nếu có lỗi
+ this.role = Role.GUEST;
+ this.userId = -1;
+ }
}
public boolean isAuthenticated() {
diff --git a/src/main/java/BsK/server/network/handler/ServerHandler.java b/src/main/java/BsK/server/network/handler/ServerHandler.java
index 8268576..9e28657 100644
--- a/src/main/java/BsK/server/network/handler/ServerHandler.java
+++ b/src/main/java/BsK/server/network/handler/ServerHandler.java
@@ -1,6 +1,7 @@
package BsK.server.network.handler;
import BsK.common.packet.Packet;
+import BsK.server.database.DatabaseManager;
import BsK.common.packet.PacketSerializer;
import BsK.common.packet.req.*;
import BsK.common.packet.res.*;
@@ -49,8 +50,11 @@ import BsK.common.entity.Service;
import BsK.common.entity.PatientHistory;
import BsK.common.entity.Template;
import BsK.common.util.text.TextUtils;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.util.concurrent.CompletableFuture;
+
-import static BsK.server.Server.statement;
@Slf4j
@@ -107,123 +111,130 @@ public class ServerHandler extends SimpleChannelInboundHandler 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()); // Needed to recode
- 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 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')""";
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql);
+ ResultSet rs = stmt.executeQuery()) {
+
+ if (!rs.isBeforeFirst()) {
+ log.warn("No data found in the checkup table for today.");
+ // Gửi về một mảng rỗng thay vì không gửi gì
+ UserUtil.sendPacket(currentUser.getSessionId(), new GetCheckUpQueueResponse(new String[0][0]));
+ } else {
+ // Phần code xử lý logic bên trong không thay đổi
+ ArrayList 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]);
+ String[][] resultArray = new String[resultString.length][];
+ for (int i = 0; i < resultString.length; i++) {
+ resultArray[i] = resultString[i].split("\\|");
+ }
+ UserUtil.sendPacket(currentUser.getSessionId(), new GetCheckUpQueueResponse(resultArray));
}
-
- 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("\\|");
- }
- UserUtil.sendPacket(currentUser.getSessionId(), new GetCheckUpQueueResponse(resultArray));
- }
} catch (SQLException e) {
- throw new RuntimeException(e);
+
+ log.error("Error processing GetCheckUpQueueRequest", e);
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
}
- }
+ }
if (packet instanceof GetCheckUpQueueUpdateRequest) {
log.debug("Received GetCheckUpQueueUpdateRequest");
broadcastQueueUpdate();
}
- // Get general doctor info
if (packet instanceof GetDoctorGeneralInfo) {
log.debug("Received GetDoctorGeneralInfo");
- try {
- ResultSet rs = statement.executeQuery(
- "select concat(doctor_last_name, ' ', doctor_first_name), doctor_id from Doctor"
- );
+ String sql = "SELECT doctor_last_name || ' ' || doctor_first_name, doctor_id FROM Doctor";
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql);
+ ResultSet rs = stmt.executeQuery()) {
+
if (!rs.isBeforeFirst()) {
- System.out.println("No data found in the doctor table.");
+ log.warn("No data found in the doctor table.");
+ UserUtil.sendPacket(currentUser.getSessionId(), new GetDoctorGeneralInfoResponse(new String[0][0]));
} else {
- ArrayList resultList = new ArrayList<>();
- while (rs.next()) {
- String doctorName = rs.getString(1);
- String doctorId = rs.getString(2);
- resultList.add(doctorName + "|" + doctorId);
- }
- 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("\\|");
- }
- UserUtil.sendPacket(currentUser.getSessionId(), new GetDoctorGeneralInfoResponse(resultArray));
- log.info("Send response to client GetDoctorGeneralInfo");
- }
- }
- catch (SQLException e) {
- throw new RuntimeException(e);
+ ArrayList resultList = new ArrayList<>();
+ while (rs.next()) {
+ String doctorName = rs.getString(1); // Lấy theo chỉ số cột
+ String doctorId = rs.getString(2); // Lấy theo chỉ số cột
+ resultList.add(doctorName + "|" + doctorId);
+ }
+
+ 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("\\|");
+ }
+
+ UserUtil.sendPacket(currentUser.getSessionId(), new GetDoctorGeneralInfoResponse(resultArray));
+ log.info("Sent response to client GetDoctorGeneralInfo");
+ }
+ } catch (SQLException e) {
+ log.error("Error processing GetDoctorGeneralInfo", e);
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
}
}
@@ -247,11 +258,13 @@ public class ServerHandler extends SimpleChannelInboundHandler resultList = new ArrayList<>();
+ while (rs.next()) {
+ String customerId = rs.getString("customer_id");
+ String customerLastName = rs.getString("customer_last_name");
+ String customerFirstName = rs.getString("customer_first_name");
+ String customerDob = rs.getString("customer_dob");
+ String year = Integer.toString(DateUtils.extractYearFromTimestamp(customerDob));
+ String customerNumber = rs.getString("customer_number");
+ String customerAddress = rs.getString("customer_address");
+ String customerGender = rs.getString("customer_gender");
+ String cccdDdcn = rs.getString("cccd_ddcn");
+
+ String result = String.join("|", customerId, customerLastName + " " + customerFirstName,
+ year, customerNumber, customerAddress, customerGender, customerDob, cccdDdcn);
+ 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("\\|");
+ }
+
+
+ UserUtil.sendPacket(currentUser.getSessionId(), new GetRecentPatientResponse(resultArray, totalCount, currentPage, totalPages, pageSize));
+ }
}
- }
-
- // Add ORDER BY
- queryBuilder.append(" ORDER BY customer_id DESC");
-
- // Count total records for pagination
- String countQuery = queryBuilder.toString().replace("SELECT customer_id, customer_last_name, customer_first_name, customer_dob, customer_number, customer_address, customer_gender, cccd_ddcn", "SELECT COUNT(*)");
- PreparedStatement countStmt = Server.connection.prepareStatement(countQuery);
-
- int paramIndex = 1;
- if (hasNameSearch) {
- String searchName = "%" + getRecentPatientRequest.getSearchName().toLowerCase().trim() + "%";
- countStmt.setString(paramIndex++, searchName);
- countStmt.setString(paramIndex++, searchName);
- countStmt.setString(paramIndex++, searchName);
- }
- if (hasPhoneSearch) {
- String searchPhone = "%" + getRecentPatientRequest.getSearchPhone().trim() + "%";
- countStmt.setString(paramIndex++, searchPhone);
- }
-
- ResultSet countRs = countStmt.executeQuery();
- int totalCount = 0;
- if (countRs.next()) {
- totalCount = countRs.getInt(1);
- }
- countRs.close();
- countStmt.close();
-
- // Calculate pagination
- int pageSize = getRecentPatientRequest.getPageSize();
- int currentPage = getRecentPatientRequest.getPage();
- int totalPages = (int) Math.ceil((double) totalCount / pageSize);
- int offset = (currentPage - 1) * pageSize;
-
- // Add LIMIT and OFFSET for pagination
- queryBuilder.append(" LIMIT ? OFFSET ?");
-
- PreparedStatement stmt = Server.connection.prepareStatement(queryBuilder.toString());
-
- // Set parameters for the main query
- paramIndex = 1;
- if (hasNameSearch) {
- String searchName = "%" + getRecentPatientRequest.getSearchName().toLowerCase().trim() + "%";
- stmt.setString(paramIndex++, searchName);
- stmt.setString(paramIndex++, searchName);
- stmt.setString(paramIndex++, searchName);
- }
- if (hasPhoneSearch) {
- String searchPhone = "%" + getRecentPatientRequest.getSearchPhone().trim() + "%";
- stmt.setString(paramIndex++, searchPhone);
- }
- stmt.setInt(paramIndex++, pageSize);
- stmt.setInt(paramIndex++, offset);
-
- ResultSet rs = stmt.executeQuery();
-
- if (!rs.isBeforeFirst()) {
- log.debug("No data found in the customer table for the given search criteria.");
- UserUtil.sendPacket(currentUser.getSessionId(), new GetRecentPatientResponse(new String[0][], totalCount, currentPage, totalPages, pageSize));
- } else {
- ArrayList resultList = new ArrayList<>();
- while (rs.next()) {
- String customerId = rs.getString("customer_id");
- String customerLastName = rs.getString("customer_last_name");
- String customerFirstName = rs.getString("customer_first_name");
- String customerDob = rs.getString("customer_dob");
- String year = Integer.toString(DateUtils.extractYearFromTimestamp(customerDob));
- String customerNumber = rs.getString("customer_number");
- String customerAddress = rs.getString("customer_address");
- String customerGender = rs.getString("customer_gender");
- String cccdDdcn = rs.getString("cccd_ddcn");
-
- String result = String.join("|", customerId, customerLastName + " " + customerFirstName,
- year, customerNumber, customerAddress, customerGender, customerDob, cccdDdcn);
- 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("\\|");
- }
-
- UserUtil.sendPacket(currentUser.getSessionId(), new GetRecentPatientResponse(resultArray, totalCount, currentPage, totalPages, pageSize));
- }
-
- rs.close();
- stmt.close();
} catch (SQLException e) {
- log.error("Error processing GetRecentPatientRequest", e);
- throw new RuntimeException(e);
+ // Xử lý lỗi an toàn
+ log.error("Error processing GetRecentPatientRequest", e);
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
}
}
if (packet instanceof GetProvinceRequest getProvinceRequest) {
log.debug("Received GetProvinceRequest");
- try {
- ResultSet rs = statement.executeQuery(
- "select provinces.code, provinces.name\n" +
- "from provinces" +
- " order by provinces.name"
- );
-
- if (!rs.isBeforeFirst()) {
- System.out.println("No data found in the province table.");
- } else {
+
+ String sql = "SELECT code, name FROM provinces ORDER BY name";
+
+ // SỬA ĐỔI: Sử dụng try-with-resources để quản lý kết nối và tài nguyên
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql);
+ ResultSet rs = stmt.executeQuery()) {
+
ArrayList resultList = new ArrayList<>();
HashMap provinceIdMap = new HashMap<>();
+
+ // Luôn thêm giá trị mặc định cho ComboBox ở client
resultList.add("Tỉnh/Thành phố");
+
+ // Vòng lặp while sẽ tự xử lý trường hợp không có dữ liệu
while (rs.next()) {
- String provinceId = rs.getString("code");
- String provinceName = rs.getString("name");
- provinceIdMap.put(provinceName, provinceId);
- resultList.add(provinceName);
+ String provinceId = rs.getString("code");
+ String provinceName = rs.getString("name");
+ provinceIdMap.put(provinceName, provinceId);
+ resultList.add(provinceName);
}
-
+
+ if (resultList.size() <= 1) {
+ log.warn("No data found in the province table.");
+ }
+
String[] resultString = resultList.toArray(new String[0]);
UserUtil.sendPacket(currentUser.getSessionId(), new GetProvinceResponse(resultString, provinceIdMap));
- }
+
} catch (SQLException e) {
- throw new RuntimeException(e);
+ // SỬA ĐỔI: Xử lý lỗi an toàn
+ log.error("Error processing GetProvinceRequest", e);
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
}
}
if (packet instanceof GetWardRequest getWardRequest) {
log.debug("Received GetWardRequest for province {}", getWardRequest.getProvinceId());
- String sql = "SELECT wards.name FROM wards WHERE province_code = ? ORDER BY wards.name";
-
- // Use try-with-resources to automatically close resources
- try (PreparedStatement preparedStatement = Server.connection.prepareStatement(sql)) {
+ String sql = "SELECT name FROM wards WHERE province_code = ? ORDER BY name";
+
+ // SỬA ĐỔI: Sử dụng try-with-resources cho Connection và đổi 'Server.connection' thành 'conn'
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement preparedStatement = conn.prepareStatement(sql)) {
+
preparedStatement.setString(1, getWardRequest.getProvinceId());
+
try (ResultSet rs = preparedStatement.executeQuery()) {
- if (!rs.isBeforeFirst()) {
- System.out.println("No data found in the ward table.");
- // Send empty response
- UserUtil.sendPacket(currentUser.getSessionId(), new GetWardResponse(new String[]{"Phường/Xã"}));
- } else {
- ArrayList resultList = new ArrayList<>();
- resultList.add("Phường/Xã");
- while (rs.next()) {
- resultList.add(rs.getString("name"));
- }
- String[] resultString = resultList.toArray(new String[0]);
- UserUtil.sendPacket(currentUser.getSessionId(), new GetWardResponse(resultString));
+ ArrayList resultList = new ArrayList<>();
+ // Luôn thêm giá trị mặc định cho ComboBox ở client
+ resultList.add("Phường/Xã");
+
+ // Vòng lặp while sẽ tự xử lý trường hợp không có dữ liệu
+ while (rs.next()) {
+ resultList.add(rs.getString("name"));
}
+
+ if (resultList.size() <= 1) {
+ log.warn("No data found in the ward table for province_code: {}", getWardRequest.getProvinceId());
+ }
+
+ String[] resultString = resultList.toArray(new String[0]);
+ UserUtil.sendPacket(currentUser.getSessionId(), new GetWardResponse(resultString));
}
} catch (SQLException e) {
- log.error("Error fetching wards for province {}", getWardRequest.getProvinceId(), e);
- throw new RuntimeException(e);
+ // SỬA ĐỔI: Xử lý lỗi an toàn
+ log.error("Error fetching wards for province {}: {}", getWardRequest.getProvinceId(), e.getMessage());
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
}
}
if (packet instanceof AddPatientRequest addPatientRequest) {
log.debug("Received AddPatientRequest");
- try {
- // Disable auto-commit for transaction
- Server.connection.setAutoCommit(false);
-
- PreparedStatement preparedStatement = Server.connection.prepareStatement(
- "INSERT INTO Customer (customer_last_name, customer_first_name, customer_dob, customer_number, customer_address, customer_gender, cccd_ddcn) VALUES (?, ?, ?, ?, ?, ?, ?)",
- PreparedStatement.RETURN_GENERATED_KEYS // Use this instead of separate query
- );
- preparedStatement.setString(1, addPatientRequest.getPatientLastName());
- preparedStatement.setString(2, addPatientRequest.getPatientFirstName());
- preparedStatement.setLong(3, addPatientRequest.getPatientDob());
- preparedStatement.setString(4, addPatientRequest.getPatientPhone());
- preparedStatement.setString(5, addPatientRequest.getPatientAddress());
- preparedStatement.setString(6, addPatientRequest.getPatientGender());
- preparedStatement.setString(7, addPatientRequest.getPatientCccdDdcn());
- preparedStatement.executeUpdate();
-
- // Get the generated key safely
- int customerId = 0;
- try (ResultSet generatedKeys = preparedStatement.getGeneratedKeys()) {
- if (generatedKeys.next()) {
- customerId = generatedKeys.getInt(1);
- } else {
- throw new SQLException("Creating patient failed, no ID obtained.");
+
+ String sql = "INSERT INTO Customer (customer_last_name, customer_first_name, customer_dob, customer_number, customer_address, customer_gender, cccd_ddcn) VALUES (?, ?, ?, ?, ?, ?, ?)";
+
+ // SỬA ĐỔI: Bắt đầu bằng try-with-resources để lấy Connection từ pool
+ try (Connection conn = DatabaseManager.getConnection()) {
+
+ // 1. Bắt đầu transaction trên kết nối vừa mượn
+ conn.setAutoCommit(false);
+
+ try (PreparedStatement preparedStatement = conn.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS)) {
+
+ preparedStatement.setString(1, addPatientRequest.getPatientLastName());
+ preparedStatement.setString(2, addPatientRequest.getPatientFirstName());
+ preparedStatement.setLong(3, addPatientRequest.getPatientDob());
+ preparedStatement.setString(4, addPatientRequest.getPatientPhone());
+ preparedStatement.setString(5, addPatientRequest.getPatientAddress());
+ preparedStatement.setString(6, addPatientRequest.getPatientGender());
+ preparedStatement.setString(7, addPatientRequest.getPatientCccdDdcn());
+ preparedStatement.executeUpdate();
+
+ // Lấy customerId vừa được tạo ra
+ int customerId = 0;
+ try (ResultSet generatedKeys = preparedStatement.getGeneratedKeys()) {
+ if (generatedKeys.next()) {
+ customerId = generatedKeys.getInt(1);
+ } else {
+ // Nếu không lấy được ID, ném lỗi để kích hoạt rollback
+ throw new SQLException("Creating patient failed, no ID obtained.");
+ }
+ }
+
+ // 2. Nếu mọi thứ thành công, commit transaction
+ conn.commit();
+
+ // 3. Gửi phản hồi thành công và thực hiện các tác vụ bất đồng bộ
+ UserUtil.sendPacket(currentUser.getSessionId(), new AddPatientResponse(true, customerId, "Thêm bệnh nhân thành công"));
+ createPatientGoogleDriveFolderAsync(customerId, addPatientRequest.getPatientLastName(), addPatientRequest.getPatientFirstName());
+
+ } catch (SQLException e) {
+ // 4. Nếu có bất kỳ lỗi nào xảy ra trong khối try bên trong, rollback lại
+ log.error("Error during AddPatient transaction, rolling back.", e);
+ conn.rollback();
+
+ // Gửi phản hồi lỗi về client
+ String errorMessage = e.getMessage();
+ UserUtil.sendPacket(currentUser.getSessionId(), new AddPatientResponse(false, -1, "Lỗi: " + errorMessage));
+ } finally {
+ // 5. Rất quan trọng: Luôn trả lại trạng thái auto-commit về true
+ // trước khi kết nối được trả về pool bởi try-with-resources
+ conn.setAutoCommit(true);
}
- }
-
- // Commit the transaction
- Server.connection.commit();
-
- // Close resources
- preparedStatement.close();
-
- // Send immediate response to client
- UserUtil.sendPacket(currentUser.getSessionId(), new AddPatientResponse(true, customerId, "Thêm bệnh nhân thành công"));
-
- // Create Google Drive folder asynchronously (don't block the response)
- createPatientGoogleDriveFolderAsync(customerId, addPatientRequest.getPatientLastName(), addPatientRequest.getPatientFirstName());
+
} catch (SQLException e) {
- try {
- // Rollback on error
- Server.connection.rollback();
- } catch (SQLException rollbackEx) {
- log.error("Error during rollback", rollbackEx);
- }
- String errorMessage = e.getMessage();
- UserUtil.sendPacket(currentUser.getSessionId(), new AddPatientResponse(false, -1, "Lỗi: " + errorMessage));
- log.error("Error adding patient", e);
- } finally {
- try {
- // Reset auto-commit
- Server.connection.setAutoCommit(true);
- } catch (SQLException e) {
- log.error("Error resetting auto-commit", e);
- }
+ // Khối catch này chỉ bắt lỗi khi không thể lấy được kết nối từ pool
+ log.error("Failed to get DB connection for AddPatientRequest", e);
+ UserUtil.sendPacket(currentUser.getSessionId(), new AddPatientResponse(false, -1, "Lỗi: Không thể kết nối CSDL."));
}
}
if (packet instanceof AddCheckupRequest addCheckupRequest) {
log.debug("Received AddCheckupRequest to add for customer {}", addCheckupRequest.getCustomerId());
-
- // Khai báo các biến ID ở ngoài khối try để có thể sử dụng sau này
- int generatedCheckupId = 0;
- int queueNumber = 0;
- try {
- // Bắt đầu một transaction
- Server.connection.setAutoCommit(false);
+ // --- SỬA ĐỔI QUAN TRỌNG: Sử dụng một kết nối riêng từ pool cho toàn bộ giao dịch ---
+ // try-with-resources sẽ đảm bảo kết nối được trả về pool ngay cả khi có lỗi.
+ try (Connection conn = DatabaseManager.getConnection()) {
+ int generatedCheckupId = 0;
+ int queueNumber = 0;
- // --- 1. LẤY SỐ THỨ TỰ (QUEUE NUMBER) MỘT CÁCH AN TOÀN ---
- // Sử dụng UPSERT (INSERT ON CONFLICT) để đảm bảo thao tác là nguyên tử
- String queueSql = "INSERT INTO DailyQueueCounter (date, current_count) " +
- "VALUES (date('now', 'localtime'), 1) " +
- "ON CONFLICT(date) DO UPDATE SET current_count = current_count + 1 " +
- "RETURNING current_count";
-
- try (PreparedStatement queueStmt = Server.connection.prepareStatement(queueSql)) {
- ResultSet rs = queueStmt.executeQuery();
- if (rs.next()) {
- queueNumber = rs.getInt(1);
- } else {
- throw new SQLException("Failed to get or update queue number.");
- }
- }
+ try {
+ // 1. Bắt đầu một transaction trên kết nối CỤC BỘ (conn) này
+ conn.setAutoCommit(false);
- // --- 2. 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 (?, ?, ?, ?, ?)";
- try (PreparedStatement checkupStmt = Server.connection.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.executeUpdate();
-
- // Lấy checkup_id vừa được tạo ra một cách an toàn
- try (ResultSet generatedKeys = checkupStmt.getGeneratedKeys()) {
- if (generatedKeys.next()) {
- generatedCheckupId = generatedKeys.getInt(1);
+ // 2. LẤY SỐ THỨ TỰ (QUEUE NUMBER)
+ // Sử dụng UPSERT để đảm bảo thao tác là nguyên tử
+ String queueSql = """
+ INSERT INTO DailyQueueCounter (date, current_count)
+ VALUES (date('now', 'localtime'), 1)
+ ON CONFLICT(date) DO UPDATE SET current_count = current_count + 1
+ RETURNING current_count
+ """;
+
+ try (PreparedStatement queueStmt = conn.prepareStatement(queueSql);
+ ResultSet rs = queueStmt.executeQuery()) {
+ if (rs.next()) {
+ queueNumber = rs.getInt(1);
} else {
- throw new SQLException("Creating checkup failed, no ID obtained.");
+ throw new SQLException("Failed to get or update queue number.");
}
}
- }
- // --- 3. CHÈN VÀO BẢNG MEDICINEORDER VÀ LẤY LẠI PRESCRIPTION_ID ---
- int generatedPrescriptionId = 0;
- String medOrderSql = "INSERT INTO MedicineOrder (checkup_id, customer_id, processed_by) VALUES (?, ?, ?)";
- try (PreparedStatement medOrderStmt = Server.connection.prepareStatement(medOrderSql, PreparedStatement.RETURN_GENERATED_KEYS)) {
- medOrderStmt.setInt(1, generatedCheckupId); // Sử dụng ID đã lấy được
- medOrderStmt.setInt(2, addCheckupRequest.getCustomerId());
- medOrderStmt.setInt(3, addCheckupRequest.getProcessedById());
- medOrderStmt.executeUpdate();
+ // 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 (?, ?, ?, ?, ?)";
+ 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.executeUpdate();
- // Lấy prescription_id vừa được tạo ra một cách an toàn
- try (ResultSet generatedKeys = medOrderStmt.getGeneratedKeys()) {
- if (generatedKeys.next()) {
- generatedPrescriptionId = generatedKeys.getInt(1);
- } else {
- throw new SQLException("Creating medicine order failed, no ID obtained.");
+ try (ResultSet generatedKeys = checkupStmt.getGeneratedKeys()) {
+ if (generatedKeys.next()) {
+ generatedCheckupId = generatedKeys.getInt(1);
+ } else {
+ throw new SQLException("Creating checkup failed, no ID obtained.");
+ }
}
}
+
+ // 4. CHÈN VÀO BẢNG MEDICINEORDER VÀ LẤY LẠI PRESCRIPTION_ID
+ int generatedPrescriptionId = 0;
+ String medOrderSql = "INSERT INTO MedicineOrder (checkup_id, customer_id, processed_by) VALUES (?, ?, ?)";
+ try (PreparedStatement medOrderStmt = conn.prepareStatement(medOrderSql, PreparedStatement.RETURN_GENERATED_KEYS)) {
+ medOrderStmt.setInt(1, generatedCheckupId);
+ medOrderStmt.setInt(2, addCheckupRequest.getCustomerId());
+ medOrderStmt.setInt(3, addCheckupRequest.getProcessedById());
+ medOrderStmt.executeUpdate();
+
+ try (ResultSet generatedKeys = medOrderStmt.getGeneratedKeys()) {
+ if (generatedKeys.next()) {
+ generatedPrescriptionId = generatedKeys.getInt(1);
+ } else {
+ throw new SQLException("Creating medicine order failed, no ID obtained.");
+ }
+ }
+ }
+
+ // 5. CẬP NHẬT BẢNG CHECKUP VỚI PRESCRIPTION_ID
+ String updateSql = "UPDATE Checkup SET prescription_id = ? WHERE checkup_id = ?";
+ try (PreparedStatement updateStmt = conn.prepareStatement(updateSql)) {
+ updateStmt.setInt(1, generatedPrescriptionId);
+ updateStmt.setInt(2, generatedCheckupId);
+ updateStmt.executeUpdate();
+ }
+
+ // 6. Nếu tất cả các bước thành công, commit transaction
+ conn.commit();
+
+ // 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();
+
+ // Tạo thư mục Google Drive bất đồng bộ
+ createCheckupGoogleDriveFolderAsync(generatedCheckupId, addCheckupRequest.getCustomerId());
+
+ } catch (SQLException e) {
+ // Nếu có bất kỳ lỗi nào trong khối try ở trên, rollback transaction
+ log.error("SQLException during AddCheckupRequest, rolling back transaction.", e);
+ conn.rollback(); // Quan trọng: rollback trên kết nối cục bộ
+
+ // Gửi phản hồi lỗi về client
+ String errorMessage = e.getMessage();
+ UserUtil.sendPacket(currentUser.getSessionId(), new AddCheckupResponse(false, "Lỗi Server: " + errorMessage, -1));
}
-
- // --- 4. CẬP NHẬT BẢNG CHECKUP VỚI PRESCRIPTION_ID ---
- String updateSql = "UPDATE Checkup SET prescription_id = ? WHERE checkup_id = ?";
- try (PreparedStatement updateStmt = Server.connection.prepareStatement(updateSql)) {
- updateStmt.setInt(1, generatedPrescriptionId); // Sử dụng ID đã lấy được
- updateStmt.setInt(2, generatedCheckupId); // Sử dụng ID đã lấy được
- updateStmt.executeUpdate();
- }
-
- // Nếu tất cả các bước thành công, commit transaction
- Server.connection.commit();
-
- // Gửi phản hồi thành công về cho client
- UserUtil.sendPacket(currentUser.getSessionId(), new AddCheckupResponse(true, "Thêm bệnh nhân thành công", queueNumber));
-
- // Broadcast cập nhật hàng chờ cho các client khác
- broadcastQueueUpdate();
-
- // Tạo thư mục Google Drive bất đồng bộ (không làm chậm phản hồi)
- // generatedCheckupId bây giờ đã có giá trị chính xác
- createCheckupGoogleDriveFolderAsync(generatedCheckupId, addCheckupRequest.getCustomerId());
+ // KHÔNG CẦN khối `finally` để `setAutoCommit(true)`.
+ // HikariCP sẽ tự động reset trạng thái của kết nối khi nó được trả về pool.
} catch (SQLException e) {
- log.error("SQLException during AddCheckupRequest, rolling back transaction.", e);
- try {
- // Rollback nếu có lỗi
- Server.connection.rollback();
- } catch (SQLException rollbackEx) {
- log.error("Error during rollback", rollbackEx);
- }
- String errorMessage = e.getMessage();
- UserUtil.sendPacket(currentUser.getSessionId(), new AddCheckupResponse(false, "Lỗi Server: " + errorMessage, -1));
- // Không cần throw RuntimeException nữa để server không bị sập
-
- } finally {
- try {
- // Luôn luôn reset auto-commit về true
- Server.connection.setAutoCommit(true);
- } catch (SQLException e) {
- log.error("Error resetting auto-commit", e);
- }
+ // Lỗi này xảy ra nếu không thể lấy kết nối từ pool, hoặc có lỗi nghiêm trọng khi rollback
+ log.error("Critical error during database transaction management for AddCheckupRequest.", e);
+ UserUtil.sendPacket(currentUser.getSessionId(), new AddCheckupResponse(false, "Lỗi hệ thống: Không thể xử lý giao dịch CSDL.", -1));
}
- }
+ }
if (packet instanceof CallPatientRequest callPatientRequest) {
log.debug("Received CallPatientRequest to call patient checkup_id: {}", callPatientRequest.getPatientId());
@@ -698,220 +728,171 @@ public class ServerHandler extends SimpleChannelInboundHandler 0) {
- try (PreparedStatement medicineOrderStmt = conn.prepareStatement(medicineOrderSql, PreparedStatement.RETURN_GENERATED_KEYS)) {
- double totalMedicineAmount = 0;
- for (String[] medicine : saveCheckupRequest.getMedicinePrescription()) {
- if (medicine.length > 8) {
- totalMedicineAmount += Double.parseDouble(medicine[8]);
- }
- }
-
- medicineOrderStmt.setInt(1, saveCheckupRequest.getCheckupId());
- medicineOrderStmt.setInt(2, saveCheckupRequest.getCustomerId());
- medicineOrderStmt.setDouble(3, totalMedicineAmount);
- medicineOrderStmt.setString(4, ""); // payment_method
- medicineOrderStmt.setString(5, "Pending"); // status
- medicineOrderStmt.setString(6, "Unpaid"); // payment_status
- medicineOrderStmt.setString(7, ""); // processed_by
- medicineOrderStmt.setString(8, ""); // notes
- medicineOrderStmt.executeUpdate();
-
- // B. Retrieve the generated key
- try (ResultSet generatedKeys = medicineOrderStmt.getGeneratedKeys()) {
- if (generatedKeys.next()) {
- newPrescriptionId = generatedKeys.getInt(1);
- log.info("New prescription_id generated: {}", newPrescriptionId);
- } else {
- throw new SQLException("Creating medicine order failed, no ID obtained.");
- }
- }
- }
-
+ // --- SỬA ĐỔI QUAN TRỌNG: Lấy một kết nối riêng từ pool cho toàn bộ giao dịch ---
+ try (Connection conn = DatabaseManager.getConnection()) {
+ try {
+ // 1. Bắt đầu transaction trên kết nối CỤC BỘ (conn)
+ conn.setAutoCommit(false);
- // C. Insert OrderItems using the new prescription_id
- String orderItemSql = """
- INSERT INTO OrderItem (
- prescription_id, med_id, quantity_ordered, dosage, duration,
- price_per_unit, total_price, checkup_id, notes
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
+ // 2. CẬP NHẬT THÔNG TIN KHÁCH HÀNG (CUSTOMER)
+ String customerSql = """
+ INSERT INTO Customer (
+ customer_id, customer_first_name, customer_last_name, customer_dob,
+ customer_gender, customer_address, customer_number, cccd_ddcn
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
+ ON CONFLICT(customer_id) DO UPDATE SET
+ customer_first_name = excluded.customer_first_name,
+ customer_last_name = excluded.customer_last_name,
+ customer_dob = excluded.customer_dob,
+ customer_gender = excluded.customer_gender,
+ customer_address = excluded.customer_address,
+ customer_number = excluded.customer_number,
+ cccd_ddcn = excluded.cccd_ddcn
""";
- try (PreparedStatement orderItemStmt = conn.prepareStatement(orderItemSql)) {
- for (String[] medicine : saveCheckupRequest.getMedicinePrescription()) {
- if (medicine.length >= 10) {
- orderItemStmt.setInt(1, newPrescriptionId); // Use the new ID
- orderItemStmt.setString(2, medicine[0]); // med_id
- orderItemStmt.setString(3, medicine[2]); // quantity
- String dosage = String.format("Sáng %s, Trưa %s, Chiều %s", medicine[4], medicine[5], medicine[6]);
- orderItemStmt.setString(4, dosage);
- orderItemStmt.setString(5, ""); // duration
- orderItemStmt.setString(6, medicine[7]); // unit_price
- orderItemStmt.setString(7, medicine[8]); // total_price
- orderItemStmt.setInt(8, saveCheckupRequest.getCheckupId());
- orderItemStmt.setString(9, medicine[9]); // notes
- orderItemStmt.addBatch();
+ try (PreparedStatement customerStmt = conn.prepareStatement(customerSql)) {
+ customerStmt.setInt(1, saveCheckupRequest.getCustomerId());
+ customerStmt.setString(2, saveCheckupRequest.getCustomerFirstName());
+ customerStmt.setString(3, saveCheckupRequest.getCustomerLastName());
+ customerStmt.setLong(4, saveCheckupRequest.getCustomerDob());
+ customerStmt.setString(5, saveCheckupRequest.getCustomerGender());
+ customerStmt.setString(6, saveCheckupRequest.getCustomerAddress());
+ customerStmt.setString(7, saveCheckupRequest.getCustomerNumber());
+ customerStmt.setString(8, saveCheckupRequest.getCustomerCccdDdcn());
+ customerStmt.executeUpdate();
+ }
+
+ // 3. XỬ LÝ ĐƠN THUỐC (MEDICINE PRESCRIPTION)
+ Integer newPrescriptionId = null;
+
+ // Xóa dữ liệu cũ của đơn thuốc liên quan đến lần khám này
+ try (PreparedStatement deleteStmt = conn.prepareStatement("DELETE FROM OrderItem WHERE checkup_id = ?")) {
+ deleteStmt.setInt(1, saveCheckupRequest.getCheckupId());
+ deleteStmt.executeUpdate();
+ }
+ try (PreparedStatement deleteStmt = conn.prepareStatement("DELETE FROM MedicineOrder WHERE checkup_id = ?")) {
+ deleteStmt.setInt(1, saveCheckupRequest.getCheckupId());
+ deleteStmt.executeUpdate();
+ }
+ log.info("Cleared previous medicine prescription for checkup_id: {}", saveCheckupRequest.getCheckupId());
+
+ // Nếu có đơn thuốc mới, tạo lại từ đầu
+ if (saveCheckupRequest.getMedicinePrescription() != null && saveCheckupRequest.getMedicinePrescription().length > 0) {
+ // A. Chèn vào MedicineOrder để lấy prescription_id mới
+ String medicineOrderSql = "INSERT INTO MedicineOrder (checkup_id, customer_id, total_amount, status, payment_status) VALUES (?, ?, ?, ?, ?)";
+ try (PreparedStatement medicineOrderStmt = conn.prepareStatement(medicineOrderSql, PreparedStatement.RETURN_GENERATED_KEYS)) {
+ double totalMedicineAmount = 0;
+ for (String[] medicine : saveCheckupRequest.getMedicinePrescription()) {
+ if (medicine.length > 8) totalMedicineAmount += Double.parseDouble(medicine[8]);
+ }
+ medicineOrderStmt.setInt(1, saveCheckupRequest.getCheckupId());
+ medicineOrderStmt.setInt(2, saveCheckupRequest.getCustomerId());
+ medicineOrderStmt.setDouble(3, totalMedicineAmount);
+ medicineOrderStmt.setString(4, "Pending"); // status
+ medicineOrderStmt.setString(5, "Unpaid"); // payment_status
+ medicineOrderStmt.executeUpdate();
+
+ try (ResultSet generatedKeys = medicineOrderStmt.getGeneratedKeys()) {
+ if (generatedKeys.next()) {
+ newPrescriptionId = generatedKeys.getInt(1);
+ } else {
+ throw new SQLException("Creating medicine order failed, no ID obtained.");
+ }
}
}
- orderItemStmt.executeBatch();
- log.info("OrderItems saved for prescription_id: {}", newPrescriptionId);
- }
- }
- // 3. Insert/Update Checkup with the correct newPrescriptionId
- String checkupSql = """
- INSERT INTO Checkup (
- checkup_date, checkup_id, customer_id, doctor_id, suggestion, diagnosis,
- prescription_id, notes, status, checkup_type, conclusion, reCheckupDate,
- customer_weight, customer_height, heart_beat, blood_pressure, doctor_ultrasound_id
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
- ON CONFLICT(checkup_id) DO UPDATE SET
- suggestion = excluded.suggestion, diagnosis = excluded.diagnosis,
- prescription_id = excluded.prescription_id, notes = excluded.notes,
- status = excluded.status, checkup_type = excluded.checkup_type,
- conclusion = excluded.conclusion, reCheckupDate = excluded.reCheckupDate,
- customer_weight = excluded.customer_weight, customer_height = excluded.customer_height,
- heart_beat = excluded.heart_beat, blood_pressure = excluded.blood_pressure,
- doctor_ultrasound_id = excluded.doctor_ultrasound_id, doctor_id = excluded.doctor_id,
- checkup_date = excluded.checkup_date
- """;
-
- try (PreparedStatement checkupStmt = conn.prepareStatement(checkupSql)) {
- checkupStmt.setLong(1, saveCheckupRequest.getCheckupDate());
- checkupStmt.setInt(2, saveCheckupRequest.getCheckupId());
- checkupStmt.setInt(3, saveCheckupRequest.getCustomerId());
- checkupStmt.setInt(4, saveCheckupRequest.getDoctorId());
- checkupStmt.setString(5, saveCheckupRequest.getSuggestions());
- checkupStmt.setString(6, saveCheckupRequest.getDiagnosis());
- // Use setObject to handle the case where newPrescriptionId is null
- checkupStmt.setObject(7, newPrescriptionId);
- checkupStmt.setString(8, saveCheckupRequest.getNotes());
- checkupStmt.setString(9, saveCheckupRequest.getStatus());
- checkupStmt.setString(10, saveCheckupRequest.getCheckupType());
- checkupStmt.setString(11, saveCheckupRequest.getConclusion());
- if (saveCheckupRequest.getReCheckupDate() != null) {
- checkupStmt.setLong(12, saveCheckupRequest.getReCheckupDate());
- } else {
- checkupStmt.setLong(12, 0);
+ // B. Chèn các chi tiết thuốc vào OrderItem bằng batch
+ String orderItemSql = "INSERT INTO OrderItem (prescription_id, med_id, quantity_ordered, dosage, price_per_unit, total_price, checkup_id, notes) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
+ try (PreparedStatement orderItemStmt = conn.prepareStatement(orderItemSql)) {
+ for (String[] medicine : saveCheckupRequest.getMedicinePrescription()) {
+ if (medicine.length >= 10) {
+ orderItemStmt.setInt(1, newPrescriptionId);
+ orderItemStmt.setString(2, medicine[0]); // med_id
+ orderItemStmt.setString(3, medicine[2]); // quantity
+ String dosage = String.format("Sáng %s, Trưa %s, Chiều %s", medicine[4], medicine[5], medicine[6]);
+ orderItemStmt.setString(4, dosage);
+ orderItemStmt.setString(5, medicine[7]); // unit_price
+ orderItemStmt.setString(6, medicine[8]); // total_price
+ orderItemStmt.setInt(7, saveCheckupRequest.getCheckupId());
+ orderItemStmt.setString(8, medicine[9]); // notes
+ orderItemStmt.addBatch();
+ }
+ }
+ orderItemStmt.executeBatch();
+ }
}
- checkupStmt.setDouble(13, saveCheckupRequest.getCustomerWeight());
- checkupStmt.setDouble(14, saveCheckupRequest.getCustomerHeight());
- checkupStmt.setInt(15, saveCheckupRequest.getHeartBeat());
- checkupStmt.setString(16, saveCheckupRequest.getBloodPressure());
- checkupStmt.setInt(17, saveCheckupRequest.getDoctorUltrasoundId());
- checkupStmt.executeUpdate();
- log.info("Checkup saved successfully");
- }
-
- // 4. Handle Service Prescriptions
- // clear old data
- try (PreparedStatement deleteServiceStmt = conn.prepareStatement("DELETE FROM CheckupService WHERE checkup_id = ?")) {
- deleteServiceStmt.setInt(1, saveCheckupRequest.getCheckupId());
- deleteServiceStmt.executeUpdate();
- }
- log.info("Cleared previous service prescription for checkup_id: {}", saveCheckupRequest.getCheckupId());
-
- String serviceSql = """
- INSERT INTO CheckupService (checkup_id, service_id, quantity, total_cost, notes)
- VALUES (?, ?, ?, ?, ?)
- """;
- try (PreparedStatement serviceStmt = conn.prepareStatement(serviceSql)) {
- for (String[] service : saveCheckupRequest.getServicePrescription()) {
- serviceStmt.setInt(1, saveCheckupRequest.getCheckupId());
- serviceStmt.setString(2, service[0]);
- serviceStmt.setInt(3, Integer.parseInt(service[2]));
- serviceStmt.setDouble(4, Double.parseDouble(service[4]));
- serviceStmt.setString(5, service[5]);
- serviceStmt.executeUpdate();
+
+ // 4. CẬP NHẬT THÔNG TIN LẦN KHÁM (CHECKUP)
+ String checkupSql = """
+ UPDATE Checkup SET
+ suggestion = ?, diagnosis = ?, prescription_id = ?, notes = ?, status = ?,
+ checkup_type = ?, conclusion = ?, reCheckupDate = ?, customer_weight = ?,
+ customer_height = ?, heart_beat = ?, blood_pressure = ?,
+ doctor_ultrasound_id = ?, doctor_id = ?, checkup_date = ?
+ WHERE checkup_id = ?
+ """;
+ try (PreparedStatement checkupStmt = conn.prepareStatement(checkupSql)) {
+ checkupStmt.setString(1, saveCheckupRequest.getSuggestions());
+ checkupStmt.setString(2, saveCheckupRequest.getDiagnosis());
+ checkupStmt.setObject(3, newPrescriptionId); // setObject an toàn cho giá trị null
+ checkupStmt.setString(4, saveCheckupRequest.getNotes());
+ checkupStmt.setString(5, saveCheckupRequest.getStatus());
+ checkupStmt.setString(6, saveCheckupRequest.getCheckupType());
+ checkupStmt.setString(7, saveCheckupRequest.getConclusion());
+ checkupStmt.setObject(8, saveCheckupRequest.getReCheckupDate()); // setObject an toàn cho null
+ checkupStmt.setDouble(9, saveCheckupRequest.getCustomerWeight());
+ checkupStmt.setDouble(10, saveCheckupRequest.getCustomerHeight());
+ checkupStmt.setInt(11, saveCheckupRequest.getHeartBeat());
+ checkupStmt.setString(12, saveCheckupRequest.getBloodPressure());
+ checkupStmt.setInt(13, saveCheckupRequest.getDoctorUltrasoundId());
+ checkupStmt.setInt(14, saveCheckupRequest.getDoctorId());
+ checkupStmt.setLong(15, saveCheckupRequest.getCheckupDate());
+ checkupStmt.setInt(16, saveCheckupRequest.getCheckupId()); // WHERE clause
+ checkupStmt.executeUpdate();
}
- log.info("Service saved successfully");
- }
-
- conn.commit(); // Commit the entire transaction
- log.info("Successfully saved checkup {} with all related data", saveCheckupRequest.getCheckupId());
- UserUtil.sendPacket(currentUser.getSessionId(), new SaveCheckupRes(true, "Checkup saved successfully"));
-
- } catch (Exception e) {
- log.error("Error saving checkup transaction", e);
- if (conn != null) {
- try {
- conn.rollback();
- log.info("Transaction rolled back due to error");
- } catch (SQLException rollbackEx) {
- log.error("Error rolling back transaction", rollbackEx);
+
+ // 5. XỬ LÝ DỊCH VỤ (SERVICES)
+ try (PreparedStatement deleteStmt = conn.prepareStatement("DELETE FROM CheckupService WHERE checkup_id = ?")) {
+ deleteStmt.setInt(1, saveCheckupRequest.getCheckupId());
+ deleteStmt.executeUpdate();
}
+ log.info("Cleared previous services for checkup_id: {}", saveCheckupRequest.getCheckupId());
+
+ // TỐI ƯU: Dùng batch để chèn dịch vụ
+ if (saveCheckupRequest.getServicePrescription() != null && saveCheckupRequest.getServicePrescription().length > 0) {
+ String serviceSql = "INSERT INTO CheckupService (checkup_id, service_id, quantity, total_cost, notes) VALUES (?, ?, ?, ?, ?)";
+ try (PreparedStatement serviceStmt = conn.prepareStatement(serviceSql)) {
+ for (String[] service : saveCheckupRequest.getServicePrescription()) {
+ serviceStmt.setInt(1, saveCheckupRequest.getCheckupId());
+ serviceStmt.setString(2, service[0]);
+ serviceStmt.setInt(3, Integer.parseInt(service[2]));
+ serviceStmt.setDouble(4, Double.parseDouble(service[4]));
+ serviceStmt.setString(5, service[5]);
+ serviceStmt.addBatch();
+ }
+ serviceStmt.executeBatch();
+ }
+ }
+
+ // 6. Hoàn tất và COMMIT giao dịch
+ conn.commit();
+ log.info("Successfully saved checkup transaction for checkup_id: {}", saveCheckupRequest.getCheckupId());
+ UserUtil.sendPacket(currentUser.getSessionId(), new SaveCheckupRes(true, "Lưu thông tin khám bệnh thành công."));
+
+ } catch (SQLException e) {
+ // Nếu có lỗi, ROLLBACK toàn bộ giao dịch
+ log.error("Error during save checkup transaction, rolling back.", e);
+ conn.rollback();
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
}
+ // Không cần khối `finally` để reset auto-commit. HikariCP sẽ tự lo việc này.
+
+ } catch (SQLException e) {
+ // Lỗi này xảy ra nếu không thể lấy kết nối từ pool hoặc có lỗi khi rollback
+ log.error("Critical error during database transaction management for SaveCheckupRequest.", e);
UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
- } finally {
- if (conn != null) {
- try {
- conn.setAutoCommit(true); // Restore auto-commit mode
- } catch (SQLException e) {
- log.error("Error restoring auto-commit", e);
- }
- }
}
- }
+ }
if (packet instanceof GetOrderInfoByCheckupReq getOrderInfoByCheckupReq) {
log.debug("Received GetOrderInfoByCheckupReq for checkupId: {}", getOrderInfoByCheckupReq.getCheckupId());
@@ -919,151 +900,138 @@ public class ServerHandler extends SimpleChannelInboundHandler medList = new ArrayList<>();
- while(medRs.next()) {
- String[] med = new String[12];
- med[0] = medRs.getString("med_id");
- med[1] = medRs.getString("med_name");
- med[2] = medRs.getString("quantity_ordered");
- med[3] = medRs.getString("med_unit");
+
+ // --- SỬA ĐỔI: Lấy thông tin đơn thuốc (Medicine Prescription) ---
+ // Sử dụng try-with-resources để quản lý TẤT CẢ tài nguyên JDBC (Connection, PreparedStatement, ResultSet)
+ String medSql = """
+ SELECT M.med_id, M.med_name, OI.quantity_ordered, M.med_unit, OI.dosage,
+ OI.price_per_unit, OI.total_price, OI.notes, M.supplement, M.route
+ FROM OrderItem OI
+ JOIN Medicine M ON OI.med_id = M.med_id
+ WHERE OI.checkup_id = ?
+ """;
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement medStmt = conn.prepareStatement(medSql)) {
- // Parse dosage: "Sáng 1, Trưa 1, Chiều 1"
- String dosage = medRs.getString("dosage");
- String morning = "0", noon = "0", evening = "0";
- if (dosage != null && !dosage.isEmpty()) {
- String[] parts = dosage.split(", ");
- for (String part : parts) {
- String[] dosagePart = part.split(" ");
- if (dosagePart.length == 2) {
- if ("Sáng".equals(dosagePart[0])) morning = dosagePart[1];
- else if ("Trưa".equals(dosagePart[0])) noon = dosagePart[1];
- else if ("Chiều".equals(dosagePart[0])) evening = dosagePart[1];
+ medStmt.setString(1, checkupId);
+ try (ResultSet medRs = medStmt.executeQuery()) {
+ ArrayList medList = new ArrayList<>();
+ while(medRs.next()) {
+ String[] med = new String[12];
+ med[0] = medRs.getString("med_id");
+ med[1] = medRs.getString("med_name");
+ med[2] = medRs.getString("quantity_ordered");
+ med[3] = medRs.getString("med_unit");
+
+ // Parse dosage: "Sáng 1, Trưa 1, Chiều 1"
+ String dosage = medRs.getString("dosage");
+ String morning = "0", noon = "0", evening = "0";
+ if (dosage != null && !dosage.isEmpty()) {
+ String[] parts = dosage.split(", ");
+ for (String part : parts) {
+ String[] dosagePart = part.split(" ");
+ if (dosagePart.length == 2) {
+ if ("Sáng".equals(dosagePart[0])) morning = dosagePart[1];
+ else if ("Trưa".equals(dosagePart[0])) noon = dosagePart[1];
+ else if ("Chiều".equals(dosagePart[0])) evening = dosagePart[1];
+ }
+ }
}
+ med[4] = morning;
+ med[5] = noon;
+ med[6] = evening;
+
+ med[7] = medRs.getString("price_per_unit");
+ med[8] = medRs.getString("total_price");
+ med[9] = medRs.getString("notes");
+ med[10] = medRs.getString("supplement");
+ med[11] = medRs.getString("route");
+ medList.add(med);
}
+ medicinePrescription = medList.toArray(new String[0][]);
}
- med[4] = morning;
- med[5] = noon;
- med[6] = evening;
-
- med[7] = medRs.getString("price_per_unit");
- med[8] = medRs.getString("total_price");
- med[9] = medRs.getString("notes");
- med[10] = medRs.getString("supplement");
- med[11] = medRs.getString("route");
- medList.add(med);
- }
- medicinePrescription = medList.toArray(new String[0][]);
-
} catch (SQLException e) {
- log.error("Error getting medicine prescription for checkupId: " + checkupId, e);
+ log.error("Error getting medicine prescription for checkupId: " + checkupId, e);
+ // Nếu có lỗi, medicinePrescription sẽ vẫn là null
}
- // Get Service Prescription
- try {
- PreparedStatement serStmt = Server.connection.prepareStatement(
- """
- SELECT
- S.service_id,
- S.service_name,
- CS.quantity,
- S.service_cost,
- CS.total_cost,
- CS.notes
+ // --- SỬA ĐỔI: Lấy thông tin dịch vụ (Service Prescription) ---
+ String serSql = """
+ SELECT S.service_id, S.service_name, CS.quantity,
+ S.service_cost, CS.total_cost, CS.notes
FROM CheckupService CS
JOIN Service S ON CS.service_id = S.service_id
WHERE CS.checkup_id = ?
- """
- );
- serStmt.setString(1, checkupId);
- ResultSet serRs = serStmt.executeQuery();
-
- ArrayList serList = new ArrayList<>();
- while(serRs.next()) {
- String[] ser = new String[6];
- ser[0] = serRs.getString("service_id");
- ser[1] = serRs.getString("service_name");
- ser[2] = serRs.getString("quantity");
- ser[3] = serRs.getString("service_cost");
- ser[4] = serRs.getString("total_cost");
- ser[5] = serRs.getString("notes");
- serList.add(ser);
- }
- servicePrescription = serList.toArray(new String[0][]);
-
+ """;
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement serStmt = conn.prepareStatement(serSql)) {
+
+ serStmt.setString(1, checkupId);
+ try (ResultSet serRs = serStmt.executeQuery()) {
+ ArrayList serList = new ArrayList<>();
+ while(serRs.next()) {
+ String[] ser = new String[6];
+ ser[0] = serRs.getString("service_id");
+ ser[1] = serRs.getString("service_name");
+ ser[2] = serRs.getString("quantity");
+ ser[3] = serRs.getString("service_cost");
+ ser[4] = serRs.getString("total_cost");
+ ser[5] = serRs.getString("notes");
+ serList.add(ser);
+ }
+ servicePrescription = serList.toArray(new String[0][]);
+ }
} catch (SQLException e) {
- log.error("Error getting service prescription for checkupId: " + checkupId, e);
+ log.error("Error getting service prescription for checkupId: " + checkupId, e);
+ // Nếu có lỗi, servicePrescription sẽ vẫn là null
}
UserUtil.sendPacket(currentUser.getSessionId(), new GetOrderInfoByCheckupRes(medicinePrescription, servicePrescription));
log.info("Sent order info for checkup {} to client", checkupId);
- }
- if (packet instanceof AddTemplateReq addTemplateReq) {
+ }
+
+ if (packet instanceof AddTemplateReq addTemplateReq) {
log.debug("Received AddTemplateReq: {}", addTemplateReq);
- String templateName = addTemplateReq.getTemplateName();
- String templateTitle = addTemplateReq.getTemplateTitle();
- String templateDiagnosis = addTemplateReq.getTemplateDiagnosis();
- String templateConclusion = addTemplateReq.getTemplateConclusion();
- String templateSuggestion = addTemplateReq.getTemplateSuggestion();
- String templateImageCount = addTemplateReq.getTemplateImageCount();
- String templatePrintType = addTemplateReq.getTemplatePrintType();
- String templateGender = addTemplateReq.getTemplateGender();
- String templateContent = addTemplateReq.getTemplateContent();
- boolean templateVisible = addTemplateReq.isVisible();
- int templateStt = addTemplateReq.getStt();
- try {
- PreparedStatement templateStmt = Server.connection.prepareStatement(
- """
- INSERT INTO CheckupTemplate (template_gender, template_name, template_title, photo_num, print_type, content, conclusion, suggestion, diagnosis, visible, stt)
+
+ // --- SỬA ĐỔI: Sử dụng try-with-resources để đảm bảo an toàn và tự động đóng tài nguyên ---
+ String sql = """
+ INSERT INTO CheckupTemplate (template_gender, template_name, template_title, photo_num,
+ print_type, content, conclusion, suggestion, diagnosis,
+ visible, stt)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
- """);
- templateStmt.setString(1, templateGender);
- templateStmt.setString(2, templateName);
- templateStmt.setString(3, templateTitle);
- templateStmt.setString(4, templateImageCount);
- templateStmt.setString(5, templatePrintType);
- templateStmt.setString(6, templateContent);
- templateStmt.setString(7, templateConclusion);
- templateStmt.setString(8, templateSuggestion);
- templateStmt.setString(9, templateDiagnosis);
- templateStmt.setBoolean(10, templateVisible);
- templateStmt.setInt(11, templateStt);
- templateStmt.executeUpdate();
- log.info("Template saved successfully");
- UserUtil.sendPacket(currentUser.getSessionId(), new AddTemplateRes(true, "Template saved successfully"));
+ """;
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement templateStmt = conn.prepareStatement(sql)) {
+
+ templateStmt.setString(1, addTemplateReq.getTemplateGender());
+ templateStmt.setString(2, addTemplateReq.getTemplateName());
+ templateStmt.setString(3, addTemplateReq.getTemplateTitle());
+ templateStmt.setString(4, addTemplateReq.getTemplateImageCount());
+ templateStmt.setString(5, addTemplateReq.getTemplatePrintType());
+ templateStmt.setString(6, addTemplateReq.getTemplateContent());
+ templateStmt.setString(7, addTemplateReq.getTemplateConclusion());
+ templateStmt.setString(8, addTemplateReq.getTemplateSuggestion());
+ templateStmt.setString(9, addTemplateReq.getTemplateDiagnosis());
+ templateStmt.setBoolean(10, addTemplateReq.isVisible());
+ templateStmt.setInt(11, addTemplateReq.getStt());
+ templateStmt.executeUpdate();
+
+ log.info("Template saved successfully");
+ UserUtil.sendPacket(currentUser.getSessionId(), new AddTemplateRes(true, "Template saved successfully"));
} catch (SQLException e) {
- log.error("Error saving template", e);
- UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
+ log.error("Error saving template", e);
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
}
}
if (packet instanceof GetAllTemplatesReq) {
log.debug("Received GetAllTemplatesReq");
- try {
- PreparedStatement stmt = Server.connection.prepareStatement("SELECT * FROM CheckupTemplate");
- ResultSet rs = stmt.executeQuery();
+
+ // --- SỬA ĐỔI: Sử dụng try-with-resources để quản lý TẤT CẢ tài nguyên ---
+ String sql = "SELECT * FROM CheckupTemplate ORDER BY stt"; // Thêm ORDER BY để sắp xếp
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql);
+ ResultSet rs = stmt.executeQuery()) {
+
List templates = new ArrayList<>();
while (rs.next()) {
templates.add(new Template(
@@ -1082,21 +1050,28 @@ public class ServerHandler extends SimpleChannelInboundHandler 0) {
+ log.info("Template {} updated successfully", template.getTemplateId());
+ UserUtil.sendPacket(currentUser.getSessionId(), new EditTemplateRes(true, "Cập nhật mẫu thành công."));
+ } else {
+ log.warn("Template {} not found for update.", template.getTemplateId());
+ UserUtil.sendPacket(currentUser.getSessionId(), new EditTemplateRes(false, "Không tìm thấy mẫu để cập nhật."));
+ }
+
} catch (SQLException e) {
- log.error("Error updating template", e);
- UserUtil.sendPacket(currentUser.getSessionId(), new EditTemplateRes(false, "Error updating template: " + e.getMessage()));
+ log.error("Error updating template {}", template.getTemplateId(), e);
+ UserUtil.sendPacket(currentUser.getSessionId(), new EditTemplateRes(false, "Lỗi khi cập nhật mẫu: " + e.getMessage()));
}
- }
- if (packet instanceof DeleteTemplateReq deleteTemplateReq) {
- log.debug("Received DeleteTemplateReq: {}", deleteTemplateReq);
- try {
- PreparedStatement stmt = Server.connection.prepareStatement("DELETE FROM CheckupTemplate WHERE template_id = ?");
+ }
+
+ if (packet instanceof DeleteTemplateReq deleteTemplateReq) {
+ log.debug("Received DeleteTemplateReq for templateId: {}", deleteTemplateReq.getTemplateId());
+
+ // --- SỬA ĐỔI: Sử dụng try-with-resources ---
+ String sql = "DELETE FROM CheckupTemplate WHERE template_id = ?";
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+
stmt.setInt(1, deleteTemplateReq.getTemplateId());
- stmt.executeUpdate();
- log.info("Template deleted successfully");
- UserUtil.sendPacket(currentUser.getSessionId(), new DeleteTemplateRes(true, "Template deleted successfully"));
+ int affectedRows = stmt.executeUpdate();
+
+ if (affectedRows > 0) {
+ log.info("Template {} deleted successfully", deleteTemplateReq.getTemplateId());
+ UserUtil.sendPacket(currentUser.getSessionId(), new DeleteTemplateRes(true, "Xóa mẫu thành công."));
+ } else {
+ log.warn("Template {} not found for deletion.", deleteTemplateReq.getTemplateId());
+ UserUtil.sendPacket(currentUser.getSessionId(), new DeleteTemplateRes(false, "Không tìm thấy mẫu để xóa."));
+ }
+
} catch (SQLException e) {
- log.error("Error deleting template", e);
- UserUtil.sendPacket(currentUser.getSessionId(), new DeleteTemplateRes(false, "Error deleting template: " + e.getMessage()));
+ log.error("Error deleting template {}", deleteTemplateReq.getTemplateId(), e);
+ UserUtil.sendPacket(currentUser.getSessionId(), new DeleteTemplateRes(false, "Lỗi khi xóa mẫu: " + e.getMessage()));
}
- }
-
- if (packet instanceof UploadCheckupImageRequest uploadCheckupImageRequest) {
+ }
+
+ if (packet instanceof UploadCheckupImageRequest uploadCheckupImageRequest) {
log.info("Received UploadCheckupImageRequest for checkupId: {}", uploadCheckupImageRequest.getCheckupId());
-
+
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));
+ UserUtil.sendPacket(currentUser.getSessionId(), new UploadCheckupImageResponse(false, "CheckupID không hợp lệ.", fileName));
return;
}
-
+
try {
- // 1. Define and create storage directory
+ // 1. Xác định và tạo thư mục lưu trữ
Path storageDir = Paths.get(Server.imageDbPath, checkupId.trim());
- Files.createDirectories(storageDir); // Create dirs if they don't exist
-
- // 2. Convert and save image as PNG for archival storage
- // Convert filename to PNG extension for server storage consistency
+ Files.createDirectories(storageDir);
+
+ // 2. Chuyển đổi và lưu ảnh dưới dạng PNG để đồng nhất
String pngFileName = fileName.substring(0, fileName.lastIndexOf('.')) + ".png";
Path filePath = storageDir.resolve(pngFileName);
- // Convert image data to PNG format
try (ByteArrayInputStream bais = new ByteArrayInputStream(imageData)) {
BufferedImage image = ImageIO.read(bais);
if (image != null) {
ImageIO.write(image, "PNG", filePath.toFile());
log.info("Successfully converted and saved image as PNG to {}", filePath);
} else {
- // Fallback: save as original format if conversion fails
- try (FileOutputStream fos = new FileOutputStream(storageDir.resolve(fileName).toFile())) {
- fos.write(imageData);
- }
- log.warn("Failed to convert image to PNG, saved as original format: {}", fileName);
+ throw new IOException("Could not decode image data.");
}
}
- String savedPath = filePath.toString().replace("\\", "/");
-
- // 3. TODO: Store file path in database, linking it to the checkupId
- // A proper implementation would have a 'CheckupImages' table and an INSERT query here.
-
- // 4. Send immediate success response to client
- UserUtil.sendPacket(currentUser.getSessionId(), new UploadCheckupImageResponse(true, "Image uploaded successfully to " + savedPath, fileName));
-
- // 5. Upload to Google Drive asynchronously (don't block the response)
+
+ // 4. Gửi phản hồi thành công cho client
+ UserUtil.sendPacket(currentUser.getSessionId(), new UploadCheckupImageResponse(true, "Tải ảnh lên thành công.", fileName));
+
+ // 5. Tải lên Google Drive bất đồng bộ
uploadCheckupImageToGoogleDriveAsync(checkupId, fileName, imageData);
-
+
} catch (IOException e) {
- log.error("Failed to save uploaded image for checkupId {}: {}", checkupId, e.getMessage());
- UserUtil.sendPacket(currentUser.getSessionId(), new UploadCheckupImageResponse(false, "Server failed to save image: " + e.getMessage(), fileName));
+ log.error("Failed to process uploaded image for checkupId {}: {}", checkupId, e.getMessage());
+ UserUtil.sendPacket(currentUser.getSessionId(), new UploadCheckupImageResponse(false, "Lỗi máy chủ khi lưu ảnh: " + e.getMessage(), fileName));
}
}
@@ -1310,51 +1294,64 @@ public class ServerHandler extends SimpleChannelInboundHandler 0) {
+ log.info("Updated remind_date for checkup_id: {}", addRemindDateRequest.getCheckupId());
+ // Giả sử hàm getRecheckUpList đã được triển khai an toàn
+ getRecheckUpList(currentUser.getSessionId());
+ } else {
+ log.warn("No checkup found with id {} to update remind_date.", addRemindDateRequest.getCheckupId());
+ }
+
} catch (SQLException e) {
log.error("Error updating remind_date", e);
UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
}
}
if (packet instanceof EmergencyRequest emergencyRequest) {
- log.info("Received EmergencyRequest");
- try {
- String sql = "SELECT user_name FROM User WHERE user_id = ?";
- PreparedStatement stmt = Server.connection.prepareStatement(sql);
- stmt.setInt(1, currentUser.getUserId());
- ResultSet rs = stmt.executeQuery();
- String senderName = rs.getString("user_name");
- int maxCurId = SessionManager.getMaxSessionId();
- for (int sessionId = 1; sessionId <= maxCurId; sessionId++) {
- UserUtil.sendPacket(sessionId, new EmergencyResponse(senderName));
- }
- log.info("send emergency response to all sessions");
+ log.info("Received EmergencyRequest from user_id: {}", currentUser.getUserId());
+ String senderName = "Không xác định";
+
+ // SỬA ĐỔI: Lấy tên người gửi một cách an toàn với try-with-resources
+ String sql = "SELECT user_name FROM User WHERE user_id = ?";
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+
+ stmt.setInt(1, currentUser.getUserId());
+ try (ResultSet rs = stmt.executeQuery()) {
+
+ if (rs.next()) {
+ senderName = rs.getString("user_name");
+ } else {
+ log.warn("Could not find user_name for user_id: {}", currentUser.getUserId());
+ }
+ }
} catch (SQLException e) {
- // if error still send emergency!
- log.error("Error fetching emergency response", e);
- int maxCurId = SessionManager.getMaxSessionId();
- for (int sessionId = 1; sessionId <= maxCurId; sessionId++) {
- UserUtil.sendPacket(sessionId, new EmergencyResponse("Không xác định"));
- }
- log.info("send emergency response to all sessions");
+ log.error("Error fetching sender name for emergency, will use default.", e);
+ }
+ int maxCurId = SessionManager.getMaxSessionId();
+ for (int sessionId = 1; sessionId <= maxCurId; sessionId++) {
+ UserUtil.sendPacket(sessionId, new EmergencyResponse(senderName));
}
}
if (packet instanceof GetCheckupDataRequest getCheckupDataRequest) {
if (currentUser.getRole() != Role.ADMIN) {
- UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.ACCESS_DENIED));
- return;
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.ACCESS_DENIED));
+ return;
}
log.info("Processing GetCheckupDataRequest for page {} with all info.", getCheckupDataRequest.getPage());
- try {
- // --- 1. Base Query Construction (UPDATED to select all fields) ---
+
+ try (Connection conn = DatabaseManager.getConnection()) {
String baseSelect = "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, " +
@@ -1364,28 +1361,29 @@ public class ServerHandler extends SimpleChannelInboundHandler params = new java.util.ArrayList<>();
boolean hasWhere = false;
-
+
String checkupIdSearch = getCheckupDataRequest.getCheckupIdSearch();
if (checkupIdSearch != null && !checkupIdSearch.trim().isEmpty()) {
whereClause.append("WHERE a.checkup_id = ?");
try {
- // Chuyển đổi sang kiểu số để tìm kiếm chính xác và an toàn
params.add(Integer.parseInt(checkupIdSearch.trim()));
} catch (NumberFormatException e) {
- // Nếu người dùng nhập chữ, ta truyền một giá trị không thể tìm thấy để trả về 0 kết quả
params.add(-1);
}
hasWhere = true;
}
else if (getCheckupDataRequest.getSearchTerm() != null && !getCheckupDataRequest.getSearchTerm().trim().isEmpty()) {
+ // SỬA LỖI: SQLite dùng || để nối chuỗi, không phải CONCAT()
whereClause.append("WHERE (LOWER(c.customer_first_name) LIKE ? OR LOWER(c.customer_last_name) LIKE ? OR " +
- "LOWER(CONCAT(c.customer_last_name, ' ', c.customer_first_name)) LIKE ? OR c.customer_number LIKE ?)");
+ "LOWER(c.customer_last_name || ' ' || c.customer_first_name) LIKE ? OR c.customer_number LIKE ?)");
String searchTerm = "%" + getCheckupDataRequest.getSearchTerm().toLowerCase().trim() + "%";
- params.add(searchTerm); params.add(searchTerm); params.add(searchTerm); params.add(searchTerm);
+ params.add(searchTerm);
+ params.add(searchTerm);
+ params.add(searchTerm);
+ params.add(searchTerm);
hasWhere = true;
}
@@ -1401,99 +1399,99 @@ public class ServerHandler extends SimpleChannelInboundHandler resultList = new java.util.ArrayList<>();
- while (rs.next()) {
- // Retrieve all 25 fields for the Patient DTO
- String checkupId = rs.getString("checkup_id");
- String checkupDate = String.valueOf(rs.getLong("checkup_date"));
- String customerLastName = rs.getString("customer_last_name");
- String customerFirstName = rs.getString("customer_first_name");
- String doctorName = "BS. " + rs.getString("doctor_last_name") + " " + rs.getString("doctor_first_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 = String.valueOf(rs.getBigDecimal("customer_weight"));
- String customerHeight = String.valueOf(rs.getBigDecimal("customer_height"));
- String customerGender = rs.getString("customer_gender");
- String customerDob = String.valueOf(rs.getLong("customer_dob"));
- String checkupType = rs.getString("checkup_type");
- String conclusion = rs.getString("conclusion");
- String reCheckupDate = String.valueOf(rs.getLong("reCheckupDate"));
- String cccdDdcn = rs.getString("cccd_ddcn");
- String heartBeat = String.valueOf(rs.getInt("heart_beat"));
- String bloodPressure = rs.getString("blood_pressure");
- String driveUrl = rs.getString("drive_url");
- String doctorUltrasoundId = String.valueOf(rs.getInt("doctor_ultrasound_id"));
- String queueNumber = String.valueOf(rs.getInt("queue_number"));
+ try (PreparedStatement stmt = conn.prepareStatement(finalQuery)) {
+ int paramIndex = 1;
+ for (Object param : params) {
+ stmt.setObject(paramIndex++, param);
+ }
+ stmt.setInt(paramIndex++, pageSize);
+ stmt.setInt(paramIndex, offset);
+
+ try (ResultSet rs = stmt.executeQuery()) {
+ while (rs.next()) {
+ String checkupId = rs.getString("checkup_id");
+ String checkupDate = String.valueOf(rs.getLong("checkup_date"));
+ String customerLastName = rs.getString("customer_last_name");
+ String customerFirstName = rs.getString("customer_first_name");
+ String doctorName = "BS. " + rs.getString("doctor_last_name") + " " + rs.getString("doctor_first_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 = String.valueOf(rs.getBigDecimal("customer_weight"));
+ String customerHeight = String.valueOf(rs.getBigDecimal("customer_height"));
+ String customerGender = rs.getString("customer_gender");
+ String customerDob = String.valueOf(rs.getLong("customer_dob"));
+ String checkupType = rs.getString("checkup_type");
+ String conclusion = rs.getString("conclusion");
+ String reCheckupDate = String.valueOf(rs.getLong("reCheckupDate"));
+ String cccdDdcn = rs.getString("cccd_ddcn");
+ String heartBeat = String.valueOf(rs.getInt("heart_beat"));
+ String bloodPressure = rs.getString("blood_pressure");
+ String driveUrl = rs.getString("drive_url");
+ String doctorUltrasoundId = String.valueOf(rs.getInt("doctor_ultrasound_id"));
+ String queueNumber = String.valueOf(rs.getInt("queue_number"));
- // Add the full data array, handling nulls to prevent client-side errors
- resultList.add(new String[]{
- checkupId != null ? checkupId : "",
- checkupDate != null ? checkupDate : "0",
- customerLastName != null ? customerLastName : "",
- customerFirstName != null ? customerFirstName : "",
- doctorName, // Already constructed
- suggestion != null ? suggestion : "",
- diagnosis != null ? diagnosis : "",
- notes != null ? notes : "",
- status != null ? status : "",
- customerId != null ? customerId : "",
- customerNumber != null ? customerNumber : "",
- customerAddress != null ? customerAddress : "",
- customerWeight != null ? customerWeight : "0.0",
- customerHeight != null ? customerHeight : "0.0",
- customerGender != null ? customerGender : "",
- customerDob != null ? customerDob : "0",
- checkupType != null ? checkupType : "",
- conclusion != null ? conclusion : "",
- reCheckupDate != null ? reCheckupDate : "0",
- cccdDdcn != null ? cccdDdcn : "",
- heartBeat != null ? heartBeat : "0",
- bloodPressure != null ? bloodPressure : "0/0",
- driveUrl != null ? driveUrl : "",
- doctorUltrasoundId != null ? doctorUltrasoundId : "0",
- queueNumber != null ? queueNumber : "0"
- });
+ resultList.add(new String[]{
+ checkupId != null ? checkupId : "",
+ checkupDate != null ? checkupDate : "0",
+ customerLastName != null ? customerLastName : "",
+ customerFirstName != null ? customerFirstName : "",
+ doctorName,
+ suggestion != null ? suggestion : "",
+ diagnosis != null ? diagnosis : "",
+ notes != null ? notes : "",
+ status != null ? status : "",
+ customerId != null ? customerId : "",
+ customerNumber != null ? customerNumber : "",
+ customerAddress != null ? customerAddress : "",
+ customerWeight != null ? customerWeight : "0.0",
+ customerHeight != null ? customerHeight : "0.0",
+ customerGender != null ? customerGender : "",
+ customerDob != null ? customerDob : "0",
+ checkupType != null ? checkupType : "",
+ conclusion != null ? conclusion : "",
+ reCheckupDate != null ? reCheckupDate : "0",
+ cccdDdcn != null ? cccdDdcn : "",
+ heartBeat != null ? heartBeat : "0",
+ bloodPressure != null ? bloodPressure : "0/0",
+ driveUrl != null ? driveUrl : "",
+ doctorUltrasoundId != null ? doctorUltrasoundId : "0",
+ queueNumber != null ? queueNumber : "0"
+ });
+ }
+ }
}
String[][] resultArray = resultList.toArray(new String[0][]);
UserUtil.sendPacket(currentUser.getSessionId(), new GetCheckupDataResponse(resultArray, totalRecords, currentPage, totalPages, pageSize));
- rs.close();
- stmt.close();
} catch (SQLException e) {
log.error("Error processing GetCheckupDataRequest", e);
+ UserUtil.sendPacket(currentUser.getSessionId(), new GetCheckupDataResponse(new String[0][0], 0, 1, 0, getCheckupDataRequest.getPageSize()));
}
}
if (packet instanceof SimpleMessageRequest simpleMessageRequest) {
@@ -1507,228 +1505,350 @@ public class ServerHandler extends SimpleChannelInboundHandler 0) {
+ log.info("Updated medicine successfully: {}", editMedicineRequest.getName());
+ // Giả sử hàm getMedInfo đã được triển khai an toàn
+ getMedInfo(currentUser.getSessionId());
+ } else {
+ log.warn("No medicine found with ID {} to update.", editMedicineRequest.getId());
+ }
+
} catch (SQLException e) {
- log.error("Error updating medicine", e);
- UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
+ log.error("Error updating medicine", e);
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
}
}
if (packet instanceof AddServiceRequest addServiceRequest) {
if (!currentUser.getRole().equals(Role.ADMIN)) {
- UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.ACCESS_DENIED));
- return;
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.ACCESS_DENIED));
+ return;
}
- try {
- String sql = "INSERT INTO Service (service_name, service_cost, deleted) VALUES (?, ?, ?)";
- PreparedStatement stmt = Server.connection.prepareStatement(sql);
- stmt.setString(1, addServiceRequest.getName());
- stmt.setDouble(2, addServiceRequest.getPrice());
- stmt.setBoolean(3, addServiceRequest.getDeleted());
- stmt.executeUpdate();
- log.info("Added service: {}", addServiceRequest.getName());
- getSerInfo(currentUser.getSessionId());
+ log.info("Received AddServiceRequest for service: {}", addServiceRequest.getName());
+
+ String sql = "INSERT INTO Service (service_name, service_cost, deleted) VALUES (?, ?, ?)";
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+
+ stmt.setString(1, addServiceRequest.getName());
+ stmt.setDouble(2, addServiceRequest.getPrice());
+ stmt.setBoolean(3, addServiceRequest.getDeleted());
+ stmt.executeUpdate();
+
+ log.info("Added service successfully: {}", addServiceRequest.getName());
+ // Giả sử hàm getSerInfo đã được triển khai an toàn
+ getSerInfo(currentUser.getSessionId());
+
} catch (SQLException e) {
- log.error("Error adding service", e);
- UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
+ log.error("Error adding service", e);
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
}
- }
- if (packet instanceof EditServiceRequest editServiceRequest) {
- if (!currentUser.getRole().equals(Role.ADMIN)) {
- UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.ACCESS_DENIED));
- return;
- }
- try {
- String sql = "UPDATE Service SET service_name = ?, service_cost = ?, deleted = ? WHERE service_id = ?";
- PreparedStatement stmt = Server.connection.prepareStatement(sql);
- stmt.setString(1, editServiceRequest.getName());
- stmt.setDouble(2, editServiceRequest.getPrice());
- stmt.setBoolean(3, editServiceRequest.getDeleted());
- stmt.setString(4, editServiceRequest.getId());
- stmt.executeUpdate();
- log.info("Updated service: {}", editServiceRequest.getName());
- getSerInfo(currentUser.getSessionId());
- } catch (SQLException e) {
- log.error("Error updating service", e);
- UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
- }
- }
-
- if (packet instanceof GetAllUserInfoRequest getAllUserRequest) {
- if (!currentUser.getRole().equals(Role.ADMIN)) {
- UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.ACCESS_DENIED));
- return;
- }
- log.info("Received GetAllUserRequest");
- getAllUser(currentUser.getSessionId());
- }
-
- if (packet instanceof AddUserRequest addUserRequest) {
- if (!currentUser.getRole().equals(Role.ADMIN)) {
- UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.ACCESS_DENIED));
- return;
- }
- try {
- String sql = "INSERT INTO User (user_name, password, last_name, first_name, role_name, deleted) VALUES (?, ?, ?, ?, ?, ?)";
- PreparedStatement stmt = Server.connection.prepareStatement(sql);
- stmt.setString(1, addUserRequest.getUserName());
- stmt.setString(2, addUserRequest.getPassword());
- stmt.setString(3, addUserRequest.getLastName());
- stmt.setString(4, addUserRequest.getFirstName());
- stmt.setString(5, addUserRequest.getRole());
- stmt.setBoolean(6, addUserRequest.getDeleted());
- stmt.executeUpdate();
- log.info("Added user: {}", addUserRequest.getUserName());
- getAllUser(currentUser.getSessionId());
- } catch (SQLException e) {
- log.error("Error adding user", e);
- UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
- }
}
-
- if (packet instanceof EditUserRequest editUserRequest) {
- if (!currentUser.getRole().equals(Role.ADMIN)) {
- UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.ACCESS_DENIED));
- return;
+
+ if (packet instanceof EditServiceRequest editServiceRequest) {
+ if (!currentUser.getRole().equals(Role.ADMIN)) {
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.ACCESS_DENIED));
+ return;
+ }
+ log.info("Received EditServiceRequest for service ID: {}", editServiceRequest.getId());
+
+ String sql = "UPDATE Service SET service_name = ?, service_cost = ?, deleted = ? WHERE service_id = ?";
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+
+ stmt.setString(1, editServiceRequest.getName());
+ stmt.setDouble(2, editServiceRequest.getPrice());
+ stmt.setBoolean(3, editServiceRequest.getDeleted());
+ stmt.setString(4, editServiceRequest.getId());
+
+ int affectedRows = stmt.executeUpdate();
+ if (affectedRows > 0) {
+ log.info("Updated service successfully: {}", editServiceRequest.getName());
+ getSerInfo(currentUser.getSessionId());
+ } else {
+ log.warn("No service found with ID {} to update.", editServiceRequest.getId());
+ }
+
+ } catch (SQLException e) {
+ log.error("Error updating service", e);
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
+ }
}
- try {
- String sql = "UPDATE User SET user_name = ?, password = ?, last_name = ?, first_name = ?, role_name = ?, deleted = ? WHERE user_id = ?";
- PreparedStatement stmt = Server.connection.prepareStatement(sql);
- stmt.setString(1, editUserRequest.getUserName());
- stmt.setString(2, editUserRequest.getPassword());
- stmt.setString(3, editUserRequest.getLastName());
- stmt.setString(4, editUserRequest.getFirstName());
- stmt.setString(5, editUserRequest.getRole());
- stmt.setBoolean(6, editUserRequest.getDeleted());
- stmt.setString(7, editUserRequest.getId());
- stmt.executeUpdate();
- log.info("Updated user: {}", editUserRequest.getUserName());
+
+ if (packet instanceof GetAllUserInfoRequest) {
+ if (!currentUser.getRole().equals(Role.ADMIN)) {
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.ACCESS_DENIED));
+ return;
+ }
+ log.info("Received GetAllUserInfoRequest");
+ // Gọi hàm trợ giúp đã được viết lại một cách an toàn
getAllUser(currentUser.getSessionId());
- } catch (SQLException e) {
- log.error("Error updating user", e);
- UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
- }
}
+
+ if (packet instanceof AddUserRequest addUserRequest) {
+ if (!currentUser.getRole().equals(Role.ADMIN)) {
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.ACCESS_DENIED));
+ return;
+ }
+ log.info("Received AddUserRequest for username: {}", addUserRequest.getUserName());
+
+ String sql = "INSERT INTO User (user_name, password, last_name, first_name, role_name, deleted) VALUES (?, ?, ?, ?, ?, ?)";
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+
+ stmt.setString(1, addUserRequest.getUserName());
+ stmt.setString(2, addUserRequest.getPassword()); // Cân nhắc mã hóa mật khẩu trước khi lưu
+ stmt.setString(3, addUserRequest.getLastName());
+ stmt.setString(4, addUserRequest.getFirstName());
+ stmt.setString(5, addUserRequest.getRole());
+ stmt.setBoolean(6, addUserRequest.getDeleted());
+
+ stmt.executeUpdate();
+
+ log.info("Added user successfully: {}", addUserRequest.getUserName());
+ getAllUser(currentUser.getSessionId());
+
+ } catch (SQLException e) {
+ // Xử lý lỗi nếu username đã tồn tại
+ if (e.getErrorCode() == 19 && e.getMessage().contains("UNIQUE constraint failed: User.user_name")) {
+ log.warn("Attempted to add a user with a duplicate username: {}", addUserRequest.getUserName());
+ // Bạn có thể gửi một gói tin lỗi cụ thể hơn ở đây
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.USERNAME_EXISTS));
+ } else {
+ log.error("Error adding user", e);
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
+ }
+ }
+ }
+
+ if (packet instanceof EditUserRequest editUserRequest) {
+ if (!currentUser.getRole().equals(Role.ADMIN)) {
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.ACCESS_DENIED));
+ return;
+ }
+ log.info("Received EditUserRequest for user ID: {}", editUserRequest.getId());
+
+ // SỬA ĐỔI: Sử dụng try-with-resources cho kết nối và statement
+ String sql;
+ boolean isPasswordIncluded;
+
+ // Kiểm tra xem client có gửi mật khẩu mới không. Nếu không, không cập nhật mật khẩu.
+ if (editUserRequest.getPassword() != null && !editUserRequest.getPassword().isEmpty()) {
+ sql = "UPDATE User SET user_name = ?, password = ?, last_name = ?, first_name = ?, role_name = ?, deleted = ? WHERE user_id = ?";
+ isPasswordIncluded = true;
+ } else {
+ sql = "UPDATE User SET user_name = ?, last_name = ?, first_name = ?, role_name = ?, deleted = ? WHERE user_id = ?";
+ isPasswordIncluded = false;
+ }
+
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+
+ if (isPasswordIncluded) {
+ stmt.setString(1, editUserRequest.getUserName());
+ stmt.setString(2, editUserRequest.getPassword()); // Cân nhắc mã hóa mật khẩu
+ stmt.setString(3, editUserRequest.getLastName());
+ stmt.setString(4, editUserRequest.getFirstName());
+ stmt.setString(5, editUserRequest.getRole());
+ stmt.setBoolean(6, editUserRequest.getDeleted());
+ stmt.setInt(7, Integer.parseInt(editUserRequest.getId())); // SỬA LỖI: Chuyển ID sang kiểu int
+ } else {
+ stmt.setString(1, editUserRequest.getUserName());
+ stmt.setString(2, editUserRequest.getLastName());
+ stmt.setString(3, editUserRequest.getFirstName());
+ stmt.setString(4, editUserRequest.getRole());
+ stmt.setBoolean(5, editUserRequest.getDeleted());
+ stmt.setInt(6, Integer.parseInt(editUserRequest.getId())); // SỬA LỖI: Chuyển ID sang kiểu int
+ }
+
+ int affectedRows = stmt.executeUpdate();
+
+ if (affectedRows > 0) {
+ log.info("Updated user successfully: {}", editUserRequest.getUserName());
+ getAllUser(currentUser.getSessionId());
+ } else {
+ log.warn("No user found with ID {} to update.", editUserRequest.getId());
+ }
+
+ } catch (NumberFormatException e) {
+ log.error("Invalid user ID format for EditUserRequest: {}", editUserRequest.getId());
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.INVALID_INPUT));
+ } catch (SQLException e) {
+ if (e.getErrorCode() == 19 && e.getMessage().contains("UNIQUE constraint failed: User.user_name")) {
+ log.warn("Attempted to update a user with a duplicate username: {}", editUserRequest.getUserName());
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.USERNAME_EXISTS));
+ } else {
+ log.error("Error updating user", e);
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
+ }
+ }
+ }
}
}
private void getAllUser(int sessionId) {
- try {
- String sql = "SELECT user_id, user_name, last_name, password, first_name, role_name, deleted FROM User";
- PreparedStatement stmt = Server.connection.prepareStatement(sql);
- ResultSet rs = stmt.executeQuery();
- ArrayList resultList = new ArrayList<>();
- while (rs.next()) {
- String userId = rs.getString("user_id");
- String userName = rs.getString("user_name");
- String lastName = rs.getString("last_name");
- String firstName = rs.getString("first_name");
- String password = rs.getString("password");
- String role = rs.getString("role_name");
- String deleted = rs.getString("deleted");
- String result = String.join("|", userId, userName, lastName, firstName, password, role, 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("\\|");
- }
- UserUtil.sendPacket(sessionId, new GetAllUserInfoResponse(resultArray));
- } catch (SQLException e) {
- throw new RuntimeException(e);
- }
- }
-
- private void getSerInfo(int sessionId) {
- try {
- ResultSet rs = statement.executeQuery(
- "select service_id, service_name, service_cost, deleted\n" +
- " from Service"
- );
-
- if (!rs.isBeforeFirst()) {
- System.out.println("No data found in the service table.");
- } else {
+ log.debug("Fetching all users for session: {}", sessionId);
+ // SỬA LỖI BẢO MẬT: Đã loại bỏ cột 'password' khỏi câu truy vấn
+ String sql = "SELECT user_id, user_name, last_name, first_name, role_name, deleted FROM User ORDER BY user_id";
+
+ // SỬA LỖI QUAN TRỌNG: Sử dụng try-with-resources để quản lý tài nguyên an toàn
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql);
+ ResultSet rs = stmt.executeQuery()) {
+
+ // GIỮ NGUYÊN LOGIC GỐC: Xây dựng mảng String[][] qua các bước join và split
ArrayList resultList = new ArrayList<>();
while (rs.next()) {
- String serId = rs.getString("service_id");
- String serName = rs.getString("service_name");
- String serPrice = rs.getString("service_cost");
- String deleted = rs.getString("deleted");
+ String userId = rs.getString("user_id");
+ String userName = rs.getString("user_name");
+ String lastName = rs.getString("last_name");
+ String firstName = rs.getString("first_name");
+ String role = rs.getString("role_name");
+ String deleted = String.valueOf(rs.getBoolean("deleted")); // Lấy giá trị boolean an toàn
- String result = String.join("|", serId, serName, serPrice, deleted);
- resultList.add(result);
+ // Mật khẩu đã được cố ý loại bỏ khỏi câu truy vấn để bảo mật
+ String result = String.join("|", userId, userName, lastName, firstName, role, 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("\\|");
+ resultArray[i] = resultString[i].split("\\|");
}
-
- UserUtil.sendPacket(sessionId, new GetSerInfoResponse(resultArray));
- }
+
+ UserUtil.sendPacket(sessionId, new GetAllUserInfoResponse(resultArray));
+
} catch (SQLException e) {
- throw new RuntimeException(e);
+ log.error("Error fetching all users", e);
+ // SỬA LỖI: Gửi gói tin lỗi thay vì làm sập server
+ UserUtil.sendPacket(sessionId, new ErrorResponse(Error.SQL_EXCEPTION));
}
}
- private void getMedInfo(int sessionId) {
- try {
- ResultSet rs = statement.executeQuery(
- "select med_id, med_name, med_company, med_description, med_unit, med_selling_price, preferred_note, supplement, deleted, route\n" +
- " from Medicine"
- );
+ /**
+ * Lấy danh sách tất cả dịch vụ và gửi cho một session cụ thể.
+ *
+ * @param sessionId ID của session sẽ nhận dữ liệu.
+ */
+ private void getSerInfo(int sessionId) {
+ log.debug("Fetching all services for session: {}", sessionId);
+ String sql = "SELECT service_id, service_name, service_cost, deleted FROM Service ORDER BY service_id";
- if (!rs.isBeforeFirst()) {
- System.out.println("No data found in the medicine table.");
- } else {
+ // SỬA LỖI QUAN TRỌNG: Sử dụng try-with-resources để quản lý tài nguyên an toàn
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql);
+ ResultSet rs = stmt.executeQuery()) {
+
+ // GIỮ NGUYÊN LOGIC GỐC: Xây dựng mảng String[][] qua các bước join và split
+ ArrayList resultList = new ArrayList<>();
+ while (rs.next()) {
+ String serId = rs.getString("service_id");
+ String serName = rs.getString("service_name");
+ String serPrice = rs.getString("service_cost");
+ // Lấy giá trị boolean một cách an toàn và chuyển thành String
+ String deleted = String.valueOf(rs.getBoolean("deleted"));
+
+ String result = String.join("|", serId, serName, serPrice, 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("\\|");
+ }
+
+ UserUtil.sendPacket(sessionId, new GetSerInfoResponse(resultArray));
+
+ } catch (SQLException e) {
+ log.error("Error fetching services", e);
+ // SỬA LỖI: Gửi gói tin lỗi thay vì làm sập server
+ UserUtil.sendPacket(sessionId, new ErrorResponse(Error.SQL_EXCEPTION));
+ }
+ }
+
+ /**
+ * Lấy danh sách tất cả các loại thuốc và gửi cho một session cụ thể.
+ *
+ * @param sessionId ID của session sẽ nhận dữ liệu.
+ */
+ private void getMedInfo(int sessionId) {
+ log.debug("Fetching all medicines for session: {}", sessionId);
+ String sql = """
+ SELECT med_id, med_name, med_company, med_description, med_unit,
+ med_selling_price, preferred_note, supplement, deleted, route
+ FROM Medicine
+ ORDER BY med_id
+ """;
+
+ // SỬA LỖI QUAN TRỌNG: Sử dụng try-with-resources để quản lý tài nguyên an toàn
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql);
+ ResultSet rs = stmt.executeQuery()) {
+
+ // GIỮ NGUYÊN LOGIC GỐC: Xây dựng mảng String[][] qua các bước join và split
ArrayList resultList = new ArrayList<>();
while (rs.next()) {
String medId = rs.getString("med_id");
@@ -1738,65 +1858,112 @@ public class ServerHandler extends SimpleChannelInboundHandler results = new ArrayList<>();
- while (rs.next()) {
- String[] row = new String[5];
- row[0] = rs.getString("customer_last_name") + " " + rs.getString("customer_first_name");
- row[1] = rs.getString("customer_number");
+ // CẢI TIẾN: Sử dụng múi giờ cụ thể ("Asia/Ho_Chi_Minh") thay vì múi giờ mặc định của server.
+ // Điều này đảm bảo tính nhất quán dù server chạy ở bất kỳ đâu.
+ ZoneId zoneId = ZoneId.of("Asia/Ho_Chi_Minh");
+
+ // Sử dụng java.time API để tính toán ngày tháng một cách rõ ràng và an toàn.
+ long fromDate = LocalDate.now(zoneId).atStartOfDay(zoneId).toInstant().toEpochMilli();
+ // Lấy đến cuối ngày thứ 7 kể từ hôm nay để bao trọn 7 ngày.
+ long toDate = LocalDate.now(zoneId).plusDays(7).atTime(23, 59, 59).atZone(zoneId).toInstant().toEpochMilli();
- long recheckTimestamp = rs.getLong("reCheckupDate");
- long remindTimestamp = rs.getLong("remind_date");
- SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
- row[2] = sdf.format(new Date(recheckTimestamp));
- row[3] = sdf.format(new Date(remindTimestamp));
- row[4] = rs.getString("checkup_id");
- results.add(row);
- }
-
- UserUtil.sendPacket(sessionId, new GetRecheckUpListResponse(results.toArray(new String[0][])));
-
- } catch (SQLException e) {
- log.error("Error fetching recheck-up list", e);
- UserUtil.sendPacket(sessionId, new ErrorResponse(Error.SQL_EXCEPTION));
- }
+ String sql = """
+ SELECT c.customer_last_name, c.customer_first_name, c.customer_number,
+ ch.reCheckupDate, ch.remind_date, ch.checkup_id
+ FROM Checkup ch
+ JOIN Customer c ON ch.customer_id = c.customer_id
+ WHERE ch.reCheckupDate IS NOT NULL AND ch.reCheckupDate BETWEEN ? AND ?
+ ORDER BY ch.reCheckupDate ASC
+ """;
+
+ // SỬA LỖI QUAN TRỌNG: Sử dụng try-with-resources để quản lý tài nguyên an toàn
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+
+ stmt.setLong(1, fromDate);
+ stmt.setLong(2, toDate);
+
+ try (ResultSet rs = stmt.executeQuery()) {
+ List results = new ArrayList<>();
+
+ // CẢI TIẾN: Tạo đối tượng formatter một lần ngoài vòng lặp để tăng hiệu năng.
+ // Sử dụng DateTimeFormatter (thread-safe) thay cho SimpleDateFormat.
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy").withZone(zoneId);
+
+ while (rs.next()) {
+ String[] row = new String[5];
+ row[0] = rs.getString("customer_last_name") + " " + rs.getString("customer_first_name");
+ row[1] = rs.getString("customer_number");
+
+ // Câu lệnh SQL đã đảm bảo reCheckupDate không null
+ long recheckTimestamp = rs.getLong("reCheckupDate");
+ row[2] = formatter.format(Instant.ofEpochMilli(recheckTimestamp));
+
+ // Xử lý an toàn cho trường remind_date có thể là null
+ long remindTimestamp = rs.getLong("remind_date");
+ if (rs.wasNull() || remindTimestamp == 0) {
+ row[3] = "Chưa đặt"; // Hoặc có thể để là ""
+ } else {
+ row[3] = formatter.format(Instant.ofEpochMilli(remindTimestamp));
+ }
+
+ row[4] = rs.getString("checkup_id");
+ results.add(row);
+ }
+
+ UserUtil.sendPacket(sessionId, new GetRecheckUpListResponse(results.toArray(new String[0][])));
+ }
+
+ } catch (SQLException e) {
+ log.error("Error fetching recheck-up list", e);
+ // Giữ nguyên logic xử lý lỗi đã tốt của bạn
+ UserUtil.sendPacket(sessionId, new ErrorResponse(Error.SQL_EXCEPTION));
+ }
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
@@ -1899,101 +2066,97 @@ public class ServerHandler extends SimpleChannelInboundHandler 0) {
- log.info("Updated Google Drive info for patient {}: URL={}, FolderID={}", patientId, driveUrl, driveFolderId);
- } else {
- log.warn("No rows updated for patient {} drive info", patientId);
- }
-
+ private void updatePatientDriveInfo(int patientId, String driveUrl, String driveFolderId) throws SQLException {
+ String sql = "UPDATE Customer SET drive_url = ?, drive_folder_id = ? WHERE customer_id = ?";
+
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement updateStmt = conn.prepareStatement(sql)) {
+
+ updateStmt.setString(1, driveUrl);
+ updateStmt.setString(2, driveFolderId);
+ updateStmt.setInt(3, patientId);
+
+ int rowsUpdated = updateStmt.executeUpdate();
+
+ if (rowsUpdated > 0) {
+ log.info("Updated Google Drive info for patient {}: URL={}, FolderID={}", patientId, driveUrl, driveFolderId);
+ } else {
+ log.warn("Could not find patient with ID {} to update Google Drive info.", patientId);
+ }
+
} catch (SQLException e) {
- log.error("Failed to update Google Drive info for patient {}: {}", patientId, e.getMessage());
+ log.error("Failed to update Google Drive info for patient {}: {}", patientId, e.getMessage());
+ throw e;
}
}
/**
- * Creates a Google Drive folder for a checkup asynchronously and updates the checkup drive_url in database.
- * This method runs in a background thread to avoid blocking the checkup creation response.
+ * Tạo thư mục Google Drive cho một lần khám trong một luồng nền (bất đồng bộ).
+ * Phiên bản này sử dụng CompletableFuture cho xử lý bất đồng bộ hiện đại và JDBC an toàn.
+ *
+ * @param checkupId ID của lần khám.
+ * @param customerId ID của bệnh nhân liên quan.
*/
private void createCheckupGoogleDriveFolderAsync(int checkupId, int customerId) {
- // Run in background thread to avoid blocking
- new Thread(() -> {
- try {
- // Check if Google Drive is connected
- if (!Server.isGoogleDriveConnected()) {
- log.info("Google Drive not connected - skipping checkup folder creation for checkup {}", checkupId);
- return;
- }
+ // Sử dụng CompletableFuture để xử lý bất đồng bộ, hiện đại hơn new Thread().
+ CompletableFuture.runAsync(() -> {
+ try {
+ if (!Server.isGoogleDriveConnected()) {
+ log.info("Google Drive not connected - skipping checkup folder creation for checkup {}", checkupId);
+ return;
+ }
- // Get patient information from database
- PreparedStatement patientStmt = Server.connection.prepareStatement(
- "SELECT customer_last_name, customer_first_name, drive_folder_id FROM Customer WHERE customer_id = ?"
- );
- patientStmt.setInt(1, customerId);
- ResultSet patientRs = patientStmt.executeQuery();
-
- String patientLastName = "";
- String patientFirstName = "";
- String patientDriveFolderId = "";
-
- if (patientRs.next()) {
- patientLastName = patientRs.getString("customer_last_name");
- patientFirstName = patientRs.getString("customer_first_name");
- patientDriveFolderId = patientRs.getString("drive_folder_id");
- }
- patientStmt.close();
- patientRs.close();
+ // Lấy thông tin bệnh nhân từ CSDL một cách an toàn
+ String patientLastName = "";
+ String patientFirstName = "";
+ String patientDriveFolderId = null;
- if (patientDriveFolderId == null || patientDriveFolderId.trim().isEmpty()) {
- log.warn("Patient {} has no Google Drive folder ID - cannot create checkup folder", customerId);
- return;
- }
+ String sql = "SELECT customer_last_name, customer_first_name, drive_folder_id FROM Customer WHERE customer_id = ?";
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+
+ stmt.setInt(1, customerId);
+ try (ResultSet rs = stmt.executeQuery()) {
+ if (rs.next()) {
+ patientLastName = rs.getString("customer_last_name");
+ patientFirstName = rs.getString("customer_first_name");
+ patientDriveFolderId = rs.getString("drive_folder_id");
+ }
+ }
+ }
- log.info("Creating Google Drive checkup folder for checkup {} (patient: {} {})", checkupId, patientLastName, patientFirstName);
-
- // Create checkup folder name using Vietnamese format
- String checkupFolderName = TextUtils.createCheckupFolderNameWithId(checkupId, patientLastName, patientFirstName);
-
- // Create checkup folder directly under patient folder using saved folder ID
- String checkupFolderId = createCheckupFolderDirectly(patientDriveFolderId, checkupFolderName);
-
- // Make the checkup folder public and get sharing URL
- String folderUrl = Server.getGoogleDriveService().getFolderSharingUrl(checkupFolderId);
-
- // Update the checkup drive_url and drive_folder_id in database
- updateCheckupDriveInfo(checkupId, folderUrl, checkupFolderId);
-
- log.info("Google Drive checkup folder created successfully for checkup {}: {}", checkupId, folderUrl);
-
- // Notify dashboard
- if (ServerDashboard.getInstance() != null) {
- ServerDashboard.getInstance().addLog(
- String.format("Created Google Drive checkup folder for checkup %d: %s", checkupId, checkupFolderName)
- );
+ if (patientDriveFolderId == null || patientDriveFolderId.trim().isEmpty()) {
+ log.warn("Patient {} has no Google Drive folder ID - cannot create checkup folder", customerId);
+ return;
+ }
+
+ log.info("Creating Google Drive checkup folder for checkup {} (patient: {} {})", checkupId, patientLastName, patientFirstName);
+
+ // Giả sử các hàm này đã được triển khai và hoạt động đúng
+ String checkupFolderName = TextUtils.createCheckupFolderNameWithId(checkupId, patientLastName, patientFirstName);
+ String checkupFolderId = createCheckupFolderDirectly(patientDriveFolderId, checkupFolderName);
+ String folderUrl = Server.getGoogleDriveService().getFolderSharingUrl(checkupFolderId);
+
+ // Gọi hàm cập nhật an toàn
+ updateCheckupDriveInfo(checkupId, folderUrl, checkupFolderId);
+
+ log.info("Google Drive checkup folder created successfully for checkup {}: {}", checkupId, folderUrl);
+
+ if (ServerDashboard.getInstance() != null) {
+ ServerDashboard.getInstance().addLog(
+ String.format("Created Google Drive checkup folder for checkup %d: %s", checkupId, checkupFolderName)
+ );
+ }
+
+ } catch (Exception e) {
+ log.error("Failed to create Google Drive checkup folder for checkup {}: {}", checkupId, e.getMessage(), e);
+ if (ServerDashboard.getInstance() != null) {
+ ServerDashboard.getInstance().addLog(
+ String.format("Failed to create Google Drive checkup folder for checkup %d: %s", checkupId, e.getMessage())
+ );
+ }
}
-
- } catch (Exception e) {
- log.error("Failed to create Google Drive checkup folder for checkup {}: {}", checkupId, e.getMessage());
-
- // Notify dashboard about the error
- if (ServerDashboard.getInstance() != null) {
- ServerDashboard.getInstance().addLog(
- String.format("Failed to create Google Drive checkup folder for checkup %d: %s", checkupId, e.getMessage())
- );
- }
- }
- }).start();
+ });
}
/**
@@ -2004,103 +2167,107 @@ public class ServerHandler extends SimpleChannelInboundHandler 0) {
- log.info("Updated Google Drive info for checkup {}: URL={}, FolderID={}", checkupId, driveUrl, driveFolderId);
- } else {
- log.warn("No rows updated for checkup {} drive info", checkupId);
- }
-
- } catch (SQLException e) {
- log.error("Failed to update Google Drive info for checkup {}: {}", checkupId, e.getMessage());
+ private void updateCheckupDriveInfo(int checkupId, String driveUrl, String driveFolderId) throws SQLException {
+ String sql = "UPDATE Checkup SET drive_url = ?, drive_folder_id = ? WHERE checkup_id = ?";
+
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+
+ stmt.setString(1, driveUrl);
+ stmt.setString(2, driveFolderId);
+ stmt.setInt(3, checkupId);
+
+ int rowsUpdated = stmt.executeUpdate();
+ if (rowsUpdated == 0) {
+ log.warn("Could not find checkup with ID {} to update Google Drive info.", checkupId);
+ }
}
}
/**
- * Uploads a checkup image to Google Drive asynchronously using the checkup's folder ID.
- * This method runs in a background thread to avoid blocking the image upload response.
+ * Tải ảnh của lần khám lên Google Drive một cách bất đồng bộ.
+ *
+ * @param checkupId ID của lần khám (dưới dạng String).
+ * @param fileName Tên của tệp ảnh.
+ * @param imageData Dữ liệu của tệp ảnh.
*/
private void uploadCheckupImageToGoogleDriveAsync(String checkupId, String fileName, byte[] imageData) {
- // Run in background thread to avoid blocking
- new Thread(() -> {
- try {
- // Check if Google Drive is connected
- if (!Server.isGoogleDriveConnected()) {
- log.info("Google Drive not connected - skipping image upload for checkup {}", checkupId);
- return;
- }
-
- // Get checkup's Google Drive folder ID from database
- PreparedStatement checkupStmt = Server.connection.prepareStatement(
- "SELECT drive_folder_id FROM Checkup WHERE checkup_id = ?");
- checkupStmt.setString(1, checkupId);
- ResultSet checkupRs = checkupStmt.executeQuery();
-
- String checkupDriveFolderId = "";
- if (checkupRs.next()) {
- checkupDriveFolderId = checkupRs.getString("drive_folder_id");
- }
- checkupStmt.close();
- checkupRs.close();
-
- if (checkupDriveFolderId == null || checkupDriveFolderId.trim().isEmpty()) {
- log.warn("Checkup {} has no Google Drive folder ID - cannot upload image", checkupId);
- return;
- }
-
- log.info("Uploading image {} to Google Drive for checkup {}", fileName, checkupId);
-
- // Create temporary file from byte array for Google Drive upload
- java.io.File tempFile = createTempFileFromBytes(imageData, fileName);
-
+ // CẢI TIẾN: Sử dụng CompletableFuture để xử lý bất đồng bộ, hiệu quả hơn new Thread().
+ CompletableFuture.runAsync(() -> {
try {
- // Upload file to Google Drive using checkup's folder ID
- String uploadedFileId = Server.getGoogleDriveService().uploadFileToFolder(
- checkupDriveFolderId, tempFile, fileName
- );
-
- log.info("Image uploaded successfully to Google Drive for checkup {}: FileID={}", checkupId, uploadedFileId);
-
- // Notify dashboard
- if (ServerDashboard.getInstance() != null) {
- ServerDashboard.getInstance().addLog(
- String.format("Uploaded image %s to Google Drive for checkup %s", fileName, checkupId)
- );
- }
-
- } finally {
- // Clean up temporary file
- if (tempFile.exists()) {
- tempFile.delete();
- log.debug("Cleaned up temporary file: {}", tempFile.getAbsolutePath());
- }
+ if (!Server.isGoogleDriveConnected()) {
+ log.info("Google Drive not connected - skipping image upload for checkup {}", checkupId);
+ return;
+ }
+
+ // --- SỬA LỖI: Lấy thông tin drive_folder_id từ CSDL một cách an toàn ---
+ String checkupDriveFolderId = null;
+ String sql = "SELECT drive_folder_id FROM Checkup WHERE checkup_id = ?";
+
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+
+ // Giả định checkup_id trong CSDL là kiểu số (integer), an toàn hơn là String.
+ stmt.setInt(1, Integer.parseInt(checkupId));
+
+ try (ResultSet rs = stmt.executeQuery()) {
+ if (rs.next()) {
+ checkupDriveFolderId = rs.getString("drive_folder_id");
+ }
+ }
+ }
+
+ if (checkupDriveFolderId == null || checkupDriveFolderId.trim().isEmpty()) {
+ log.warn("Checkup {} has no Google Drive folder ID - cannot upload image", checkupId);
+ return;
+ }
+
+ log.info("Uploading image {} to Google Drive for checkup {}", fileName, checkupId);
+
+ // Logic tạo file tạm và xóa sau khi dùng của bạn đã tốt, chúng ta sẽ giữ lại.
+ java.io.File tempFile = null;
+ try {
+ tempFile = createTempFileFromBytes(imageData, fileName);
+
+ String uploadedFileId = Server.getGoogleDriveService().uploadFileToFolder(
+ checkupDriveFolderId, tempFile, fileName
+ );
+
+ log.info("Image uploaded successfully to Google Drive for checkup {}: FileID={}", checkupId, uploadedFileId);
+
+ if (ServerDashboard.getInstance() != null) {
+ ServerDashboard.getInstance().addLog(
+ String.format("Uploaded image %s to Google Drive for checkup %s", fileName, checkupId)
+ );
+ }
+ } finally {
+ if (tempFile != null && tempFile.exists()) {
+ if (tempFile.delete()) {
+ log.debug("Cleaned up temporary file: {}", tempFile.getAbsolutePath());
+ } else {
+ log.warn("Could not delete temporary file: {}", tempFile.getAbsolutePath());
+ }
+ }
+ }
+
+ } catch (NumberFormatException e) {
+ log.error("Invalid checkupId format '{}' for Google Drive upload.", checkupId);
+ } catch (Exception e) {
+ log.error("Failed to upload image to Google Drive for checkup {}: {}", checkupId, e.getMessage(), e);
+ if (ServerDashboard.getInstance() != null) {
+ ServerDashboard.getInstance().addLog(
+ String.format("Failed to upload image %s to Google Drive for checkup %s: %s", fileName, checkupId, e.getMessage())
+ );
+ }
}
-
- } catch (Exception e) {
- log.error("Failed to upload image to Google Drive for checkup {}: {}", checkupId, e.getMessage());
-
- // Notify dashboard about the error
- if (ServerDashboard.getInstance() != null) {
- ServerDashboard.getInstance().addLog(
- String.format("Failed to upload image %s to Google Drive for checkup %s: %s", fileName, checkupId, e.getMessage())
- );
- }
- }
- }).start();
+ });
}
/**
@@ -2126,194 +2293,204 @@ public class ServerHandler extends SimpleChannelInboundHandler {
- try {
- // Check if Google Drive is connected
- if (!Server.isGoogleDriveConnected()) {
- log.info("Google Drive not connected - skipping PDF upload for checkup {}", checkupId);
- return;
- }
-
- // Get checkup's Google Drive folder ID from database
- PreparedStatement checkupStmt = Server.connection.prepareStatement(
- "SELECT drive_folder_id FROM Checkup WHERE checkup_id = ?");
- checkupStmt.setString(1, checkupId);
- ResultSet checkupRs = checkupStmt.executeQuery();
-
- String checkupDriveFolderId = "";
- if (checkupRs.next()) {
- checkupDriveFolderId = checkupRs.getString("drive_folder_id");
- }
- checkupStmt.close();
- checkupRs.close();
-
- if (checkupDriveFolderId == null || checkupDriveFolderId.trim().isEmpty()) {
- log.warn("Checkup {} has no Google Drive folder ID - cannot upload PDF", checkupId);
- return;
- }
-
- log.info("Uploading PDF {} ({}) to Google Drive for checkup {}", fileName, pdfType, checkupId);
-
- // Create temporary file from byte array for Google Drive upload
- java.io.File tempFile = createTempFileFromBytes(pdfData, fileName);
-
+ // CẢI TIẾN: Sử dụng CompletableFuture để xử lý bất đồng bộ, hiệu quả hơn new Thread().
+ CompletableFuture.runAsync(() -> {
try {
- // Upload file to Google Drive using checkup's folder ID (with override behavior)
- String uploadedFileId = Server.getGoogleDriveService().uploadFileToFolder(
- checkupDriveFolderId, tempFile, fileName
- );
-
- log.info("PDF uploaded successfully to Google Drive for checkup {}: FileID={}", checkupId, uploadedFileId);
-
- // Notify dashboard
- if (ServerDashboard.getInstance() != null) {
- ServerDashboard.getInstance().addLog(
- String.format("Uploaded PDF %s (%s) to Google Drive for checkup %s", fileName, pdfType, checkupId)
- );
- }
-
- } finally {
- // Clean up temporary file
- if (tempFile.exists()) {
- tempFile.delete();
- log.debug("Cleaned up temporary PDF file: {}", tempFile.getAbsolutePath());
- }
+ if (!Server.isGoogleDriveConnected()) {
+ log.info("Google Drive not connected - skipping PDF upload for checkup {}", checkupId);
+ return;
+ }
+
+ // --- SỬA LỖI: Lấy thông tin drive_folder_id từ CSDL một cách an toàn ---
+ String checkupDriveFolderId = null;
+ String sql = "SELECT drive_folder_id FROM Checkup WHERE checkup_id = ?";
+
+ try (Connection conn = DatabaseManager.getConnection();
+ PreparedStatement stmt = conn.prepareStatement(sql)) {
+
+ // Giả định checkup_id trong CSDL là kiểu số (integer)
+ stmt.setInt(1, Integer.parseInt(checkupId));
+
+ try (ResultSet rs = stmt.executeQuery()) {
+ if (rs.next()) {
+ checkupDriveFolderId = rs.getString("drive_folder_id");
+ }
+ }
+ }
+
+ if (checkupDriveFolderId == null || checkupDriveFolderId.trim().isEmpty()) {
+ log.warn("Checkup {} has no Google Drive folder ID - cannot upload PDF", checkupId);
+ return;
+ }
+
+ log.info("Uploading PDF {} ({}) to Google Drive for checkup {}", fileName, pdfType, checkupId);
+
+ // Logic tạo và dọn dẹp file tạm của bạn đã tốt, chúng ta giữ lại
+ java.io.File tempFile = null;
+ try {
+ tempFile = createTempFileFromBytes(pdfData, fileName);
+
+ String uploadedFileId = Server.getGoogleDriveService().uploadFileToFolder(
+ checkupDriveFolderId, tempFile, fileName
+ );
+
+ log.info("PDF uploaded successfully to Google Drive for checkup {}: FileID={}", checkupId, uploadedFileId);
+
+ if (ServerDashboard.getInstance() != null) {
+ ServerDashboard.getInstance().addLog(
+ String.format("Uploaded PDF %s (%s) to Google Drive for checkup %s", fileName, pdfType, checkupId)
+ );
+ }
+ } finally {
+ if (tempFile != null && tempFile.exists()) {
+ if (!tempFile.delete()) {
+ log.warn("Could not delete temporary PDF file: {}", tempFile.getAbsolutePath());
+ }
+ }
+ }
+
+ } catch (NumberFormatException e) {
+ log.error("Invalid checkupId format '{}' for PDF upload.", checkupId);
+ } catch (Exception e) {
+ log.error("Failed to upload PDF to Google Drive for checkup {}: {}", checkupId, e.getMessage(), e);
+ if (ServerDashboard.getInstance() != null) {
+ ServerDashboard.getInstance().addLog(
+ String.format("Failed to upload PDF %s (%s) for checkup %s: %s", fileName, pdfType, checkupId, e.getMessage())
+ );
+ }
}
-
- } catch (Exception e) {
- log.error("Failed to upload PDF to Google Drive for checkup {}: {}", checkupId, e.getMessage(), e);
-
- // Notify dashboard of error
- if (ServerDashboard.getInstance() != null) {
- ServerDashboard.getInstance().addLog(
- String.format("Failed to upload PDF %s (%s) to Google Drive for checkup %s: %s", fileName, pdfType, checkupId, e.getMessage())
- );
- }
- }
- }).start();
+ });
}
private void broadcastQueueUpdate() {
- try {
- // --- EXISTING LOGIC FOR THE QUEUE (UNCHANGED) ---
- ResultSet rs = statement.executeQuery(
- """
- 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: 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()) {
+
+ // --- LOGIC GỐC CHO HÀNG CHỜ (KHÔNG THAY ĐỔI THUẬT TOÁN) ---
String[][] resultArray;
- if (!rs.isBeforeFirst()) {
- System.out.println("No data found in the checkup table.");
- resultArray = new String[0][0];
- } else {
- // (Your existing code to process the queue results remains here)
- ArrayList 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("\\|");
+ 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 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("\\|");
+ }
}
}
- // --- EXISTING LOGIC FOR TODAY'S PATIENT COUNT ---
+ // --- 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);
long startOfTodayMillis = today.atStartOfDay(zoneId).toInstant().toEpochMilli();
long endOfTodayMillis = today.plusDays(1).atStartOfDay(zoneId).toInstant().toEpochMilli() - 1;
- String countQuery = String.format(
- "SELECT COUNT(DISTINCT a.customer_id) as patient_count FROM checkup as a WHERE a.checkup_date >= %d AND a.checkup_date <= %d",
- startOfTodayMillis, endOfTodayMillis
- );
+
int totalPatientsToday = 0;
- ResultSet countRs = statement.executeQuery(countQuery);
- if (countRs.next()) {
- totalPatientsToday = countRs.getInt("patient_count");
+ 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);
+ try (ResultSet countRs = countStmt.executeQuery()) {
+ if (countRs.next()) {
+ totalPatientsToday = countRs.getInt("patient_count");
+ }
+ }
}
- // --- ADDED LOGIC: QUERY FOR TODAY'S RE-CHECKUP COUNT ---
- String recheckQuery = String.format(
- "SELECT COUNT(*) as recheck_count FROM checkup WHERE reCheckupDate >= %d AND reCheckupDate <= %d",
- startOfTodayMillis, endOfTodayMillis
- );
+ // --- LOGIC GỐC ĐẾM SỐ LƯỢT TÁI KHÁM ---
int totalRecheckToday = 0;
- ResultSet recheckRs = statement.executeQuery(recheckQuery);
- if (recheckRs.next()) {
- totalRecheckToday = recheckRs.getInt("recheck_count");
+ 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);
+ try (ResultSet recheckRs = recheckStmt.executeQuery()) {
+ if (recheckRs.next()) {
+ totalRecheckToday = recheckRs.getInt("recheck_count");
+ }
+ }
}
- // --- SEND ALL RESPONSES TO ALL CLIENTS ---
+ // --- LOGIC GỐC GỬI GÓI TIN ---
int maxCurId = SessionManager.getMaxSessionId();
for (int sessionId = 1; sessionId <= maxCurId; sessionId++) {
- // Send the queue update response
UserUtil.sendPacket(sessionId, new GetCheckUpQueueUpdateResponse(resultArray));
-
- // Send the patient count response
UserUtil.sendPacket(sessionId, new TodayPatientCountResponse(totalPatientsToday));
-
- // Send the new re-checkup count response
UserUtil.sendPacket(sessionId, new RecheckCountResponse(totalRecheckToday));
}
log.info("send checkup queue update response to all clients");
@@ -2321,8 +2498,9 @@ public class ServerHandler extends SimpleChannelInboundHandler