diff --git a/.gitignore b/.gitignore index 6b0636e..ba182bd 100644 --- a/.gitignore +++ b/.gitignore @@ -127,6 +127,8 @@ buildNumber.properties .mvn/timing.properties # https://github.com/takari/maven-wrapper#usage-without-binary-jar .mvn/wrapper/maven-wrapper.jar +# ignore all json +*.json # User-generated content img_db/ @@ -149,3 +151,11 @@ src/main/resources/image/checkup_media/34/VID_34_20250604_120241.mp4 src/main/resources/image/checkup_media/8/IMG_8_20250604_112523.png src/main/resources/image/checkup_media/8/IMG_8_20250604_112526.png src/main/resources/image/checkup_media/8/VID_8_20250604_115008.mp4 +src/main/resources/client_secret_1063629801433-01ubsp30l0sabhbk28q2esh9c3bhjgka.apps.googleusercontent.com.json +GOOGLE_DRIVE_SETUP.md +src/main/resources/google-drive-service-account.json + +tokens/StoredCredential +src/main/resources/database/BSK.db +src/main/resources/database/BSK.db +src/main/resources/database/BSK.db diff --git a/.vscode/launch.json b/.vscode/launch.json index 9eb8ca9..56bde02 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,6 +1,20 @@ { "version": "0.2.0", "configurations": [ + { + "type": "java", + "name": "GoogleDriveOAuthTest", + "request": "launch", + "mainClass": "BsK.server.test.GoogleDriveOAuthTest", + "projectName": "ldS_BsK" + }, + { + "type": "java", + "name": "GoogleDriveTest", + "request": "launch", + "mainClass": "BsK.server.test.GoogleDriveTest", + "projectName": "ldS_BsK" + }, { "type": "java", "name": "DateUtils", diff --git a/medical_invoice.pdf b/medical_invoice.pdf index acaf2f4..d65b208 100644 Binary files a/medical_invoice.pdf and b/medical_invoice.pdf differ diff --git a/pom.xml b/pom.xml index 7ed7cdd..bedad98 100644 --- a/pom.xml +++ b/pom.xml @@ -273,6 +273,39 @@ jasperreports-pdf 7.0.3 + + + + com.google.apis + google-api-services-drive + v3-rev20240914-2.0.0 + + + + + com.google.auth + google-auth-library-oauth2-http + 1.27.0 + + + + + com.google.oauth-client + google-oauth-client-jetty + 1.36.0 + + + + + com.google.zxing + core + 3.5.3 + + + com.google.zxing + javase + 3.5.3 + 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 25d9b35..4150b1c 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java @@ -1905,7 +1905,11 @@ public class CheckUpPage extends JPanel { suggestionField.setText(selectedPatient.getSuggestion()); // De nghi diagnosisField.setText(selectedPatient.getDiagnosis()); // Chuan doan conclusionField.setText(selectedPatient.getConclusion()); // Ket luan - recheckupDatePicker.getJFormattedTextField().setText(DateUtils.convertToDisplayFormat(selectedPatient.getReCheckupDate())); // Ngày khám lại + if (selectedPatient.getReCheckupDate() != null) { + recheckupDatePicker.getJFormattedTextField().setText(DateUtils.convertToDisplayFormat(selectedPatient.getReCheckupDate())); + } else { + recheckupDatePicker.getJFormattedTextField().setText(""); + } // Handle RTF notes content String notes = selectedPatient.getNotes(); diff --git a/src/main/java/BsK/client/ui/component/QueueViewPage/QueueViewPage.java b/src/main/java/BsK/client/ui/component/QueueViewPage/QueueViewPage.java index ae6ffd7..ec0b180 100644 --- a/src/main/java/BsK/client/ui/component/QueueViewPage/QueueViewPage.java +++ b/src/main/java/BsK/client/ui/component/QueueViewPage/QueueViewPage.java @@ -1,6 +1,7 @@ package BsK.client.ui.component.QueueViewPage; import BsK.client.LocalStorage; +import BsK.common.util.date.DateUtils; import javax.swing.*; import javax.swing.border.EmptyBorder; @@ -288,26 +289,32 @@ public class QueueViewPage extends JFrame { SimpleDateFormat inputDateFormat = new SimpleDateFormat("dd/MM/yyyy"); for (String[] row : fullQueueData) { - if (row.length > 3) { + if (row.length > 15) { // Changed from > 3 to > 15 to ensure we have DOB data String maKhamBenh = row[0]; String ho = row[2]; String ten = row[3]; String hoVaTen = ho + " " + ten; - String dateStr = row[1]; + String customerDob = row[15]; // Use customer DOB instead of checkup date String namSinh = "N/A"; - try { - Date parsedDate; - if (dateStr.matches("\\d+")) { - parsedDate = new Date(Long.parseLong(dateStr)); - } else { - parsedDate = inputDateFormat.parse(dateStr); + if (customerDob != null && !customerDob.isEmpty()) { + // Extract year from DOB using DateUtils for consistency + int year = DateUtils.extractYearFromTimestamp(customerDob); + if (year != -1) { + namSinh = String.valueOf(year); + } else { + // Fallback: try to parse as date string and extract year + try { + Date dobDate = DateUtils.convertToDate(customerDob); + if (dobDate != null) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(dobDate); + namSinh = String.valueOf(calendar.get(Calendar.YEAR)); + } + } catch (Exception e) { + System.err.println("Lỗi phân tích ngày sinh cho năm trên màn hình chờ TV: " + customerDob + " - " + e.getMessage()); + } } - Calendar calendar = Calendar.getInstance(); - calendar.setTime(parsedDate); - namSinh = String.valueOf(calendar.get(Calendar.YEAR)); - } catch (ParseException | NumberFormatException e) { - System.err.println("Lỗi phân tích ngày cho năm trên màn hình chờ TV: " + dateStr + " - " + e.getMessage()); } tvDataList.add(new Object[]{maKhamBenh, hoVaTen, namSinh}); } diff --git a/src/main/java/BsK/server/service/GoogleDriveService.java b/src/main/java/BsK/server/service/GoogleDriveService.java new file mode 100644 index 0000000..ae7aeb2 --- /dev/null +++ b/src/main/java/BsK/server/service/GoogleDriveService.java @@ -0,0 +1,243 @@ +package BsK.server.service; + +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.http.FileContent; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.gson.GsonFactory; +import com.google.api.services.drive.Drive; +import com.google.api.services.drive.DriveScopes; +import com.google.api.services.drive.model.File; +import com.google.api.services.drive.model.FileList; +import com.google.auth.http.HttpCredentialsAdapter; +import com.google.auth.oauth2.GoogleCredentials; +import com.google.auth.oauth2.ServiceAccountCredentials; +import lombok.extern.slf4j.Slf4j; + +import java.io.FileInputStream; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +@Slf4j +public class GoogleDriveService { + private static final String APPLICATION_NAME = "BSK Clinic Management System"; + private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); + private static final List SCOPES = Collections.singletonList(DriveScopes.DRIVE_FILE); + + // Path to service account key file (will be configured) + private static final String SERVICE_ACCOUNT_KEY_PATH = "src/main/resources/google-drive-service-account.json"; + + private Drive driveService; + private String rootFolderId; // BSK Clinic root folder + + public GoogleDriveService() { + try { + this.driveService = buildDriveService(); + this.rootFolderId = getOrCreateRootFolder(); + log.info("Google Drive service initialized successfully"); + } catch (Exception e) { + log.error("Failed to initialize Google Drive service", e); + throw new RuntimeException("Google Drive service initialization failed", e); + } + } + + private Drive buildDriveService() throws GeneralSecurityException, IOException { + GoogleCredentials credentials = ServiceAccountCredentials + .fromStream(new FileInputStream(SERVICE_ACCOUNT_KEY_PATH)) + .createScoped(SCOPES); + + return new Drive.Builder( + GoogleNetHttpTransport.newTrustedTransport(), + JSON_FACTORY, + new HttpCredentialsAdapter(credentials)) + .setApplicationName(APPLICATION_NAME) + .build(); + } + + /** + * Get or create the root folder for BSK Clinic + */ + private String getOrCreateRootFolder() throws IOException { + String folderName = "BSK_Clinic_Patient_Files"; + + // Search for existing root folder + String query = "name='" + folderName + "' and mimeType='application/vnd.google-apps.folder' and trashed=false"; + FileList result = driveService.files().list() + .setQ(query) + .setSpaces("drive") + .execute(); + + List files = result.getFiles(); + if (files != null && !files.isEmpty()) { + String folderId = files.get(0).getId(); + log.info("Found existing root folder: {} (ID: {})", folderName, folderId); + return folderId; + } + + // Create root folder if it doesn't exist + File folderMetadata = new File(); + folderMetadata.setName(folderName); + folderMetadata.setMimeType("application/vnd.google-apps.folder"); + + File folder = driveService.files().create(folderMetadata) + .setFields("id") + .execute(); + + log.info("Created new root folder: {} (ID: {})", folderName, folder.getId()); + return folder.getId(); + } + + /** + * Create a folder for a specific patient + */ + public String createPatientFolder(String patientId, String patientName) throws IOException { + String folderName = "Patient_" + patientId + "_" + sanitizeFileName(patientName); + + // Check if folder already exists + Optional existingFolderId = findPatientFolder(patientId); + if (existingFolderId.isPresent()) { + log.info("Patient folder already exists: {} (ID: {})", folderName, existingFolderId.get()); + return existingFolderId.get(); + } + + // Create new patient folder + File folderMetadata = new File(); + folderMetadata.setName(folderName); + folderMetadata.setMimeType("application/vnd.google-apps.folder"); + folderMetadata.setParents(Collections.singletonList(rootFolderId)); + + File folder = driveService.files().create(folderMetadata) + .setFields("id") + .execute(); + + log.info("Created patient folder: {} (ID: {})", folderName, folder.getId()); + return folder.getId(); + } + + /** + * Find existing patient folder by patient ID + */ + private Optional findPatientFolder(String patientId) throws IOException { + String query = "name contains 'Patient_" + patientId + "_' and mimeType='application/vnd.google-apps.folder' and trashed=false and '" + rootFolderId + "' in parents"; + + FileList result = driveService.files().list() + .setQ(query) + .setSpaces("drive") + .execute(); + + List files = result.getFiles(); + if (files != null && !files.isEmpty()) { + return Optional.of(files.get(0).getId()); + } + + return Optional.empty(); + } + + /** + * Upload a file to a patient's folder + */ + public String uploadFile(String patientFolderId, java.io.File localFile, String fileName) throws IOException { + String mimeType = determineMimeType(fileName); + + File fileMetadata = new File(); + fileMetadata.setName(fileName); + fileMetadata.setParents(Collections.singletonList(patientFolderId)); + + FileContent mediaContent = new FileContent(mimeType, localFile); + + File uploadedFile = driveService.files().create(fileMetadata, mediaContent) + .setFields("id,webViewLink,webContentLink") + .execute(); + + log.info("Uploaded file: {} (ID: {})", fileName, uploadedFile.getId()); + return uploadedFile.getId(); + } + + /** + * Get the public sharing URL for a folder + */ + public String getFolderSharingUrl(String folderId) throws IOException { + // Make the folder publicly viewable + com.google.api.services.drive.model.Permission permission = new com.google.api.services.drive.model.Permission(); + permission.setType("anyone"); + permission.setRole("reader"); + + driveService.permissions().create(folderId, permission).execute(); + + // Get the folder details with web view link + File folder = driveService.files().get(folderId) + .setFields("webViewLink") + .execute(); + + return folder.getWebViewLink(); + } + + /** + * Create a test folder and upload sample files + */ + public void runConnectionTest() throws IOException { + log.info("Starting Google Drive connection test..."); + + // Create test folder + String testFolderId = createPatientFolder("TEST001", "Test Patient"); + + // Create a test text file + java.io.File testFile = new java.io.File("test-connection.txt"); + try { + java.io.FileWriter writer = new java.io.FileWriter(testFile); + writer.write("BSK Clinic - Google Drive Connection Test\n"); + writer.write("Timestamp: " + java.time.LocalDateTime.now() + "\n"); + writer.write("This file verifies that the Google Drive integration is working correctly.\n"); + writer.close(); + + // Upload the test file + String fileId = uploadFile(testFolderId, testFile, "connection-test.txt"); + + // Get sharing URL + String folderUrl = getFolderSharingUrl(testFolderId); + + log.info("✅ Google Drive connection test successful!"); + log.info("📁 Test folder URL: {}", folderUrl); + log.info("📄 Test file ID: {}", fileId); + + // Clean up test file + testFile.delete(); + + } catch (Exception e) { + log.error("❌ Google Drive connection test failed", e); + if (testFile.exists()) { + testFile.delete(); + } + throw e; + } + } + + private String sanitizeFileName(String fileName) { + // Remove or replace invalid characters for file names + return fileName.replaceAll("[^a-zA-Z0-9._-]", "_"); + } + + private String determineMimeType(String fileName) { + String lowerFileName = fileName.toLowerCase(); + if (lowerFileName.endsWith(".pdf")) { + return "application/pdf"; + } else if (lowerFileName.endsWith(".jpg") || lowerFileName.endsWith(".jpeg")) { + return "image/jpeg"; + } else if (lowerFileName.endsWith(".png")) { + return "image/png"; + } else if (lowerFileName.endsWith(".txt")) { + return "text/plain"; + } else if (lowerFileName.endsWith(".doc")) { + return "application/msword"; + } else if (lowerFileName.endsWith(".docx")) { + return "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; + } + return "application/octet-stream"; // Default binary + } + + public String getRootFolderId() { + return rootFolderId; + } +} \ No newline at end of file diff --git a/src/main/java/BsK/server/service/GoogleDriveServiceOAuth.java b/src/main/java/BsK/server/service/GoogleDriveServiceOAuth.java new file mode 100644 index 0000000..066d309 --- /dev/null +++ b/src/main/java/BsK/server/service/GoogleDriveServiceOAuth.java @@ -0,0 +1,266 @@ +package BsK.server.service; + +import com.google.api.client.auth.oauth2.Credential; +import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp; +import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver; +import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; +import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets; +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.http.FileContent; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.gson.GsonFactory; +import com.google.api.client.util.store.FileDataStoreFactory; +import com.google.api.services.drive.Drive; +import com.google.api.services.drive.DriveScopes; +import com.google.api.services.drive.model.File; +import com.google.api.services.drive.model.FileList; +import lombok.extern.slf4j.Slf4j; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.security.GeneralSecurityException; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +@Slf4j +public class GoogleDriveServiceOAuth { + private static final String APPLICATION_NAME = "BSK Clinic Management System"; + private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); + private static final List SCOPES = Collections.singletonList(DriveScopes.DRIVE_FILE); + + // Path to OAuth2 client credentials file + private static final String OAUTH_CREDENTIALS_PATH = "src/main/resources/google-oauth-credentials.json"; + // Directory to store authorization tokens + private static final String TOKENS_DIRECTORY_PATH = "tokens"; + + private Drive driveService; + private String rootFolderId; // BSK Clinic root folder + + public GoogleDriveServiceOAuth() { + try { + this.driveService = buildDriveService(); + this.rootFolderId = getOrCreateRootFolder(); + log.info("Google Drive OAuth service initialized successfully"); + } catch (Exception e) { + log.error("Failed to initialize Google Drive OAuth service", e); + throw new RuntimeException("Google Drive OAuth service initialization failed", e); + } + } + + private Drive buildDriveService() throws GeneralSecurityException, IOException { + // Load client secrets + GoogleClientSecrets clientSecrets = GoogleClientSecrets.load( + JSON_FACTORY, + new InputStreamReader(new FileInputStream(OAUTH_CREDENTIALS_PATH)) + ); + + // Build flow and trigger user authorization request + GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder( + GoogleNetHttpTransport.newTrustedTransport(), + JSON_FACTORY, + clientSecrets, + SCOPES) + .setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH))) + .setAccessType("offline") + .build(); + + LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build(); + Credential credential = new AuthorizationCodeInstalledApp(flow, receiver).authorize("user"); + + // Build a new authorized API client service + return new Drive.Builder( + GoogleNetHttpTransport.newTrustedTransport(), + JSON_FACTORY, + credential) + .setApplicationName(APPLICATION_NAME) + .build(); + } + + /** + * Get or create the root folder for BSK Clinic + */ + private String getOrCreateRootFolder() throws IOException { + String folderName = "BSK_Clinic_Patient_Files"; + + // Search for existing root folder + String query = "name='" + folderName + "' and mimeType='application/vnd.google-apps.folder' and trashed=false"; + FileList result = driveService.files().list() + .setQ(query) + .setSpaces("drive") + .execute(); + + List files = result.getFiles(); + if (files != null && !files.isEmpty()) { + String folderId = files.get(0).getId(); + log.info("Found existing root folder: {} (ID: {})", folderName, folderId); + return folderId; + } + + // Create root folder if it doesn't exist + File folderMetadata = new File(); + folderMetadata.setName(folderName); + folderMetadata.setMimeType("application/vnd.google-apps.folder"); + + File folder = driveService.files().create(folderMetadata) + .setFields("id") + .execute(); + + log.info("Created new root folder: {} (ID: {})", folderName, folder.getId()); + return folder.getId(); + } + + /** + * Create a folder for a specific patient + */ + public String createPatientFolder(String patientId, String patientName) throws IOException { + String folderName = "Patient_" + patientId + "_" + sanitizeFileName(patientName); + + // Check if folder already exists + Optional existingFolderId = findPatientFolder(patientId); + if (existingFolderId.isPresent()) { + log.info("Patient folder already exists: {} (ID: {})", folderName, existingFolderId.get()); + return existingFolderId.get(); + } + + // Create new patient folder + File folderMetadata = new File(); + folderMetadata.setName(folderName); + folderMetadata.setMimeType("application/vnd.google-apps.folder"); + folderMetadata.setParents(Collections.singletonList(rootFolderId)); + + File folder = driveService.files().create(folderMetadata) + .setFields("id") + .execute(); + + log.info("Created patient folder: {} (ID: {})", folderName, folder.getId()); + return folder.getId(); + } + + /** + * Find existing patient folder by patient ID + */ + private Optional findPatientFolder(String patientId) throws IOException { + String query = "name contains 'Patient_" + patientId + "_' and mimeType='application/vnd.google-apps.folder' and trashed=false and '" + rootFolderId + "' in parents"; + + FileList result = driveService.files().list() + .setQ(query) + .setSpaces("drive") + .execute(); + + List files = result.getFiles(); + if (files != null && !files.isEmpty()) { + return Optional.of(files.get(0).getId()); + } + + return Optional.empty(); + } + + /** + * Upload a file to a patient's folder + */ + public String uploadFile(String patientFolderId, java.io.File localFile, String fileName) throws IOException { + String mimeType = determineMimeType(fileName); + + File fileMetadata = new File(); + fileMetadata.setName(fileName); + fileMetadata.setParents(Collections.singletonList(patientFolderId)); + + FileContent mediaContent = new FileContent(mimeType, localFile); + + File uploadedFile = driveService.files().create(fileMetadata, mediaContent) + .setFields("id,webViewLink,webContentLink") + .execute(); + + log.info("Uploaded file: {} (ID: {})", fileName, uploadedFile.getId()); + return uploadedFile.getId(); + } + + /** + * Get the public sharing URL for a folder + */ + public String getFolderSharingUrl(String folderId) throws IOException { + // Make the folder publicly viewable + com.google.api.services.drive.model.Permission permission = new com.google.api.services.drive.model.Permission(); + permission.setType("anyone"); + permission.setRole("reader"); + + driveService.permissions().create(folderId, permission).execute(); + + // Get the folder details with web view link + File folder = driveService.files().get(folderId) + .setFields("webViewLink") + .execute(); + + return folder.getWebViewLink(); + } + + /** + * Create a test folder and upload sample files + */ + public void runConnectionTest() throws IOException { + log.info("Starting Google Drive OAuth connection test..."); + + // Create test folder + String testFolderId = createPatientFolder("TEST002", "OAuth Test Patient"); + + // Create a test text file + java.io.File testFile = new java.io.File("test-oauth-connection.txt"); + try { + java.io.FileWriter writer = new java.io.FileWriter(testFile); + writer.write("BSK Clinic - Google Drive OAuth Connection Test\n"); + writer.write("Timestamp: " + java.time.LocalDateTime.now() + "\n"); + writer.write("This file verifies that the Google Drive OAuth integration is working correctly.\n"); + writer.write("Authentication method: OAuth2 with user consent\n"); + writer.close(); + + // Upload the test file + String fileId = uploadFile(testFolderId, testFile, "oauth-connection-test.txt"); + + // Get sharing URL + String folderUrl = getFolderSharingUrl(testFolderId); + + log.info("✅ Google Drive OAuth connection test successful!"); + log.info("📁 Test folder URL: {}", folderUrl); + log.info("📄 Test file ID: {}", fileId); + + // Clean up test file + testFile.delete(); + + } catch (Exception e) { + log.error("❌ Google Drive OAuth connection test failed", e); + if (testFile.exists()) { + testFile.delete(); + } + throw e; + } + } + + private String sanitizeFileName(String fileName) { + // Remove or replace invalid characters for file names + return fileName.replaceAll("[^a-zA-Z0-9._-]", "_"); + } + + private String determineMimeType(String fileName) { + String lowerFileName = fileName.toLowerCase(); + if (lowerFileName.endsWith(".pdf")) { + return "application/pdf"; + } else if (lowerFileName.endsWith(".jpg") || lowerFileName.endsWith(".jpeg")) { + return "image/jpeg"; + } else if (lowerFileName.endsWith(".png")) { + return "image/png"; + } else if (lowerFileName.endsWith(".txt")) { + return "text/plain"; + } else if (lowerFileName.endsWith(".doc")) { + return "application/msword"; + } else if (lowerFileName.endsWith(".docx")) { + return "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; + } + return "application/octet-stream"; // Default binary + } + + public String getRootFolderId() { + return rootFolderId; + } +} \ No newline at end of file diff --git a/src/main/java/BsK/server/test/GoogleDriveOAuthTest.java b/src/main/java/BsK/server/test/GoogleDriveOAuthTest.java new file mode 100644 index 0000000..e693b8a --- /dev/null +++ b/src/main/java/BsK/server/test/GoogleDriveOAuthTest.java @@ -0,0 +1,125 @@ +package BsK.server.test; + +import BsK.server.service.GoogleDriveServiceOAuth; +import BsK.server.util.QRCodeGenerator; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +@Slf4j +public class GoogleDriveOAuthTest { + + public static void main(String[] args) { + log.info("🚀 Starting BSK Google Drive OAuth Integration Test"); + + try { + // Initialize Google Drive OAuth service + log.info("🔐 This will open a browser window for Google authentication..."); + GoogleDriveServiceOAuth driveService = new GoogleDriveServiceOAuth(); + + // Run basic connection test + driveService.runConnectionTest(); + + // Test patient folder creation and file upload + testPatientFileOperations(driveService); + + // Test QR code generation + testQRCodeGeneration(); + + log.info("🎉 All Google Drive OAuth tests completed successfully!"); + + } catch (Exception e) { + log.error("❌ Google Drive OAuth test failed", e); + System.exit(1); + } + } + + private static void testPatientFileOperations(GoogleDriveServiceOAuth driveService) throws IOException { + log.info("🧪 Testing patient file operations..."); + + // Test patient folder creation + String patientId = "P002"; + String patientName = "Trần Thị B"; + String patientFolderId = driveService.createPatientFolder(patientId, patientName); + + // Create sample PDF file (mock) + File testPdf = createSamplePDF("medical_report_" + patientId + ".pdf"); + + // Create sample image file (mock) + File testImage = createSampleTextFile("xray_" + patientId + ".txt", "Sample X-ray data for patient " + patientId); + + try { + // Upload files + String pdfFileId = driveService.uploadFile(patientFolderId, testPdf, testPdf.getName()); + String imageFileId = driveService.uploadFile(patientFolderId, testImage, "xray_" + patientId + ".jpg"); + + // Get folder sharing URL + String folderUrl = driveService.getFolderSharingUrl(patientFolderId); + + log.info("✅ Patient folder created successfully"); + log.info("📁 Patient folder URL: {}", folderUrl); + log.info("📄 PDF file ID: {}", pdfFileId); + log.info("🖼️ Image file ID: {}", imageFileId); + + // Test QR code generation for this folder + testQRCodeForFolder(folderUrl, patientId); + + } finally { + // Clean up test files + if (testPdf.exists()) testPdf.delete(); + if (testImage.exists()) testImage.delete(); + } + } + + private static void testQRCodeGeneration() { + log.info("🧪 Testing QR code generation..."); + QRCodeGenerator.testQRGeneration(); + } + + private static void testQRCodeForFolder(String folderUrl, String patientId) { + try { + log.info("🧪 Testing QR code generation for patient folder..."); + String qrFileName = "patient_" + patientId + "_qr.png"; + File qrFile = QRCodeGenerator.generateQRCode(folderUrl, qrFileName); + + log.info("✅ QR code generated for patient folder: {}", qrFile.getAbsolutePath()); + log.info("📱 QR code contains URL: {}", folderUrl); + + // Clean up + if (qrFile.exists()) { + qrFile.delete(); + log.info("🧹 QR code file cleaned up"); + } + + } catch (Exception e) { + log.error("❌ QR code generation failed for patient folder", e); + } + } + + private static File createSamplePDF(String fileName) throws IOException { + File file = new File(fileName); + FileWriter writer = new FileWriter(file); + writer.write("BSK Clinic - Sample Medical Report (OAuth Test)\n"); + writer.write("==========================================\n\n"); + writer.write("Patient ID: P002\n"); + writer.write("Patient Name: Trần Thị B\n"); + writer.write("Date: " + java.time.LocalDate.now() + "\n\n"); + writer.write("This is a sample medical report for testing Google Drive OAuth integration.\n"); + writer.write("In production, this would be a properly formatted PDF report.\n"); + writer.write("Authentication method: OAuth2 with user consent\n"); + writer.close(); + return file; + } + + private static File createSampleTextFile(String fileName, String content) throws IOException { + File file = new File(fileName); + FileWriter writer = new FileWriter(file); + writer.write(content); + writer.write("\nCreated: " + java.time.LocalDateTime.now()); + writer.write("\nAuthentication method: OAuth2 with user consent"); + writer.close(); + return file; + } +} \ No newline at end of file diff --git a/src/main/java/BsK/server/test/GoogleDriveTest.java b/src/main/java/BsK/server/test/GoogleDriveTest.java new file mode 100644 index 0000000..664c92d --- /dev/null +++ b/src/main/java/BsK/server/test/GoogleDriveTest.java @@ -0,0 +1,122 @@ +package BsK.server.test; + +import BsK.server.service.GoogleDriveService; +import BsK.server.util.QRCodeGenerator; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +@Slf4j +public class GoogleDriveTest { + + public static void main(String[] args) { + log.info("🚀 Starting BSK Google Drive Integration Test"); + + try { + // Initialize Google Drive service + GoogleDriveService driveService = new GoogleDriveService(); + + // Run basic connection test + driveService.runConnectionTest(); + + // Test patient folder creation and file upload + testPatientFileOperations(driveService); + + // Test QR code generation + testQRCodeGeneration(); + + log.info("🎉 All Google Drive tests completed successfully!"); + + } catch (Exception e) { + log.error("❌ Google Drive test failed", e); + System.exit(1); + } + } + + private static void testPatientFileOperations(GoogleDriveService driveService) throws IOException { + log.info("🧪 Testing patient file operations..."); + + // Test patient folder creation + String patientId = "P001"; + String patientName = "Nguyễn Văn A"; + String patientFolderId = driveService.createPatientFolder(patientId, patientName); + + // Create sample PDF file (mock) + File testPdf = createSamplePDF("sample_report_" + patientId + ".pdf"); + + // Create sample image file (mock) + File testImage = createSampleTextFile("ultrasound_" + patientId + ".txt", "Sample ultrasound data for patient " + patientId); + + try { + // Upload files + String pdfFileId = driveService.uploadFile(patientFolderId, testPdf, testPdf.getName()); + String imageFileId = driveService.uploadFile(patientFolderId, testImage, "ultrasound_" + patientId + ".jpg"); + + // Get folder sharing URL + String folderUrl = driveService.getFolderSharingUrl(patientFolderId); + + log.info("✅ Patient folder created successfully"); + log.info("📁 Patient folder URL: {}", folderUrl); + log.info("📄 PDF file ID: {}", pdfFileId); + log.info("🖼️ Image file ID: {}", imageFileId); + + // Test QR code generation for this folder + testQRCodeForFolder(folderUrl, patientId); + + } finally { + // Clean up test files + if (testPdf.exists()) testPdf.delete(); + if (testImage.exists()) testImage.delete(); + } + } + + private static void testQRCodeGeneration() { + log.info("🧪 Testing QR code generation..."); + QRCodeGenerator.testQRGeneration(); + } + + private static void testQRCodeForFolder(String folderUrl, String patientId) { + try { + log.info("🧪 Testing QR code generation for patient folder..."); + String qrFileName = "patient_" + patientId + "_qr.png"; + File qrFile = QRCodeGenerator.generateQRCode(folderUrl, qrFileName); + + log.info("✅ QR code generated for patient folder: {}", qrFile.getAbsolutePath()); + log.info("📱 QR code contains URL: {}", folderUrl); + + // Clean up + if (qrFile.exists()) { + qrFile.delete(); + log.info("🧹 QR code file cleaned up"); + } + + } catch (Exception e) { + log.error("❌ QR code generation failed for patient folder", e); + } + } + + private static File createSamplePDF(String fileName) throws IOException { + File file = new File(fileName); + FileWriter writer = new FileWriter(file); + writer.write("BSK Clinic - Sample Medical Report\n"); + writer.write("=================================\n\n"); + writer.write("Patient ID: P001\n"); + writer.write("Patient Name: Nguyễn Văn A\n"); + writer.write("Date: " + java.time.LocalDate.now() + "\n\n"); + writer.write("This is a sample medical report for testing Google Drive integration.\n"); + writer.write("In production, this would be a properly formatted PDF report.\n"); + writer.close(); + return file; + } + + private static File createSampleTextFile(String fileName, String content) throws IOException { + File file = new File(fileName); + FileWriter writer = new FileWriter(file); + writer.write(content); + writer.write("\nCreated: " + java.time.LocalDateTime.now()); + writer.close(); + return file; + } +} \ No newline at end of file diff --git a/src/main/java/BsK/server/util/QRCodeGenerator.java b/src/main/java/BsK/server/util/QRCodeGenerator.java new file mode 100644 index 0000000..d36f355 --- /dev/null +++ b/src/main/java/BsK/server/util/QRCodeGenerator.java @@ -0,0 +1,111 @@ +package BsK.server.util; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.QRCodeWriter; +import lombok.extern.slf4j.Slf4j; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +@Slf4j +public class QRCodeGenerator { + + private static final int DEFAULT_WIDTH = 200; + private static final int DEFAULT_HEIGHT = 200; + private static final int BLACK = 0xFF000000; + private static final int WHITE = 0xFFFFFFFF; + + /** + * Generate QR code for a given URL and save as image file + */ + public static File generateQRCode(String url, String fileName) throws WriterException, IOException { + return generateQRCode(url, fileName, DEFAULT_WIDTH, DEFAULT_HEIGHT); + } + + /** + * Generate QR code with custom dimensions + */ + public static File generateQRCode(String url, String fileName, int width, int height) throws WriterException, IOException { + QRCodeWriter qrCodeWriter = new QRCodeWriter(); + BitMatrix bitMatrix = qrCodeWriter.encode(url, BarcodeFormat.QR_CODE, width, height); + + BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + bufferedImage.setRGB(x, y, bitMatrix.get(x, y) ? BLACK : WHITE); + } + } + + File qrCodeFile = new File(fileName); + ImageIO.write(bufferedImage, "PNG", qrCodeFile); + + log.info("QR code generated: {} for URL: {}", fileName, url); + return qrCodeFile; + } + + /** + * Generate QR code as BufferedImage (for embedding in reports) + */ + public static BufferedImage generateQRCodeImage(String url, int width, int height) throws WriterException { + QRCodeWriter qrCodeWriter = new QRCodeWriter(); + BitMatrix bitMatrix = qrCodeWriter.encode(url, BarcodeFormat.QR_CODE, width, height); + + BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + bufferedImage.setRGB(x, y, bitMatrix.get(x, y) ? BLACK : WHITE); + } + } + + return bufferedImage; + } + + /** + * Generate QR code with custom colors + */ + public static BufferedImage generateQRCodeImage(String url, int width, int height, Color foregroundColor, Color backgroundColor) throws WriterException { + QRCodeWriter qrCodeWriter = new QRCodeWriter(); + BitMatrix bitMatrix = qrCodeWriter.encode(url, BarcodeFormat.QR_CODE, width, height); + + BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + int foregroundRGB = foregroundColor.getRGB(); + int backgroundRGB = backgroundColor.getRGB(); + + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + bufferedImage.setRGB(x, y, bitMatrix.get(x, y) ? foregroundRGB : backgroundRGB); + } + } + + return bufferedImage; + } + + /** + * Test method to generate a sample QR code + */ + public static void testQRGeneration() { + try { + String testUrl = "https://drive.google.com/drive/folders/test123"; + String fileName = "test-qr-code.png"; + + File qrFile = generateQRCode(testUrl, fileName); + log.info("✅ Test QR code generated successfully: {}", qrFile.getAbsolutePath()); + + // Clean up test file + if (qrFile.exists()) { + qrFile.delete(); + log.info("🧹 Test QR code file cleaned up"); + } + + } catch (Exception e) { + log.error("❌ QR code generation test failed", e); + } + } +} \ No newline at end of file diff --git a/src/main/resources/database/BSK.db b/src/main/resources/database/BSK.db index ed25376..2619708 100644 Binary files a/src/main/resources/database/BSK.db and b/src/main/resources/database/BSK.db differ